def _parse_event(self, user_id, event): try: uid = str(event['id']) # The entirety of the raw event data in json representation. raw_data = str(event) subject = event.get('name', '')[:SUBJECT_MAX_LEN] body = event.get('description', None) location = event.get('location', None) if location: location = location[:LOCATION_MAX_LEN] all_day = event.get('is_all_day_event', False) locked = True start = parse_datetime(event['start_time']) end = parse_datetime(event['end_time']) # See if we made the event if 'from' in event['from']: if event['from'].get('id') == user_id: locked = False recurrence = event['recurrence'] if event['is_recurrent'] else None busy = event['availability'] == 'busy' reminder_time = event.get('reminder_time') reminders = str([reminder_time] if reminder_time else []) participants = [] time_zone = 0 # FIXME: this ain't right -cg3 except (KeyError, AttributeError): raise MalformedEventError() return Event(account_id=self.account_id, uid=uid, provider_name=self.PROVIDER_NAME, raw_data=raw_data, subject=subject, body=body, location=location, reminders=reminders, recurrence=recurrence, start=start, end=end, busy=busy, all_day=all_day, locked=locked, time_zone=time_zone, source='remote', participants=participants)
def test_parse_datetime(): t = '20140104T102030Z' dt = parse_datetime(t) assert dt == arrow.get(2014, 01, 04, 10, 20, 30) t = '2014-01-15T17:00:00-05:00' dt = parse_datetime(t) assert dt == arrow.get(2014, 01, 15, 22, 00, 00) t = None dt = parse_datetime(t) assert dt is None t = 1426008600 dt = parse_datetime(t) assert dt == arrow.get(2015, 03, 10, 17, 30, 00)
def test_parse_datetime(): t = "20140104T102030Z" dt = parse_datetime(t) assert dt == arrow.get(2014, 1, 4, 10, 20, 30) t = "2014-01-15T17:00:00-05:00" dt = parse_datetime(t) assert dt == arrow.get(2014, 1, 15, 22, 0, 0) t = None dt = parse_datetime(t) assert dt is None t = 1426008600 dt = parse_datetime(t) assert dt == arrow.get(2015, 3, 10, 17, 30, 0)
def parse_event_response(event): """ Constructs an Event object from a Google event resource (a dictionary). See https://developers.google.com/google-apps/calendar/v3/reference/events Parameters ---------- event: dict Returns ------- A corresponding Event instance. This instance is not committed or added to a session. """ uid = str(event['id']) # The entirety of the raw event data in json representation. raw_data = json.dumps(event) title = event.get('summary', '') # Timing data _start = event['start'] _end = event['end'] _original = event.get('originalStartTime', {}) event_time = google_to_event_time(_start, _end) original_start = parse_google_time(_original) start_tz = _start.get('timeZone') last_modified = parse_datetime(event.get('updated')) description = event.get('description') location = event.get('location') busy = event.get('transparency') != 'transparent' # We're lucky because event statuses follow the icalendar # spec. event_status = event.get('status', 'confirmed') assert event_status in EVENT_STATUSES # Ownership, read_only information creator = event.get('creator') if creator: owner = u'{} <{}>'.format( creator.get('displayName', ''), creator.get('email', '')) else: owner = '' is_owner = bool(creator and creator.get('self')) read_only = not (is_owner or event.get('guestsCanModify')) participants = [] attendees = event.get('attendees', []) for attendee in attendees: status = STATUS_MAP[attendee.get('responseStatus')] participants.append({ 'email': attendee.get('email'), 'name': attendee.get('displayName'), 'status': status, 'notes': attendee.get('comment') }) # Recurring master or override info recurrence = event.get('recurrence') master_uid = event.get('recurringEventId') cancelled = (event.get('status') == 'cancelled') return Event(uid=uid, raw_data=raw_data, title=title, description=description, location=location, busy=busy, start=event_time.start, end=event_time.end, all_day=event_time.all_day, owner=owner, is_owner=is_owner, read_only=read_only, participants=participants, recurrence=recurrence, last_modified=last_modified, original_start_tz=start_tz, original_start_time=original_start, master_event_uid=master_uid, cancelled=cancelled, status=event_status, # TODO(emfree): remove after data cleanup source='local')
def parse_event(self, event, cal_info): """Constructs an Event object from a Google calendar entry. Parameters ---------- event: gdata.calendar.entry.CalendarEntry The Google calendar entry to parse. Returns ------- ..models.tables.base.Event A corresponding Inbox Event instance. Raises ------ MalformedEventError If the calendar data could not be parsed correctly. """ try: uid = str(event['id']) # The entirety of the raw event data in json representation. raw_data = str(event) # 'cancelled' events signify those instances within a series # that have been cancelled (for that given series). As such, # since full support for dealing with single instances within # a reocurring event series is not added, right now we just # ignore the event. -cg3 # TODO: Add support for reocurring events (see ways to handle # this generically across providers) if 'status' in event and event['status'] == 'cancelled': return None title = event.get('summary', '') description = event.get('description', None) location = event.get('location', None) all_day = False read_only = True is_owner = False start = event['start'] end = event['end'] g_recur = event.get('recurrence', None) recurrence = str(g_recur) if g_recur else None busy = event.get('transparency', True) if busy == 'transparent': busy = False reminders = [] if 'dateTime' in start: if event['reminders']['useDefault']: reminder_source = cal_info['defaultReminders'] elif 'overrides' in event['reminders']: reminder_source = event['reminders']['overrides'] else: reminder_source = None if reminder_source: for reminder in reminder_source: reminders.append(reminder['minutes']) try: start = parse_datetime(start['dateTime']) end = parse_datetime(end['dateTime']) except TypeError: self.log.error('Invalid start: {} or end: {}' .format(start['dateTime'], end['dateTime'])) raise MalformedEventError() else: start = date_parser.parse(start['date']) end = date_parser.parse(end['date']) all_day = True reminders = str(reminders) # Convert google's notion of status into our own participants = [] status_map = {'accepted': 'yes', 'needsAction': 'noreply', 'declined': 'no', 'tentative': 'maybe'} for attendee in event.get('attendees', []): g_status = attendee.get('responseStatus') if g_status not in status_map: raise MalformedEventError() status = status_map[g_status] email = attendee.get('email') if not email: raise MalformedEventError() name = attendee.get('displayName') notes = None guests = 0 if 'additionalGuests' in attendee: guests = attendee['additionalGuests'] elif 'comment' in attendee: notes = attendee['comment'] participants.append(Participant(email_address=email, name=name, status=status, notes=notes, guests=guests)) if 'self' in event['creator']: is_owner = True read_only = False elif 'guestsCanModify' in event: read_only = False owner = "" if 'creator' in event: creator = event['creator'] owner = u'{} <{}>'.format(creator['displayName'], creator['email']) except (KeyError, AttributeError): raise MalformedEventError() return Event(namespace_id=self.namespace_id, uid=uid, provider_name=self.PROVIDER_NAME, raw_data=raw_data, title=title, description=description, location=location, reminders=reminders, recurrence=recurrence, start=start, end=end, owner=owner, is_owner=is_owner, busy=busy, all_day=all_day, read_only=read_only, source='remote', participants=participants)
def parse_event_response(event, read_only_calendar): """ Constructs an Event object from a Google event resource (a dictionary). See https://developers.google.com/google-apps/calendar/v3/reference/events Parameters ---------- event: dict Returns ------- A corresponding Event instance. This instance is not committed or added to a session. """ uid = str(event['id']) # The entirety of the raw event data in json representation. raw_data = json.dumps(event) title = event.get('summary', '') # Timing data _start = event['start'] _end = event['end'] _original = event.get('originalStartTime', {}) event_time = google_to_event_time(_start, _end) original_start = parse_google_time(_original) start_tz = _start.get('timeZone') last_modified = parse_datetime(event.get('updated')) description = event.get('description') location = event.get('location') busy = event.get('transparency') != 'transparent' sequence = event.get('sequence', 0) # We're lucky because event statuses follow the icalendar # spec. event_status = event.get('status', 'confirmed') assert event_status in EVENT_STATUSES # Ownership, read_only information creator = event.get('creator') if creator: owner = u'{} <{}>'.format(creator.get('displayName', ''), creator.get('email', '')) else: owner = '' participants = [] attendees = event.get('attendees', []) for attendee in attendees: status = STATUS_MAP[attendee.get('responseStatus')] participants.append({ 'email': attendee.get('email'), 'name': attendee.get('displayName'), 'status': status, 'notes': attendee.get('comment') }) organizer = event.get('organizer') is_owner = bool(organizer and organizer.get('self')) # FIXME @karim: The right thing here would be to use Google's ACL API. # There's some obscure cases, like an autoimported event which guests can # edit that can't be modified. read_only = True if not read_only_calendar: read_only = False # Recurring master or override info recurrence = event.get('recurrence') master_uid = event.get('recurringEventId') cancelled = (event.get('status') == 'cancelled') return Event(uid=uid, raw_data=raw_data, title=title, description=description, location=location, busy=busy, start=event_time.start, end=event_time.end, all_day=event_time.all_day, owner=owner, is_owner=is_owner, read_only=read_only, participants=participants, recurrence=recurrence, last_modified=last_modified, original_start_tz=start_tz, original_start_time=original_start, master_event_uid=master_uid, cancelled=cancelled, status=event_status, sequence_number=sequence, source='local')
def populate(): # Populate new classes from the existing data from inbox.models.event import (Event, RecurringEvent, RecurringEventOverride) from inbox.models.session import session_scope from inbox.events.util import parse_datetime from inbox.events.recurring import link_events with session_scope() as db: # Redo recurrence rule population, since we extended the column length print "Repopulating max-length recurrences...", for e in db.query(Event).filter( sa.func.length(Event.recurrence) > 250): try: raw_data = json.loads(e.raw_data) except: try: raw_data = ast.literal_eval(e.raw_data) except: print "Could not load raw data for event {}".format(e.id) continue e.recurrence = raw_data['recurrence'] db.commit() print "done." print "Updating types for Override...", # Slightly hacky way to convert types (only needed for one-off import) convert = """UPDATE event SET type='recurringeventoverride' WHERE raw_data LIKE '%recurringEventId%'""" db.execute(convert) create = """INSERT INTO recurringeventoverride (id) SELECT id FROM event WHERE type='recurringeventoverride' AND id NOT IN (SELECT id FROM recurringeventoverride)""" try: db.execute(create) except Exception as e: print "Couldn't insert RecurringEventOverrides: {}".format(e) exit(2) print "done." c = 0 print "Expanding Overrides .", query = db.query(RecurringEventOverride) for e in query: try: # Some raw data is str(dict), other is json.dumps raw_data = json.loads(e.raw_data) except: try: raw_data = ast.literal_eval(e.raw_data) except: print "Could not load raw data for event {}".format(e.id) continue rec_uid = raw_data.get('recurringEventId') if rec_uid: e.master_event_uid = rec_uid ost = raw_data.get('originalStartTime') if ost: # this is a dictionary with one value start_time = ost.values().pop() e.original_start_time = parse_datetime(start_time) # attempt to get the ID for the event, if we can, and # set the relationship appropriately if raw_data.get('status') == 'cancelled': e.cancelled = True link_events(db, e) c += 1 if c % 100 == 0: print ".", sys.stdout.flush() db.commit() print "done. ({} modified)".format(c) # Convert Event to RecurringEvent print "Updating types for RecurringEvent...", convert = """UPDATE event SET type='recurringevent' WHERE recurrence IS NOT NULL""" db.execute(convert) create = """INSERT INTO recurringevent (id) SELECT id FROM event WHERE type='recurringevent' AND id NOT IN (SELECT id FROM recurringevent)""" try: db.execute(create) except Exception as e: print "Couldn't insert RecurringEvents: {}".format(e) exit(2) print "done." # Pull out recurrence metadata from recurrence c = 0 print "Expanding master events .", query = db.query(RecurringEvent) for r in query: r.unwrap_rrule() try: raw_data = json.loads(r.raw_data) except: try: raw_data = ast.literal_eval(r.raw_data) except: print "Could not load raw data for event {}".format(r.id) continue r.start_timezone = raw_data['start'].get('timeZone') # find any un-found overrides that didn't have masters earlier link_events(db, r) db.add(r) c += 1 if c % 100 == 0: print ".", sys.stdout.flush() db.commit() print "done. ({} modified)".format(c) # Finally, convert all remaining Events to type='event' convert = """UPDATE event SET type='event' WHERE type IS NULL""" db.execute(convert)
def parse_event(self, event, cal_info): """Constructs an Event object from a Google calendar entry. Parameters ---------- event: gdata.calendar.entry.CalendarEntry The Google calendar entry to parse. Returns ------- ..models.tables.base.Event A corresponding Inbox Event instance. Raises ------ MalformedEventError If the calendar data could not be parsed correctly. """ try: uid = str(event['id']) # The entirety of the raw event data in json representation. raw_data = str(event) # 'cancelled' events signify those instances within a series # that have been cancelled (for that given series). As such, # since full support for dealing with single instances within # a reocurring event series is not added, right now we just # treat this event as 'malformed'. -cg3 # TODO: Add support for reocurring events (see ways to handle # this generically across providers) if 'status' in event and event['status'] == 'cancelled': raise MalformedEventError() subject = event.get('summary', '')[:SUBJECT_MAX_LEN] body = event.get('description', None) location = event.get('location', None) if location: location = location[:LOCATION_MAX_LEN] all_day = False read_only = True is_owner = False start = event['start'] end = event['end'] g_reccur = event.get('recurrence', None) recurrence = str(g_reccur) if g_reccur else None busy = event.get('transparency', True) if busy == 'transparent': busy = False reminders = [] if 'dateTime' in start: if event['reminders']['useDefault']: reminder_source = cal_info['defaultReminders'] elif 'overrides' in event['reminders']: reminder_source = event['reminders']['overrides'] else: reminder_source = None if reminder_source: for reminder in reminder_source: reminders.append(reminder['minutes']) start = parse_datetime(start['dateTime']) end = parse_datetime(end['dateTime']) else: start = date_parser.parse(start['date']) end = date_parser.parse(end['date']) all_day = True reminders = str(reminders) # Convert google's notion of status into our own participants = [] status_map = {'accepted': 'yes', 'needsAction': 'noreply', 'declined': 'no', 'tentative': 'maybe'} for attendee in event.get('attendees', []): g_status = attendee.get('responseStatus') if g_status not in status_map: raise MalformedEventError() status = status_map[g_status] email = attendee.get('email') if not email: raise MalformedEventError() name = attendee.get('displayName') notes = None if 'additionalGuests' in attendee: notes = "Guests: {}".format(attendee['additionalGuests']) if 'comment' in attendee: notes += " Notes: {}".format(attendee['comment']) elif 'comment' in attendee: notes = "Notes: {}".format(attendee['comment']) participants.append(Participant(email_address=email, name=name, status=status, notes=notes)) if 'self' in event['creator']: is_owner = True read_only = False elif 'guestsCanModify' in event: read_only = False owner = "{} <{}>".format(event['creator']['displayName'], event['creator']['email']) except (KeyError, AttributeError): raise MalformedEventError() return Event(account_id=self.account_id, uid=uid, provider_name=self.PROVIDER_NAME, raw_data=raw_data, subject=subject, body=body, location=location, reminders=reminders, recurrence=recurrence, start=start, end=end, owner=owner, is_owner=is_owner, busy=busy, all_day=all_day, read_only=read_only, source='remote', participants=participants)
def parse_event_response(event): """ Constructs an Event object from a Google event resource (a dictionary). See https://developers.google.com/google-apps/calendar/v3/reference/events Parameters ---------- event: dict Returns ------- A corresponding Event instance. This instance is not committed or added to a session. """ uid = str(event['id']) # The entirety of the raw event data in json representation. raw_data = json.dumps(event) title = event.get('summary', '') # Timing data _start = event['start'] _end = event['end'] _original = event.get('originalStartTime', {}) event_time = google_to_event_time(_start, _end) original_start = parse_google_time(_original) start_tz = _start.get('timeZone') last_modified = parse_datetime(event.get('updated')) description = event.get('description') location = event.get('location') busy = event.get('transparency') != 'transparent' # We're lucky because an event statuses follow the icalendar # spec. event_status = event.get('status', 'confirmed') assert event_status in EVENT_STATUSES # Ownership, read_only information creator = event.get('creator') if creator: owner = u'{} <{}>'.format(creator.get('displayName', ''), creator.get('email', '')) else: owner = '' is_owner = bool(creator and creator.get('self')) read_only = not (is_owner or event.get('guestsCanModify')) participants = [] attendees = event.get('attendees', []) for attendee in attendees: status = STATUS_MAP[attendee.get('responseStatus')] participants.append({ 'email': attendee.get('email'), 'name': attendee.get('displayName'), 'status': status, 'notes': attendee.get('comment') }) # Recurring master or override info recurrence = event.get('recurrence') master_uid = event.get('recurringEventId') cancelled = (event.get('status') == 'cancelled') return Event( uid=uid, raw_data=raw_data, title=title, description=description, location=location, busy=busy, start=event_time.start, end=event_time.end, all_day=event_time.all_day, owner=owner, is_owner=is_owner, read_only=read_only, participants=participants, recurrence=recurrence, last_modified=last_modified, original_start_tz=start_tz, original_start_time=original_start, master_event_uid=master_uid, cancelled=cancelled, status=event_status, # TODO(emfree): remove after data cleanup source='local')
def _parse_event(self, cal_info, event): """Constructs a Calendar object from a Google calendar entry. Parameters ---------- google_calendar: gdata.calendar.entry.CalendarEntry The Google calendar entry to parse. Returns ------- ..models.tables.base.Calendar A corresponding Inbox Calendar instance. Raises ------ AttributeError If the calendar data could not be parsed correctly. """ try: uid = str(event['id']) # The entirety of the raw event data in json representation. raw_data = str(event) # 'cancelled' events signify those instances within a series # that have been cancelled (for that given series). As such, # since full support for dealing with single instances within # a reocurring event series is not added, right now we just # treat this event as 'malformed'. -cg3 # TODO: Add support for reocurring events (see ways to handle # this generically across providers) if 'status' in event and event['status'] == 'cancelled': raise MalformedEventError() subject = event.get('summary', '')[0:1023] body = event.get('description', None) location = event.get('location', None) if location: location = location[0:254] all_day = False locked = True start = event['start'] end = event['end'] recurrence = str(event.get('recurrence', None)) busy = event.get('transparency', True) if busy == 'transparent': busy = False reminders = [] if 'dateTime' in start: if event['reminders']['useDefault']: reminder_source = cal_info['defaultReminders'] elif 'overrides' in event['reminders']: reminder_source = event['reminders']['overrides'] else: reminder_source = None if reminder_source: for reminder in reminder_source: reminders.append(reminder['minutes']) start = parse_datetime(start['dateTime']) end = parse_datetime(end['dateTime']) else: start = date_parser.parse(start['date']) end = date_parser.parse(end['date']) all_day = True reminders = str(reminders) if 'self' in event['creator']: locked = False elif 'guestsCanModify' in event: locked = False time_zone = cal_info['timeZone'] time_zone = 0 # FIXME: this ain't right -cg3 except (KeyError, AttributeError): raise MalformedEventError() return Event(account_id=self.account_id, uid=uid, provider_name=self.PROVIDER_NAME, raw_data=raw_data, subject=subject, body=body, location=location, reminders=reminders, recurrence=recurrence, start=start, end=end, busy=busy, all_day=all_day, locked=locked, time_zone=time_zone, source='remote')
def parse_event(self, event, cal_info): """Constructs an Event object from a Google calendar entry. Parameters ---------- event: gdata.calendar.entry.CalendarEntry The Google calendar entry to parse. Returns ------- ..models.tables.base.Event A corresponding Inbox Event instance. Raises ------ MalformedEventError If the calendar data could not be parsed correctly. """ try: uid = str(event['id']) # The entirety of the raw event data in json representation. raw_data = str(event) # 'cancelled' events signify those instances within a series # that have been cancelled (for that given series). As such, # since full support for dealing with single instances within # a reocurring event series is not added, right now we just # ignore the event. -cg3 # TODO: Add support for reocurring events (see ways to handle # this generically across providers) if 'status' in event and event['status'] == 'cancelled': return None title = event.get('summary', '') description = event.get('description', None) location = event.get('location', None) all_day = False read_only = True is_owner = False start = event['start'] end = event['end'] g_recur = event.get('recurrence', None) recurrence = str(g_recur) if g_recur else None busy = event.get('transparency', True) if busy == 'transparent': busy = False reminders = [] if 'dateTime' in start: if event['reminders']['useDefault']: reminder_source = cal_info['defaultReminders'] elif 'overrides' in event['reminders']: reminder_source = event['reminders']['overrides'] else: reminder_source = None if reminder_source: for reminder in reminder_source: reminders.append(reminder['minutes']) try: start = parse_datetime(start['dateTime']) end = parse_datetime(end['dateTime']) except TypeError: self.log.error('Invalid start: {} or end: {}'.format( start['dateTime'], end['dateTime'])) raise MalformedEventError() else: start = date_parser.parse(start['date']) end = date_parser.parse(end['date']) all_day = True reminders = str(reminders) # Convert google's notion of status into our own participants = [] for attendee in event.get('attendees', []): g_status = attendee.get('responseStatus') if g_status not in GoogleEventsProvider.status_map: raise MalformedEventError() status = GoogleEventsProvider.status_map[g_status] email = attendee.get('email') if not email: raise MalformedEventError() name = attendee.get('displayName') notes = None guests = 0 if 'additionalGuests' in attendee: guests = attendee['additionalGuests'] elif 'comment' in attendee: notes = attendee['comment'] participants.append({ 'email_address': email, 'name': name, 'status': status, 'notes': notes, 'guests': guests }) if 'guestsCanModify' in event: read_only = False owner = '' if 'creator' in event: creator = event['creator'] if 'self' in creator: is_owner = True read_only = False owner = u'{} <{}>'.format(creator.get('displayName', ''), creator.get('email', '')) except (KeyError, AttributeError): raise MalformedEventError() return Event(namespace_id=self.namespace_id, uid=uid, provider_name=self.PROVIDER_NAME, raw_data=raw_data, title=title, description=description, location=location, reminders=reminders, recurrence=recurrence, start=start, end=end, owner=owner, is_owner=is_owner, busy=busy, all_day=all_day, read_only=read_only, source='remote', participants=participants)
def parse_event(self, event, extra): user_id = extra['user_id'] stored_uids = extra['stored_uids'] try: uid = str(event['id']) if uid in stored_uids: raise MalformedEventError() # The entirety of the raw event data in json representation. raw_data = str(event) title = event.get('name', '') description = event.get('description', None) location = event.get('location', None) all_day = event.get('is_all_day_event', False) read_only = True is_owner = False owner = None start = parse_datetime(event['start_time']) end = parse_datetime(event['end_time']) # See if we made the event if 'from' in event['from']: if event['from'].get('id') == user_id: is_owner = True read_only = False else: is_owner = False owner = event['from'].get('name') recurrence = event['recurrence'] if event['is_recurrent'] else None busy = event['availability'] == 'busy' reminder_time = event.get('reminder_time') reminders = str([reminder_time] if reminder_time else []) participants = [] except (KeyError, AttributeError): raise MalformedEventError() stored_uids.append(uid) return Event(namespace_id=self.namespace_id, uid=uid, provider_name=self.PROVIDER_NAME, raw_data=raw_data, title=title, description=description, location=location, reminders=reminders, recurrence=recurrence, start=start, end=end, busy=busy, all_day=all_day, read_only=read_only, is_owner=is_owner, owner=owner, source='remote', participants=participants)
def parse_event_response(event, read_only_calendar): """ Constructs an Event object from a Google event resource (a dictionary). See https://developers.google.com/google-apps/calendar/v3/reference/events Parameters ---------- event: dict Returns ------- A corresponding Event instance. This instance is not committed or added to a session. """ uid = str(event["id"]) # The entirety of the raw event data in json representation. raw_data = json.dumps(event) title = event.get("summary", "") # Timing data _start = event["start"] _end = event["end"] _original = event.get("originalStartTime", {}) event_time = google_to_event_time(_start, _end) original_start = parse_google_time(_original) start_tz = _start.get("timeZone") last_modified = parse_datetime(event.get("updated")) description = event.get("description") location = event.get("location") busy = event.get("transparency") != "transparent" sequence = event.get("sequence", 0) # We're lucky because event statuses follow the icalendar # spec. event_status = event.get("status", "confirmed") assert event_status in EVENT_STATUSES # Ownership, read_only information creator = event.get("creator") if creator: owner = u"{} <{}>".format(creator.get("displayName", ""), creator.get("email", "")) else: owner = "" participants = [] attendees = event.get("attendees", []) for attendee in attendees: status = STATUS_MAP[attendee.get("responseStatus")] participants.append({ "email": attendee.get("email"), "name": attendee.get("displayName"), "status": status, "notes": attendee.get("comment"), }) organizer = event.get("organizer") is_owner = bool(organizer and organizer.get("self")) # FIXME @karim: The right thing here would be to use Google's ACL API. # There's some obscure cases, like an autoimported event which guests can # edit that can't be modified. read_only = True if not read_only_calendar: read_only = False # Recurring master or override info recurrence = event.get("recurrence") master_uid = event.get("recurringEventId") cancelled = event.get("status") == "cancelled" visibility = event.get("visibility") # Rewrite some values documented in # https://developers.google.com/calendar/v3/reference/events if visibility == "default": visibility = None elif visibility == "confidential": visibility = "private" return Event( uid=uid, raw_data=raw_data, title=title, description=description, location=location, busy=busy, start=event_time.start, end=event_time.end, all_day=event_time.all_day, owner=owner, is_owner=is_owner, read_only=read_only, participants=participants, recurrence=recurrence, last_modified=last_modified, original_start_tz=start_tz, original_start_time=original_start, master_event_uid=master_uid, cancelled=cancelled, status=event_status, sequence_number=sequence, source="local", visibility=visibility, )
def _parse_event(self, cal_info, event): """Constructs a Calendar object from a Google calendar entry. Parameters ---------- google_calendar: gdata.calendar.entry.CalendarEntry The Google calendar entry to parse. Returns ------- ..models.tables.base.Calendar A corresponding Inbox Calendar instance. Raises ------ AttributeError If the calendar data could not be parsed correctly. """ try: uid = str(event['id']) # The entirety of the raw event data in json representation. raw_data = str(event) # 'cancelled' events signify those instances within a series # that have been cancelled (for that given series). As such, # since full support for dealing with single instances within # a reocurring event series is not added, right now we just # treat this event as 'malformed'. -cg3 # TODO: Add support for reocurring events (see ways to handle # this generically across providers) if 'status' in event and event['status'] == 'cancelled': raise MalformedEventError() subject = event.get('summary', '')[:SUBJECT_MAX_LEN] body = event.get('description', None) location = event.get('location', None) if location: location = location[:LOCATION_MAX_LEN] all_day = False locked = True start = event['start'] end = event['end'] g_reccur = event.get('recurrence', None) recurrence = str(g_reccur) if g_reccur else None busy = event.get('transparency', True) if busy == 'transparent': busy = False reminders = [] if 'dateTime' in start: if event['reminders']['useDefault']: reminder_source = cal_info['defaultReminders'] elif 'overrides' in event['reminders']: reminder_source = event['reminders']['overrides'] else: reminder_source = None if reminder_source: for reminder in reminder_source: reminders.append(reminder['minutes']) start = parse_datetime(start['dateTime']) end = parse_datetime(end['dateTime']) else: start = date_parser.parse(start['date']) end = date_parser.parse(end['date']) all_day = True reminders = str(reminders) # Convert google's notion of status into our own participants = [] status_map = {'accepted': 'yes', 'needsAction': 'awaiting', 'declined': 'no', 'tentative': 'maybe'} for attendee in event.get('attendees', []): g_status = attendee.get('responseStatus') if g_status not in status_map: raise MalformedEventError() status = status_map[g_status] email = attendee.get('email') if not email: raise MalformedEventError() name = attendee.get('displayName') notes = None if 'additionalGuests' in attendee: notes = "Guests: {}".format(attendee['additionalGuests']) if 'comment' in attendee: notes += " Notes: {}".format(attendee['comment']) elif 'comment' in attendee: notes = "Notes: {}".format(attendee['comment']) participants.append(Participant(email_address=email, name=name, status=status, notes=notes)) if 'self' in event['creator']: locked = False elif 'guestsCanModify' in event: locked = False time_zone = cal_info['timeZone'] time_zone = 0 # FIXME: this ain't right -cg3 except (KeyError, AttributeError): raise MalformedEventError() return Event(account_id=self.account_id, uid=uid, provider_name=self.PROVIDER_NAME, raw_data=raw_data, subject=subject, body=body, location=location, reminders=reminders, recurrence=recurrence, start=start, end=end, busy=busy, all_day=all_day, locked=locked, time_zone=time_zone, source='remote', participants=participants)