Skip to content

Commit

Permalink
Add tests and refactor into module (#84)
Browse files Browse the repository at this point in the history
* Refactor to introduce __meta__ file

* Add tests and refactor code into a module
  • Loading branch information
mobeigi authored Nov 13, 2020
1 parent 8b3c39e commit d3e9dc5
Show file tree
Hide file tree
Showing 17 changed files with 3,773 additions and 147 deletions.
6 changes: 1 addition & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
# Config file
config/config.ini

# Sensitive credentials
src/credentials.json
src/token.json

# Logs
logs/

# Output files
*.ics
out/

# Github.com gitignore/Python.gitignore
# Byte-compiled / optimized / DLL files
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ After gathering a list of birthdays for all the users friends for a full year, i
1. Clone repo
`git clone [email protected]:mobeigi/fb2cal.git`
2. Rename `config/config-template.ini` to `config/config.ini` and enter your Facebook email and password (no quotes).
3. Install required python modules
3. Set up pipenv environment
`pipenv install`
4. Run the script manually:
`pipenv run python src/fb2cal.py`
5. Import the created `birthdays.ics` file into Calendar applications (i.e. Google Calendar)
4. Run the `fb2cal` module
`pipenv run python -m fb2cal`
5. Check the output folder (`out` by default) for the created `birthdays.ics` file

## Configuration
This tool can be configured by editing the `config/config.ini` configuration file.
Expand Down
2 changes: 1 addition & 1 deletion config/config-template.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ fb_pass =

[FILESYSTEM]
save_to_file = True
ics_file_path = ./birthdays.ics
ics_file_path = ./out/birthdays.ics

[LOGGING]
level = INFO
49 changes: 21 additions & 28 deletions src/__init__.py → fb2cal/__init__.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
"""
fb2cal - Facebook Birthday Events to ICS file converter
Created by: mobeigi
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
"""

from _version import __version_info__, __version__

__author__ = 'Mo Beigi'
__copyright__ = 'Copyright 2019'
__email__ = '[email protected]'
__license__ = "GPLv3"
__maintainer__ = 'Mo Beigi'
__status__ = 'Production'
__website__ = 'https://git.io/fjMwr'

# Make metadata public to script
__all__ = ['__author__', '__copyright__', '__email__', '__license__', '__maintainer__', '__status__', '__website__', '__version_info__', '__version__']
"""
fb2cal - Facebook Birthday Events to ICS file converter
Created by: mobeigi
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
"""

from .__meta__ import *
from .transformer import *
from .facebook_user import *
from .facebook_browser import *
from .ics_writer import *
96 changes: 96 additions & 0 deletions fb2cal/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env python3

"""
fb2cal - Facebook Birthday Events to ICS file converter
Created by: mobeigi
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
"""

import os
import sys
import logging
from distutils import util

from .ics_writer import ICSWriter
from .logger import Logger
from .config import Config
from .facebook_browser import FacebookBrowser
from .transformer import Transformer

from .__init__ import __version__, __status__, __website__, __license__

# Set CWD to script directory
os.chdir(sys.path[0])

# Init logger
logger = Logger('fb2cal').getLogger()
logger.info(f'Starting fb2cal v{__version__} ({__status__}) [{__website__}]')
logger.info(f'This project is released under the {__license__} license.')

try:
# Read config
logger.info(f'Attemping to parse config file...')
config = Config().getConfig()
logger.info('Config successfully loaded.')

# Set logging level based on config
try:
logger.setLevel(getattr(logging, config['LOGGING']['level']))
logging.getLogger().setLevel(logger.level) # Also set root logger level
except AttributeError:
logger.error(f'Invalid logging level specified. Level: {config["LOGGING"]["level"]}')
raise SystemError

logger.info(f'Logging level set to: {logging.getLevelName(logger.level)}')

# Init Facebook browser
facebook_browser = FacebookBrowser()

# Attempt login
logger.info('Attemping to authenticate with Facebook...')
facebook_browser.authenticate(config['AUTH']['FB_EMAIL'], config['AUTH']['FB_PASS'])
logger.info('Successfully authenticated with Facebook.')

# Fetch birthdays for a full calendar year and transform them
facebook_users = []
transformer = Transformer()

# Endpoint will return all birthdays for offset_month plus the following 2 consecutive months.
logger.info('Fetching all Birthdays via BirthdayCometRootQuery endpoint...')
for offset_month in [1, 4, 7, 10]:
birthday_comet_root_json = facebook_browser.query_graph_ql_birthday_comet_root(offset_month)
facebook_users_for_quarter = transformer.transform_birthday_comet_root_to_birthdays(birthday_comet_root_json)
facebook_users.extend(facebook_users_for_quarter)

if len(facebook_users) == 0:
logger.warning(f'Facebook user list is empty. Failed to fetch any birthdays.')
raise SystemError

logger.info(f'A total of {len(facebook_users)} birthdays were found.')

# Generate ICS
ics_writer = ICSWriter(facebook_users)
logger.info('Creating birthday ICS file...')
ics_writer.generate()
logger.info('ICS file created successfully.')

# Save to file system
if util.strtobool(config['FILESYSTEM']['SAVE_TO_FILE']):
ics_writer.write(config['FILESYSTEM']['ICS_FILE_PATH'])

logger.info('Done! Terminating gracefully.')
except SystemExit:
logger.critical(f'Critical error encountered. Terminating.')
sys.exit()
finally:
logging.shutdown()
13 changes: 13 additions & 0 deletions fb2cal/__meta__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
__author__ = 'Mo Beigi'
__copyright__ = 'Copyright 2019-2020'
__email__ = '[email protected]'
__license__ = "GPLv3"
__maintainer__ = 'Mo Beigi'
__status__ = 'Production'
__website__ = 'https://git.io/fjMwr'
__version_info__ = (1, 2, 0)
__version__ = '.'.join(map(str, __version_info__))


# Make metadata public to script
__all__ = ['__author__', '__copyright__', '__email__', '__license__', '__maintainer__', '__status__', '__website__', '__version_info__', '__version__']
4 changes: 2 additions & 2 deletions src/config.py → fb2cal/config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import configparser
from logger import Logger
from .logger import Logger

CONFIG_FILE_NAME = 'config.ini'
CONFIG_FILE_PATH = f'../config/{CONFIG_FILE_NAME}'
CONFIG_FILE_PATH = f'config/{CONFIG_FILE_NAME}'
CONFIG_FILE_TEMPLATE_NAME = 'config-template.ini'

class Config:
Expand Down
3 changes: 2 additions & 1 deletion src/facebook_browser.py → fb2cal/facebook_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import re
import requests
import json
from logger import Logger

from .logger import Logger

class FacebookBrowser:
def __init__(self):
Expand Down
File renamed without changes.
7 changes: 5 additions & 2 deletions src/ics_writer.py → fb2cal/ics_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
import calendar
from logger import Logger

from __init__ import __version__, __status__, __website__
from .logger import Logger
from .__init__ import __version__, __status__, __website__

""" Write Birthdays to an ICS file """
class ICSWriter:
Expand Down Expand Up @@ -66,3 +66,6 @@ def write(self, ics_file_path):
with open(ics_file_path, mode='w', encoding="UTF-8") as ics_file:
ics_file.write(ics_str)
self.logger.info(f'Successfully saved ICS file to {os.path.abspath(ics_file_path)}')

def get_birthday_calendar(self):
return self.birthday_calendar
2 changes: 1 addition & 1 deletion src/logger.py → fb2cal/logger.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os
import logging

LOGGING_FILE_PATH = '../logs/fb2cal.log'
LOGGING_FILE_PATH = 'logs/fb2cal.log'

class Logger:
def __init__(self, name):
Expand Down
8 changes: 4 additions & 4 deletions src/transformer.py → fb2cal/transformer.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
from facebook_user import FacebookUser
from .facebook_user import FacebookUser

class Transformer:

def transform_birthday_comet_root_to_birthdays(self, birthday_comet_root_json):
""" Transforms outfrom from BirthdayCometRootQuery to list of Birthdays """

birthdays = []
facebook_users = []

for all_friends_by_birthday_month_edge in birthday_comet_root_json['data']['viewer']['all_friends_by_birthday_month']['edges']:
for friend_edge in all_friends_by_birthday_month_edge['node']['friends']['edges']:
friend = friend_edge['node']

# Create Birthday object
birthdays.append(
facebook_users.append(
FacebookUser(
friend["id"],
friend["name"],
Expand All @@ -22,4 +22,4 @@ def transform_birthday_comet_root_to_birthdays(self, birthday_comet_root_json):
friend["birthdate"]["month"]
))

return birthdays
return facebook_users
2 changes: 0 additions & 2 deletions src/_version.py

This file was deleted.

97 changes: 0 additions & 97 deletions src/fb2cal.py

This file was deleted.

Loading

0 comments on commit d3e9dc5

Please sign in to comment.