def transition_done_record(update_fields: dict, record: dict, calendar: Calendar) -> Dict: """ Transitions recently marked "Done" and "Abandoned" `Status` records Does the following for "Done" and "Abandoned" Records 1. Changes Google Calendar Event color to the completed color 2. Sets the `lastStatus` field, which makes it an inactive record Args: update_fields: The payload dictionary that will be sent in a patch/post request to the Airtable API record: The individual record being processed calendar: The :obj:`calendar_request.Calendar` instance corresponding to the calendar out of which we're working Returns: An updated-version of `update_fields` to be sent to airtable in a patch/post request """ status = get_in(record, ["fields", "Status"], "") if status == "Done" or status == "Abandoned": color_id = GCAL_COLOR_MAPPING[status] calendar_event_id = get_in(record, ["fields", "calendarEventId"]) airtable_record_id = get_in(record, ["id"]) if calendar_event_id: calendar.patch_event(calendar_event_id, airtable_record_id, color_id=color_id) update_fields.update({ "lastStatus": "Done", }) return update_fields
def process_name_change(update_fields: dict, record: dict, calendar: Calendar) -> Dict: """ Detects records where the `Name` changed and updates the update_field payload accordingly A deadline change is detected when the `Name` does not equal the `lastName` field. Actions: 1. Updates the Gcal event 2. Update the `lastName` field in Airtable Args: update_fields: The payload dictionary that will be sent in a patch/post request to the Airtable API record: The individual record being processed calendar: The :obj:`calendar_request.Calendar` instance corresponding to the calendar out of which we're working Returns: An updated-version of `update_fields` to be sent to airtable in a patch/post request """ name = get_in(record, ["fields", "Name"]) last_name = get_in(record, ["fields", "lastName"], "") if name != last_name: airtable_record_id = get_in(record, ['id']) calendar_event_id = get_in(record, ["fields", "calendarEventId"]) # valid calendar_event_id if calendar_event_id: calendar.patch_event(calendar_event_id, airtable_record_id, title=name) print(f'new_name: {name}') update_fields.update({ "lastName": name, }) return update_fields
def process_deadline_change(update_fields: dict, record: dict, calendar: Calendar) -> Dict: """ Detects records where the `Deadline` changed and updates the update_field payload accordingly A deadline change is detected when the `deadline` does not equal the `lastDeadline` field. Actions: 1. Updates the Gcal event, if the detected deadline change did not originate from the webhook 2. Update the `Deadline Group`, `Day`, and `lastDeadline` fields in Airtable Args: update_fields: The payload dictionary that will be sent in a patch/post request to the Airtable API record: The individual record being processed calendar: The :obj:`calendar_request.Calendar` instance corresponding to the calendar out of which we're working Returns: An updated-version of `update_fields` to be sent to airtable in a patch/post request """ deadline = get_in(record, ["fields", "Deadline"]) last_deadline = get_in(record, ["fields", "lastDeadline"], "") if deadline != last_deadline: calendar_event_id = get_in(record, ["fields", "calendarEventId"]) duration = get_in(record, ["fields", "duration"]) lastCalendarDeadline = get_in(record, ["fields", "lastCalendarDeadline"], "")[0:10] airtable_record_id = get_in(record, ["id"]) deadline_date = datetime.strptime(deadline, "%Y-%m-%d") + timedelta(hours=16) days_to_sunday = 6 - deadline_date.weekday() next_sunday = (deadline_date + timedelta(days=days_to_sunday)).strftime("%m/%d") if not duration: duration = 1 # valid calendar_event_id; and wasn't recently updated by gcal webhook if calendar_event_id and lastCalendarDeadline != deadline: calendar.patch_event(calendar_event_id, airtable_record_id, start=deadline_date, duration=duration) update_fields.update({ "Deadline Group": next_sunday, "Day": DAY_OF_WEEK[deadline_date.weekday()], "lastDeadline": deadline }) return update_fields
def process_new_event(event: dict, calendar: Calendar): """ Create an Airtable record for the new event, then link the record with the event Args: event: Dictionary that stores the event's information calendar: The :obj:`calendar_request.Calendar` associated with the calendar we're editting """ if get_in(event, ["status"], "cancelled") == "cancelled": return # Create Airtable Record payload = {"records": [create_payload_from_event(event)], "typecast": True} response = airtable_request("post", json=payload).json() # get airtable id airtable_record_id = response['records'][0]['id'] # attach to airtable_record_id to event description calendar_event_id = get_in(event, ['id']) calendar.patch_event(calendar_event_id, airtable_record_id) return
def process_new_record(update_fields: dict, record: dict, calendar: Calendar) -> Dict: """ Detects and adds New Records to the update_field payload New Records are defined as: - lastDeadline is not set - Deadline is set Note: Events/Tasks created by the Gcal Webhook are considered New Records Does the following for New Records: 1. Creates the initial Gcal Event, if not already created 2. Populates the Deadline Group field in Airtable with the week-grouping or "Today" 3. Populates the Day field in AirTable with the correct string "Mon"..."Sun" Args: update_fields: The payload dictionary that will be sent in a patch/post request to the Airtable API record: The individual record being processed calendar: The :obj:`calendar_request.Calendar` instance corresponding to the calendar out of which we're working Returns: An updated-version of `update_fields` to be sent to airtable in a patch/post request """ deadline = get_in(record, ["fields", "Deadline"]) lastDeadline = get_in(record, ["fields", "lastDeadline"]) if deadline and not lastDeadline: name = get_in(record, ["fields", "Name"]) airtable_record_id = get_in(record, ['id']) calendar_event_id = get_in(record, ["fields", "calendarEventId"]) deadline_date = datetime.strptime(deadline, "%Y-%m-%d") + timedelta(hours=16) if not calendar_event_id: created_event = calendar.create_event(name, deadline_date, airtable_record_id) update_fields.update({ "calendarEventId": created_event['id'], "duration": 1, "lastDeadline": deadline, }) return update_fields
def sync(): """ Retrieves active records and then updates the records with outlined logic """ active_records = get_active_records() calendar = Calendar(CALENDAR_ID) update_records(calendar, active_records)
from flask import Flask, request, jsonify, Response from flask_sqlalchemy import SQLAlchemy from sqlalchemy.sql import func from dotenv import load_dotenv from calendar_request import Calendar from airtable_request import airtable_request, update_payload_state, send_nonempty_payload, single_airtable_request load_dotenv() app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DATABASE_URL'] app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) CALENDAR_ID = os.getenv('CALENDAR_ID') calendar = Calendar(CALENDAR_ID) GCAL_COLOR_MAPPING = { "5": "Done", # yellow ish "11": "Abandoned" # red ish } # some GCAL magic variables here class Snapshot(db.Model): """ Snapshot defines the model for a Postgres database entry Attributes: id (int): The primary key and unique identifier for the row syncToken (str): The syncToken granted by the Gcal API used for keeping track of changes time_created (Datetime): Datetime when the record was inserted to Postgres """