Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests and refactor into module #84

Merged
merged 2 commits into from
Nov 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 git@github.com: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__ = 'me@mobeigi.com'
__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__ = 'me@mobeigi.com'
__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