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
Пример #4
0
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)
Пример #7
0
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
    """