def create_event(self, event):
        """Creates the event in Google Calendar.

        :param event: The event to create.
        :type event: :class:`Event`

        :raises: :class:`EventumError.GCalAPI.BadStatusLine`,
            :class:`EventumError.GCalAPI.NotFound`

        :returns: The Google Calendar API response.
        :rtype: dict
        """
        self.before_request()

        resource = None
        resource = GoogleCalendarResourceBuilder.event_resource(event)

        calendar_id = self._calendar_id_for_event(event)

        current_app.logger.info('[GOOGLE_CALENDAR]: Create Event')
        request = self.service.events().insert(calendarId=calendar_id,
                                               body=resource)

        # Execute the request
        created_event = self._execute_request(request)

        # Update the Event with the latest info from the response.
        self._update_event_from_response(event, created_event)

        # Return the Google Calendar response dict
        return created_event
    def _instance_resource_for_event_in_series(self, event):
        """Searches through the instances of ``event``'s parent series,
        returning the Google Calendar instance resource for with ``start_time``
        that matches ``event``'s.

        :param event: The event to find the instance resource for.
        :type event: :class:`Event`

        :returns: The instance resource that represents ``event``.
        :rtype: dict
        """
        calendar_id = self._calendar_id_for_event(event)
        event_start_date = (GoogleCalendarResourceBuilder.rfc3339(
            event.start_datetime))
        page_token = None
        while True:
            # Find more instances
            request = self.service.events().instances(calendarId=calendar_id,
                                                      eventId=event.gcal_id,
                                                      pageToken=page_token)
            instances = self._execute_request(request)

            # Look for instances with matching start date
            for instance in instances['items']:
                if instance['start']['dateTime'] == event_start_date:
                    return instance

            # Get the next page of events
            page_token = instances.get('nextPageToken')

            # Quit if there are no more pages
            if not page_token:
                break

        return None
    def create_event(self, event):
        """Creates the event in Google Calendar.

        :param event: The event to create.
        :type event: :class:`Event`

        :raises: :class:`EventumError.GCalAPI.BadStatusLine`,
            :class:`EventumError.GCalAPI.NotFound`

        :returns: The Google Calendar API response.
        :rtype: dict
        """
        self.before_request()

        resource = None
        resource = GoogleCalendarResourceBuilder.event_resource(event)

        calendar_id = self._calendar_id_for_event(event)

        current_app.logger.info('[GOOGLE_CALENDAR]: Create Event')
        request = self.service.events().insert(calendarId=calendar_id,
                                               body=resource)

        # Execute the request
        created_event = self._execute_request(request)

        # Update the Event with the latest info from the response.
        self._update_event_from_response(event, created_event)

        # Return the Google Calendar response dict
        return created_event
    def delete_event(self, event, as_exception=False):
        """Delete an event or series from Google Calendar, or cancel a single
        event from a series.

        :param event: The event to delete.
        :type event: :class:`Event`
        :param bool as_exception: Whether or not to cancel this event as an
            exception in a series.  Otherwise, series' will be deleted in their
            entirety.

        :raises: :class:`EventumError.GCalAPI.BadStatusLine`,
            :class:`EventumError.GCalAPI.NotFound`,
            :class:`EventumError.GCalAPI.MissingID`

        :returns: The Google Calendar API response.
        :rtype: dict
        """
        self.before_request()

        if not event.gcal_id:
            raise EventumError.GCalAPI.MissingID()

        calendar_id = self._calendar_id_for_event(event)

        # Create the request
        if as_exception:
            current_app.logger.info(
                '[GOOGLE_CALENDAR]: Delete Event (as exception)')
            resource = GoogleCalendarResourceBuilder.event_resource(event)
            instance = self._instance_resource_for_event_in_series(event)
            instance.update(resource)
            instance['status'] = u'cancelled'

            request = self.service.events().update(calendarId=calendar_id,
                                                   eventId=instance['id'],
                                                   body=instance)
        else:
            current_app.logger.info('[GOOGLE_CALENDAR]: Delete Event')
            request = self.service.events().delete(calendarId=calendar_id,
                                                   eventId=event.gcal_id)

        # Execute the request, failing silently if the event has already been
        # deleted from Google Calendar.
        try:
            return self._execute_request(request)
        except EventumError.GCalAPI.NotFound as e:
            # If the resource has already been deleted, fail quietly.
            raise EventumError.GCalAPI.EventAlreadyDeleted(e=e)
    def delete_event(self, event, as_exception=False):
        """Delete an event or series from Google Calendar, or cancel a single
        event from a series.

        :param event: The event to delete.
        :type event: :class:`Event`
        :param bool as_exception: Whether or not to cancel this event as an
            exception in a series.  Otherwise, series' will be deleted in their
            entirety.

        :raises: :class:`EventumError.GCalAPI.BadStatusLine`,
            :class:`EventumError.GCalAPI.NotFound`,
            :class:`EventumError.GCalAPI.MissingID`

        :returns: The Google Calendar API response.
        :rtype: dict
        """
        self.before_request()

        if not event.gcal_id:
            raise EventumError.GCalAPI.MissingID()

        calendar_id = self._calendar_id_for_event(event)

        # Create the request
        if as_exception:
            current_app.logger.info(
                '[GOOGLE_CALENDAR]: Delete Event (as exception)')
            resource = GoogleCalendarResourceBuilder.event_resource(event)
            instance = self._instance_resource_for_event_in_series(event)
            instance.update(resource)
            instance['status'] = u'cancelled'

            request = self.service.events().update(calendarId=calendar_id,
                                                   eventId=instance['id'],
                                                   body=instance)
        else:
            current_app.logger.info('[GOOGLE_CALENDAR]: Delete Event')
            request = self.service.events().delete(calendarId=calendar_id,
                                                   eventId=event.gcal_id)

        # Execute the request, failing silently if the event has already been
        # deleted from Google Calendar.
        try:
            return self._execute_request(request)
        except EventumError.GCalAPI.NotFound as e:
            # If the resource has already been deleted, fail quietly.
            raise EventumError.GCalAPI.EventAlreadyDeleted(e=e)
    def update_event(self, stale_event, as_exception=False):
        """Updates the event in Google Calendar.

        The first argument is called ``stale_event`` because it might have
        outdated fields.  The first thing we do is find a fresh event with it's
        id in mongo.

        This method will fall back to creating a new event if we don't have
        reference to a ``gcal_id`` for the event, or if the update otherwise
        fails.

        :param stale_event: The event to update.
        :type stale_event: :class:`Event`
        :param bool as_exception: Whether or not this update should happen as
            an exception in a series.  Otherwise, series' will be updated in
            their entirety.

        :raises: :class:`EventumError.GCalAPI.BadStatusLine`,
            :class:`EventumError.GCalAPI.NotFound`,
            :class:`EventumError.GCalAPI.MissingID`

        :returns: The Google Calendar API response.
        :rtype: dict
        """
        self.before_request()

        # Freshen up stale_event
        event = Event.objects().get(id=stale_event.id)

        if not event.gcal_id:
            # If we don't have a reference if it's associate Google Calendar
            # ID, then create it fresh.  This raises still because it
            # *shouldn't* ever happen, but it does.
            self.create_event(stale_event)
            raise EventumError.GCalAPI.MissingID.UpdateFellBackToCreate()

        resource = None
        resource = GoogleCalendarResourceBuilder.event_resource(
            event, for_update=True)

        calendar_id = self._calendar_id_for_event(event)

        # If this update should be an exception to a series of events, then
        # we only want to update the instance id.  Otherwise, using the
        # ``event.gcal_id`` will update the entire series.
        event_id_for_update = event.gcal_id
        if as_exception:
            instance = self._instance_resource_for_event_in_series(event)
            instance.update(resource)
            resource = instance
            event_id_for_update = instance['id']

        current_app.logger.info('[GOOGLE_CALENDAR]: Update Event')
        request = self.service.events().update(calendarId=calendar_id,
                                               eventId=event_id_for_update,
                                               body=resource)

        # Send the request, falling back to update if it fails.
        try:
            updated_event = self._execute_request(request)
        except EventumError.GCalAPI.NotFound as e:
            self.create_event(event)
            raise EventumError.GCalAPI.NotFound.UpdateFellBackToCreate(e=e)

        # Update the Event with the latest info from the response.
        self._update_event_from_response(event, updated_event)

        # Return the Google Calendar response dict
        return updated_event
    def update_event(self, stale_event, as_exception=False):
        """Updates the event in Google Calendar.

        The first argument is called ``stale_event`` because it might have
        outdated fields.  The first thing we do is find a fresh event with it's
        id in mongo.

        This method will fall back to creating a new event if we don't have
        reference to a ``gcal_id`` for the event, or if the update otherwise
        fails.

        :param stale_event: The event to update.
        :type stale_event: :class:`Event`
        :param bool as_exception: Whether or not this update should happen as
            an exception in a series.  Otherwise, series' will be updated in
            their entirety.

        :raises: :class:`EventumError.GCalAPI.BadStatusLine`,
            :class:`EventumError.GCalAPI.NotFound`,
            :class:`EventumError.GCalAPI.MissingID`

        :returns: The Google Calendar API response.
        :rtype: dict
        """
        self.before_request()

        # Freshen up stale_event
        event = Event.objects().get(id=stale_event.id)

        if not event.gcal_id:
            # If we don't have a reference if it's associate Google Calendar
            # ID, then create it fresh.  This raises still because it
            # *shouldn't* ever happen, but it does.
            self.create_event(stale_event)
            raise EventumError.GCalAPI.MissingID.UpdateFellBackToCreate()

        resource = None
        resource = GoogleCalendarResourceBuilder.event_resource(
            event, for_update=True)

        calendar_id = self._calendar_id_for_event(event)

        # If this update should be an exception to a series of events, then
        # we only want to update the instance id.  Otherwise, using the
        # ``event.gcal_id`` will update the entire series.
        event_id_for_update = event.gcal_id
        if as_exception:
            instance = self._instance_resource_for_event_in_series(event)
            instance.update(resource)
            resource = instance
            event_id_for_update = instance['id']

        current_app.logger.info('[GOOGLE_CALENDAR]: Update Event')
        request = self.service.events().update(calendarId=calendar_id,
                                               eventId=event_id_for_update,
                                               body=resource)

        # Send the request, falling back to update if it fails.
        try:
            updated_event = self._execute_request(request)
        except EventumError.GCalAPI.NotFound as e:
            self.create_event(event)
            raise EventumError.GCalAPI.NotFound.UpdateFellBackToCreate(e=e)

        # Update the Event with the latest info from the response.
        self._update_event_from_response(event, updated_event)

        # Return the Google Calendar response dict
        return updated_event