diff --git a/example_credentials.py b/example_credentials.py index 7a6fc1f..a0748d8 100644 --- a/example_credentials.py +++ b/example_credentials.py @@ -2,7 +2,7 @@ # Timetable timetable_links = { - ### NOTE THE ORDER OF NAMES IN DICT MUST STAY THE SAME! ### + # NOTE THE ORDER OF NAMES IN DICT MUST STAY THE SAME! ### # Must USE _ UNDERSCORES, NO SPACES "Name1": "", "Name_Two": "", @@ -23,14 +23,14 @@ combined_cal = "" personal_cals = { - "Link": "Name1", - "Link": "Name_Two", + "Link": "Name", + "Link_Two": "Name_Two", } quote_url = "https://zenquotes.io/api/today" - +menu_sheet = "" # Set a testing date if you want the calendar to render a different date. # NOTE this doesn't affect date display/percentage etc. -testing_date = False +testing_date = None # testing_date = (2024,2,29) diff --git a/helper.py b/helper.py index 8e98e57..da88bd6 100644 --- a/helper.py +++ b/helper.py @@ -17,7 +17,11 @@ def get_ordinal_suffix(day: int) -> str: Returns: str: The ordinal suffix for the given day. """ - return {1: 'st', 2: 'nd', 3: 'rd'}.get(day % 10, 'th') if day not in (11, 12, 13) else 'th' + if 11 <= day <= 13: + return 'th' + else: + return {1: 'st', 2: 'nd', 3: 'rd'}.get(day % 10, 'th') + def getDateReadable() -> str: """ @@ -28,7 +32,6 @@ def getDateReadable() -> str: """ day_of_week = datetime.now(tz).strftime("%A") - day = int(datetime.now(tz).strftime("%d")) ordinal_suffix = get_ordinal_suffix(day) @@ -65,6 +68,7 @@ def getQuote() -> tuple[str, str]: return quote, author + def decide_day_check() -> date: """ Helper function that decides what date to check for events. @@ -76,6 +80,4 @@ def decide_day_check() -> date: # If past 8pm, check tomorrow's events if datetime.now(tz).hour >= 20: check_date += timedelta(1) - - return check_date diff --git a/main.py b/main.py index 7b704cf..c2902a1 100644 --- a/main.py +++ b/main.py @@ -5,7 +5,7 @@ import json -##* WEB APP ## +# WEB APP # app = Flask(__name__) # app.config.from_object('config') @@ -16,30 +16,32 @@ def home(): return render_template("index.html", quote=quote, author=author) + @app.route("/api/date") def date(): return json.dumps({"date": helper.getDateReadable()}) + @app.route("/api/cal") def cal(): return parse_calendar.parse_all_calendars() - #! TESTING LINE +# TESTING LINE with open("examplecal.json") as file: data = json.load(file) return data - @app.route("/api/menu") def menu(): return parse_menu.main() - #! TESTING LINE + # TESTING LINE with open("examplemenu.json") as file: data = json.load(file) return data + @app.route("/api/quote") def quote(): quote, author = helper.getQuote() @@ -49,4 +51,4 @@ def quote(): # Run as flask app (locally only) for testing. # Production level runs gunicorn (WSGI server) if __name__ == "__main__": - app.run(debug=True) \ No newline at end of file + app.run(debug=True) diff --git a/parse_calendar.py b/parse_calendar.py index 8b7ce91..9608dd4 100644 --- a/parse_calendar.py +++ b/parse_calendar.py @@ -1,7 +1,8 @@ """ parse_calendar.py -This file is responsible for parsing the calendar data from the icalendar format to a json format. +This file is responsible for parsing the calendar data from the icalendar +format to a json format. """ import icalendar @@ -11,12 +12,10 @@ import requests import json -from credentials import * +import credentials import helper - - def download_calendar(url): """ Downloads a calendar from the specified URL @@ -43,9 +42,9 @@ def get_events_today(cal) -> list: events_today = recurring_ical_events.of(cal).at(check_date) - if testing_date != False: - events_today = recurring_ical_events.of(cal).at(testing_date) - + if credentials.testing_date is not None: + events_today = recurring_ical_events.of( + cal).at(credentials.testing_date) return events_today @@ -57,7 +56,8 @@ def boundary_checks(event): event (dict): The event to be checked. Returns: - tuple: A tuple containing the modified event and a boolean indicating if the event is valid. + tuple: A tuple containing the modified event and a boolean indicating + if the event is valid. """ valid = True @@ -71,19 +71,24 @@ def boundary_checks(event): if event['DTSTART'].dt.hour >= 21 and event['DTEND'].dt.hour >= 21: valid = False - # if event ends after the start date, set end to start date then set time to 9pm - #! NOTE this means multiday events w/ times only work on day 1 + # if event ends after the start date + # set end to start date then set time to 9pm + # NOTE this means multiday events w/ times only work on day 1 if event['DTEND'].dt.day > event['DTSTART'].dt.day: - event['DTEND'].dt = event['DTEND'].dt.replace(day=event['DTSTART'].dt.day) - event['DTEND'].dt = event['DTEND'].dt.replace(hour=21, minute=0, second=0) + event['DTEND'].dt = event['DTEND'].dt.replace( + day=event['DTSTART'].dt.day) + event['DTEND'].dt = event['DTEND'].dt.replace( + hour=21, minute=0, second=0) # Set event end to 9pm if later if event['DTEND'].dt.hour >= 21 and event['DTEND'].dt.minute > 0: - event['DTEND'].dt = event['DTEND'].dt.replace(hour=21, minute=0, second=0) + event['DTEND'].dt = event['DTEND'].dt.replace( + hour=21, minute=0, second=0) # If event starts before 8am but stops after 8:30am, set start to 8am if event['DTSTART'].dt.hour < 8 and event['DTEND'].dt.hour >= 9: - event['DTSTART'].dt = event['DTSTART'].dt.replace(hour=8, minute=0, second=0) + event['DTSTART'].dt = event['DTSTART'].dt.replace( + hour=8, minute=0, second=0) return event, valid @@ -119,13 +124,14 @@ def determine_user_index(user: str) -> int: user_index = 2 case "Joshua_AC": user_index = 3 - case "Everyone": #default + case "Everyone": # default user_index = 4 - case _: # Case of error/doesnt match + case _: # Case of error/doesnt match user_index = 4 return user_index + def parse_single_timetable_calendar(url: str) -> str: """ Parse users' timetable calendar into json format @@ -156,13 +162,14 @@ def parse_single_timetable_calendar(url: str) -> str: event, valid = boundary_checks(event) # If event is invalid, i.e. out of bounds, skip to next event - if valid == False: + if not valid: continue # Clean event summary by removing any text after a colon if present # note we keep 1 word after the colon if ":" in event['SUMMARY']: - event['SUMMARY'] = event['SUMMARY'].split(':')[0] + ': ' + event['SUMMARY'].split(':')[1].split()[0] + event['SUMMARY'] = event['SUMMARY'].split( + ':')[0] + ': ' + event['SUMMARY'].split(':')[1].split()[0] # Calculate duration of event duration = event['DTEND'].dt - event['DTSTART'].dt @@ -173,7 +180,8 @@ def parse_single_timetable_calendar(url: str) -> str: "start": str(event['DTSTART'].dt), "end": str(event['DTEND'].dt), "location": str(event['LOCATION']) if 'LOCATION' in event else "", - "description": str(event['DESCRIPTION']) if 'DESCRIPTION' in event else "", + "description": str(event['DESCRIPTION']) if 'DESCRIPTION' in event + else "", "duration": str(duration) } @@ -185,8 +193,8 @@ def parse_single_timetable_calendar(url: str) -> str: def parse_combined_calendar(url: str, json_data: list) -> list: """ Parses a combined calendar by downloading the calendar from the given URL, - extracting today's events, applying boundary checks, and organizing the events - based on the person associated with each event. + extracting today's events, applying boundary checks, and organizing the + events based on the person associated with each event. Args: url (str): The URL of the calendar to download. @@ -208,13 +216,13 @@ def parse_combined_calendar(url: str, json_data: list) -> list: event, valid = boundary_checks(event) # If event is invalid, i.e. out of bounds, skip to next event - if valid == False: + if not valid: continue # Strip leading and trailing whitespace event['SUMMARY'] = event['SUMMARY'].strip() - #* Determine whose event this is + # * Determine whose event this is user_index_list = [] @@ -224,21 +232,20 @@ def parse_combined_calendar(url: str, json_data: list) -> list: # Default user = "Everyone" - # Loop through initials dictionary, checking the start of the event summary - for inits in initials: + # Loop through initials dictionary, checking start of event summary + for inits in credentials.initials: if event['SUMMARY'].startswith(inits): # Identify the user, then append this to the index list. - user = initials[inits] + user = credentials.initials[inits] # Remove those initials from event summary event['SUMMARY'] = event['SUMMARY'][len(inits):].strip() break - + # Update list with user index user_index_list.append(determine_user_index(user)) - # Check if multiperson event if event['SUMMARY'].startswith("&"): # Remove "&" from event summary @@ -256,7 +263,6 @@ def parse_combined_calendar(url: str, json_data: list) -> list: # Loop through user indexes for event for user_index in user_index_list: - # Clean event summary, where format is JK - EVENT or JK: EVENT if user_index != 4: # if dash present, remove text before it. Note split once only @@ -268,24 +274,23 @@ def parse_combined_calendar(url: str, json_data: list) -> list: # Strip leading and trailing whitespace event['SUMMARY'] = event['SUMMARY'].strip() - - # prepare json data event_data = { "name": str(event['SUMMARY']), "start": str(event['DTSTART'].dt), "end": str(event['DTEND'].dt), - "location": str(event['LOCATION']) if 'LOCATION' in event else "", - "description": str(event['DESCRIPTION']) if 'DESCRIPTION' in event else "", + "location": str(event['LOCATION']) if 'LOCATION' in event + else "", + "description": str(event['DESCRIPTION']) if 'DESCRIPTION' in + event else "", "duration": str(duration) } - ### Attempt to update JSON data + # Attempt to update JSON data # Check if user is "Everyone" if user_index == 4: user = "Everyone" - # Check for EVERYONE events. If event summary contains "[ALL]" if "[ALL]" in event_data["name"]: @@ -296,19 +301,18 @@ def parse_combined_calendar(url: str, json_data: list) -> list: user_data["events"].append(event_data) continue - #! Skips rest of the loop + # Skips rest of the loop - else: + else: try: json_data[4] except IndexError: # If "everyone" 5th element doesn't exist, create it json_data.append({"user": "Everyone", "events": []}) - + else: # If not everyone, get user from user index - user = list(initials.values())[user_index] - + user = list(credentials.initials.values())[user_index] # Append event to json data # print(user) @@ -316,7 +320,6 @@ def parse_combined_calendar(url: str, json_data: list) -> list: if user_data["user"] == user: user_data["events"].append(event_data) - # Return updated json data. return json_data @@ -326,12 +329,11 @@ def parse_personal_calendars(json_data: list) -> list: Parse personal calendars into json format """ - for url in personal_cals: + for url in credentials.personal_cals: cal = download_calendar(url) events_today = get_events_today(cal) - # Loop through today's events for event in events_today: @@ -339,14 +341,14 @@ def parse_personal_calendars(json_data: list) -> list: event, valid = boundary_checks(event) # If event is invalid, i.e. out of bounds, skip to next event - if valid == False: + if not valid: continue # Calculate duration of event duration = event['DTEND'].dt - event['DTSTART'].dt # Determine whose event this is - user = personal_cals[url] + user = credentials.personal_cals[url] # Check for EVERYONE events. If event summary ends in "EVERYONE" # then set user to everyone @@ -355,7 +357,8 @@ def parse_personal_calendars(json_data: list) -> list: # Remove "- EVERYONE" from end of event summary # and remove leading/trailing whitespace - event['SUMMARY'] = event['SUMMARY'].replace("- EVERYONE", "").strip() + event['SUMMARY'] = event['SUMMARY'].replace( + "- EVERYONE", "").strip() # If "everyone" element doesn't exist, create it try: @@ -368,8 +371,10 @@ def parse_personal_calendars(json_data: list) -> list: "name": str(event['SUMMARY']), "start": str(event['DTSTART'].dt), "end": str(event['DTEND'].dt), - "location": str(event['LOCATION']) if 'LOCATION' in event else "", - "description": str(event['DESCRIPTION']) if 'DESCRIPTION' in event else "", + "location": str(event['LOCATION']) if 'LOCATION' in event + else "", + "description": str(event['DESCRIPTION']) if 'DESCRIPTION' in + event else "", "duration": str(duration) } @@ -382,21 +387,22 @@ def parse_personal_calendars(json_data: list) -> list: def parse_all_calendars(): - """ - Iterate through all calendars available in credentials.py and parse them into json format + Iterate through all calendars available in credentials.py + and parse them into json format Example format in example.json """ # init json_data = [] # loop through url dictionary for timetables - for name in timetable_links: + for name in credentials.timetable_links: # Construct each json user_json_data = { "user": name, - "events": parse_single_timetable_calendar(timetable_links[name]) + "events": parse_single_timetable_calendar( + credentials.timetable_links[name]) } # Append to json list @@ -405,12 +411,11 @@ def parse_all_calendars(): # loop through additional calendars. # Parse combined calendar and update json_data - json_data = parse_combined_calendar(combined_cal, json_data) + json_data = parse_combined_calendar(credentials.combined_cal, json_data) # Parse personal calendars and update json_data json_data = parse_personal_calendars(json_data) - # Check if events are empty json_data = check_empty(json_data) diff --git a/parse_menu.py b/parse_menu.py index 799d1d7..8e96859 100644 --- a/parse_menu.py +++ b/parse_menu.py @@ -6,24 +6,22 @@ """ import gspread -import helper from datetime import date, timedelta -from credentials import * +import credentials import json import helper - def determine_chef(chef) -> str: # check who is cooking (ALL, JAC, JP, JT, JK) decoded_chef = chef - for inits in initials: + for inits in credentials.initials: if chef.startswith(inits): - decoded_chef = initials[inits] + decoded_chef = credentials.initials[inits] break - + return decoded_chef @@ -32,7 +30,7 @@ def main(): gc = gspread.service_account(filename="service_account.json") # Open the sheet - sh = gc.open_by_url("https://docs.google.com/spreadsheets/d/1Zck-jr2AarnekU2aEVZ2758NqdLvZ_zzRvqmzR2s704/edit#gid=1048819164") + sh = gc.open_by_url(credentials.menu_sheet) worksheet = sh.worksheet("Meal Plan") @@ -41,11 +39,11 @@ def main(): # Remove the header row values.pop(0) - # Get today's date + # Get today's date today_date = helper.getDateToday() # Calculate days since 13 Feb 2024 as a timedelta object - row_index = (today_date - date(2024, 2, 13)).days + row_index = (today_date - date(2024, 2, 13)).days # create json list menu = [] @@ -80,7 +78,7 @@ def main(): menu.append(day_data) - row_index+=1 + row_index += 1 today_date += timedelta(1) # print(menu) @@ -89,7 +87,6 @@ def main(): return json_menu - if __name__ == '__main__': json_menu = main() print(json_menu)