Пример #1
0
    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)
Пример #2
0
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)
Пример #3
0
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)
Пример #4
0
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)
Пример #5
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')
Пример #6
0
    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)
Пример #7
0
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)
Пример #9
0
    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)
Пример #10
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 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')
Пример #11
0
    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')
Пример #12
0
    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 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)
Пример #14
0
    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)
Пример #15
0
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,
    )
Пример #16
0
    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)