Skip to content

Commit d3e9dc5

Browse files
authored
Add tests and refactor into module (#84)
* Refactor to introduce __meta__ file * Add tests and refactor code into a module
1 parent 8b3c39e commit d3e9dc5

17 files changed

Lines changed: 3773 additions & 147 deletions

.gitignore

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
# Config file
22
config/config.ini
33

4-
# Sensitive credentials
5-
src/credentials.json
6-
src/token.json
7-
84
# Logs
95
logs/
106

117
# Output files
12-
*.ics
8+
out/
139

1410
# Github.com gitignore/Python.gitignore
1511
# Byte-compiled / optimized / DLL files

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ After gathering a list of birthdays for all the users friends for a full year, i
3232
1. Clone repo
3333
`git clone git@github.com:mobeigi/fb2cal.git`
3434
2. Rename `config/config-template.ini` to `config/config.ini` and enter your Facebook email and password (no quotes).
35-
3. Install required python modules
35+
3. Set up pipenv environment
3636
`pipenv install`
37-
4. Run the script manually:
38-
`pipenv run python src/fb2cal.py`
39-
5. Import the created `birthdays.ics` file into Calendar applications (i.e. Google Calendar)
37+
4. Run the `fb2cal` module
38+
`pipenv run python -m fb2cal`
39+
5. Check the output folder (`out` by default) for the created `birthdays.ics` file
4040

4141
## Configuration
4242
This tool can be configured by editing the `config/config.ini` configuration file.

config/config-template.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ fb_pass =
55

66
[FILESYSTEM]
77
save_to_file = True
8-
ics_file_path = ./birthdays.ics
8+
ics_file_path = ./out/birthdays.ics
99

1010
[LOGGING]
1111
level = INFO
Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,21 @@
1-
"""
2-
fb2cal - Facebook Birthday Events to ICS file converter
3-
Created by: mobeigi
4-
5-
This program is free software: you can redistribute it and/or modify it under
6-
the terms of the GNU General Public License as published by the Free Software
7-
Foundation, either version 3 of the License, or (at your option) any later
8-
version.
9-
10-
This program is distributed in the hope that it will be useful, but WITHOUT
11-
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12-
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13-
You should have received a copy of the GNU General Public License along with
14-
this program. If not, see <http://www.gnu.org/licenses/>.
15-
"""
16-
17-
from _version import __version_info__, __version__
18-
19-
__author__ = 'Mo Beigi'
20-
__copyright__ = 'Copyright 2019'
21-
__email__ = 'me@mobeigi.com'
22-
__license__ = "GPLv3"
23-
__maintainer__ = 'Mo Beigi'
24-
__status__ = 'Production'
25-
__website__ = 'https://git.io/fjMwr'
26-
27-
# Make metadata public to script
28-
__all__ = ['__author__', '__copyright__', '__email__', '__license__', '__maintainer__', '__status__', '__website__', '__version_info__', '__version__']
1+
"""
2+
fb2cal - Facebook Birthday Events to ICS file converter
3+
Created by: mobeigi
4+
5+
This program is free software: you can redistribute it and/or modify it under
6+
the terms of the GNU General Public License as published by the Free Software
7+
Foundation, either version 3 of the License, or (at your option) any later
8+
version.
9+
10+
This program is distributed in the hope that it will be useful, but WITHOUT
11+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12+
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13+
You should have received a copy of the GNU General Public License along with
14+
this program. If not, see <http://www.gnu.org/licenses/>.
15+
"""
16+
17+
from .__meta__ import *
18+
from .transformer import *
19+
from .facebook_user import *
20+
from .facebook_browser import *
21+
from .ics_writer import *

fb2cal/__main__.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
fb2cal - Facebook Birthday Events to ICS file converter
5+
Created by: mobeigi
6+
7+
This program is free software: you can redistribute it and/or modify it under
8+
the terms of the GNU General Public License as published by the Free Software
9+
Foundation, either version 3 of the License, or (at your option) any later
10+
version.
11+
12+
This program is distributed in the hope that it will be useful, but WITHOUT
13+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14+
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15+
You should have received a copy of the GNU General Public License along with
16+
this program. If not, see <http://www.gnu.org/licenses/>.
17+
"""
18+
19+
import os
20+
import sys
21+
import logging
22+
from distutils import util
23+
24+
from .ics_writer import ICSWriter
25+
from .logger import Logger
26+
from .config import Config
27+
from .facebook_browser import FacebookBrowser
28+
from .transformer import Transformer
29+
30+
from .__init__ import __version__, __status__, __website__, __license__
31+
32+
# Set CWD to script directory
33+
os.chdir(sys.path[0])
34+
35+
# Init logger
36+
logger = Logger('fb2cal').getLogger()
37+
logger.info(f'Starting fb2cal v{__version__} ({__status__}) [{__website__}]')
38+
logger.info(f'This project is released under the {__license__} license.')
39+
40+
try:
41+
# Read config
42+
logger.info(f'Attemping to parse config file...')
43+
config = Config().getConfig()
44+
logger.info('Config successfully loaded.')
45+
46+
# Set logging level based on config
47+
try:
48+
logger.setLevel(getattr(logging, config['LOGGING']['level']))
49+
logging.getLogger().setLevel(logger.level) # Also set root logger level
50+
except AttributeError:
51+
logger.error(f'Invalid logging level specified. Level: {config["LOGGING"]["level"]}')
52+
raise SystemError
53+
54+
logger.info(f'Logging level set to: {logging.getLevelName(logger.level)}')
55+
56+
# Init Facebook browser
57+
facebook_browser = FacebookBrowser()
58+
59+
# Attempt login
60+
logger.info('Attemping to authenticate with Facebook...')
61+
facebook_browser.authenticate(config['AUTH']['FB_EMAIL'], config['AUTH']['FB_PASS'])
62+
logger.info('Successfully authenticated with Facebook.')
63+
64+
# Fetch birthdays for a full calendar year and transform them
65+
facebook_users = []
66+
transformer = Transformer()
67+
68+
# Endpoint will return all birthdays for offset_month plus the following 2 consecutive months.
69+
logger.info('Fetching all Birthdays via BirthdayCometRootQuery endpoint...')
70+
for offset_month in [1, 4, 7, 10]:
71+
birthday_comet_root_json = facebook_browser.query_graph_ql_birthday_comet_root(offset_month)
72+
facebook_users_for_quarter = transformer.transform_birthday_comet_root_to_birthdays(birthday_comet_root_json)
73+
facebook_users.extend(facebook_users_for_quarter)
74+
75+
if len(facebook_users) == 0:
76+
logger.warning(f'Facebook user list is empty. Failed to fetch any birthdays.')
77+
raise SystemError
78+
79+
logger.info(f'A total of {len(facebook_users)} birthdays were found.')
80+
81+
# Generate ICS
82+
ics_writer = ICSWriter(facebook_users)
83+
logger.info('Creating birthday ICS file...')
84+
ics_writer.generate()
85+
logger.info('ICS file created successfully.')
86+
87+
# Save to file system
88+
if util.strtobool(config['FILESYSTEM']['SAVE_TO_FILE']):
89+
ics_writer.write(config['FILESYSTEM']['ICS_FILE_PATH'])
90+
91+
logger.info('Done! Terminating gracefully.')
92+
except SystemExit:
93+
logger.critical(f'Critical error encountered. Terminating.')
94+
sys.exit()
95+
finally:
96+
logging.shutdown()

fb2cal/__meta__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
__author__ = 'Mo Beigi'
2+
__copyright__ = 'Copyright 2019-2020'
3+
__email__ = 'me@mobeigi.com'
4+
__license__ = "GPLv3"
5+
__maintainer__ = 'Mo Beigi'
6+
__status__ = 'Production'
7+
__website__ = 'https://git.io/fjMwr'
8+
__version_info__ = (1, 2, 0)
9+
__version__ = '.'.join(map(str, __version_info__))
10+
11+
12+
# Make metadata public to script
13+
__all__ = ['__author__', '__copyright__', '__email__', '__license__', '__maintainer__', '__status__', '__website__', '__version_info__', '__version__']

src/config.py renamed to fb2cal/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import configparser
2-
from logger import Logger
2+
from .logger import Logger
33

44
CONFIG_FILE_NAME = 'config.ini'
5-
CONFIG_FILE_PATH = f'../config/{CONFIG_FILE_NAME}'
5+
CONFIG_FILE_PATH = f'config/{CONFIG_FILE_NAME}'
66
CONFIG_FILE_TEMPLATE_NAME = 'config-template.ini'
77

88
class Config:
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
import re
33
import requests
44
import json
5-
from logger import Logger
5+
6+
from .logger import Logger
67

78
class FacebookBrowser:
89
def __init__(self):
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
from datetime import datetime, timedelta
66
from dateutil.relativedelta import relativedelta
77
import calendar
8-
from logger import Logger
98

10-
from __init__ import __version__, __status__, __website__
9+
from .logger import Logger
10+
from .__init__ import __version__, __status__, __website__
1111

1212
""" Write Birthdays to an ICS file """
1313
class ICSWriter:
@@ -66,3 +66,6 @@ def write(self, ics_file_path):
6666
with open(ics_file_path, mode='w', encoding="UTF-8") as ics_file:
6767
ics_file.write(ics_str)
6868
self.logger.info(f'Successfully saved ICS file to {os.path.abspath(ics_file_path)}')
69+
70+
def get_birthday_calendar(self):
71+
return self.birthday_calendar

0 commit comments

Comments
 (0)