|
|
from enum import IntEnumimport datetimeimport re
from dateutil import rrule
from .calendar_api import calendar_apifrom .utils import to_dateTime, from_dateTime
class weekdays(IntEnum): SUN = 6 MON = 0 TUE = 1 WED = 2 THU = 3 FRI = 4 SAT = 5
class Event: """Model for a calendar event that can be uploaded"""
def __init__( self, start, end, name, description=None, recurrences=None, reminders=None, attendees=None, location=None, id=None, ): self.start = start self.attendees = attendees self.end = end self.name = name self.description = description self.location = location
if recurrences is None: self.recurrences = [] else: self.recurrences = recurrences self.id = id if reminders is None: self.reminders = {"useDefault": True} else: self.reminders = reminders
def add_reminder(self, until, method="popup"): """Add a reminder minutes before an event.
Use either a notification (popup) or email"""
assert method in ("email", "popup") self.reminders["useDefault"] = False if isinstance(until, datetime.timedelta): minutes_until = until.days * 24 * 60 minutes_until += until.seconds // 60 else: minutes_until = until self.reminders.setdefault("overrides", []).append( {"method": method, "minutes": minutes_until} )
def add_weekly_recurrence(self, until: datetime.datetime, *days): if not until.tzinfo: until = until.astimezone() self.recurrences.append( rrule.rrule( freq=rrule.WEEKLY, dtstart=self.start, wkst=weekdays.SUN, until=until, byweekday=days, ) )
def serialize_recurrences(self): for _rrule in self.recurrences: ret_str = str(_rrule).split("\n")[-1] ret_str = re.sub(r"(UNTIL=[^;]+)", r"\1Z", ret_str) yield ret_str
def to_json(self): keys = ("attendees", "description", "location", "reminders") ret = { "summary": self.name, "start": to_dateTime(self.start), "end": to_dateTime(self.end), } for _rrule in self.serialize_recurrences(): ret.setdefault("recurrence", []).append(_rrule) for key in keys: try: value = self.__getattribute__(key) if value: ret[key] = value except AttributeError: pass return ret
def add_attendees(self): pass
@classmethod def from_json(cls, body): args = {} args["name"] = body.get("summary", "unnamed") args["start"] = from_dateTime(body["start"]) args["end"] = from_dateTime(body["end"]) keys = ( "attendees", "description", "location", "recurrences", "reminders", "id", ) for key in keys: try: args[key] = body[key] except KeyError: pass instance = cls(**args) return instance
@classmethod def from_id(cls, api: calendar_api, calendar_id, event_id): """creates an event model from specified calendar and event_id""" event = api.get_event_by_id(calendar_id, event_id) return cls.from_json(event)
def upload(self, api: calendar_api, calendar_id): """Upload an event to calendar.
Either modifies an event in place or creates a new event."""
if self.id is not None: event_id = api.update_event(calendar_id, self.id, self.to_json()) else: event_id = api.create_event(calendar_id, self.to_json()) self.id = event_id return event_id
class Calendar: """Model for representing a Google calendar"""
def __init__(self, api: calendar_api, calendar_id): self.api = api
if calendar_id in api.ids.keys(): self.name = calendar_id self.id = self.api.ids[self.name] elif calendar_id in api.ids.values(): self.name = self.api.calendars[calendar_id]["summary"] self.id = calendar_id else: raise ValueError("Non-existent calendar specified") self.calendar_id = calendar_id
def update_or_add_event(self, event: Event): event.upload(self.api, self.calendar_id)
def get_events( self, start: datetime.datetime = None, end: datetime.datetime = None ): return ( Event(event_representation) for event_representation in self.api.get_events_in_range( self.id, start, end ) )
def delete_event(self, event: Event): if event.id is not None: return self.api.delete_event(self.calendar_id, event.id)
def search_events( self, event_name_regex=None, event_description_regex=None, start=None, end=None ): for event in self.get_events(start, end): will_yield = False if event_name_regex is not None: will_yield = re.search(event_name_regex, Event.name) is not None
if event_description_regex is not None: will_yield = ( re.search(event_description_regex, Event.description) is not None or will_yield # noqa )
if will_yield: yield event
def calendar_dict(api: calendar_api): return dict( (calendar_name, Calendar(api, calendar_name)) for calendar_name in api.ids.keys() )
|