from datetime import date, datetime from typing import Union, Iterator, Iterable, Callable from beautiful_date import BeautifulDate from dateutil.relativedelta import relativedelta from tzlocal import get_localzone_name from gcsa._services.base_service import BaseService from gcsa.event import Event from gcsa.serializers.event_serializer import EventSerializer from gcsa.util.date_time_util import to_localized_iso class SendUpdatesMode: """Possible values of the mode for sending updates or invitations to attendees. * ALL - Send updates to all participants. This is the default value. * EXTERNAL_ONLY - Send updates only to attendees not using google calendar. * NONE - Do not send updates. """ ALL = "all" EXTERNAL_ONLY = "externalOnly" NONE = "none" class EventsService(BaseService): """Event management methods of the `GoogleCalendar`""" def _list_events( self, request_method: Callable, time_min: Union[date, datetime, BeautifulDate], time_max: Union[date, datetime, BeautifulDate], timezone: str, calendar_id: str, **kwargs ) -> Iterable[Event]: """Lists paginated events received from request_method.""" time_min = time_min or datetime.now() time_max = time_max or time_min + relativedelta(years=1) time_min = to_localized_iso(time_min, timezone) time_max = to_localized_iso(time_max, timezone) yield from self._list_paginated( request_method, serializer_cls=EventSerializer, calendarId=calendar_id, timeMin=time_min, timeMax=time_max, **kwargs ) def get_events( self, time_min: Union[date, datetime, BeautifulDate] = None, time_max: Union[date, datetime, BeautifulDate] = None, order_by: str = None, timezone: str = get_localzone_name(), single_events: bool = False, query: str = None, calendar_id: str = None, **kwargs ) -> Iterable[Event]: """Lists events. :param time_min: Staring date/datetime :param time_max: Ending date/datetime :param order_by: Order of the events. Possible values: "startTime", "updated". Default is unspecified stable order. :param timezone: Timezone formatted as an IANA Time Zone Database name, e.g. "Europe/Zurich". By default, the computers local timezone is used if it is configured. UTC is used otherwise. :param single_events: Whether to expand recurring events into instances and only return single one-off events and instances of recurring events, but not the underlying recurring events themselves. :param query: Free text search terms to find events that match these terms in any field, except for extended properties. :param calendar_id: Calendar identifier. Default is `default_calendar` specified in `GoogleCalendar`. To retrieve calendar IDs call the :py:meth:`~gcsa.google_calendar.GoogleCalendar.get_calendar_list`. If you want to access the primary calendar of the currently logged-in user, use the "primary" keyword. :param kwargs: Additional API parameters. See https://developers.google.com/calendar/v3/reference/events/list#optional-parameters :return: Iterable of `Event` objects """ calendar_id = calendar_id or self.default_calendar if not single_events and order_by == 'startTime': raise ValueError( '"startTime" ordering is only available when querying single events, i.e. single_events=True' ) yield from self._list_events( self.service.events().list, time_min=time_min, time_max=time_max, timezone=timezone, calendar_id=calendar_id, **{ 'singleEvents': single_events, 'orderBy': order_by, 'q': query, **kwargs } ) def get_instances( self, recurring_event: Union[Event, str], time_min: Union[date, datetime, BeautifulDate] = None, time_max: Union[date, datetime, BeautifulDate] = None, timezone: str = get_localzone_name(), calendar_id: str = None, **kwargs ) -> Iterable[Event]: """Lists instances of recurring event :param recurring_event: Recurring event or instance of recurring event (`Event` object) or id of the recurring event :param time_min: Staring date/datetime :param time_max: Ending date/datetime :param timezone: Timezone formatted as an IANA Time Zone Database name, e.g. "Europe/Zurich". By default, the computers local timezone is used if it is configured. UTC is used otherwise. :param calendar_id: Calendar identifier. Default is `default_calendar` specified in `GoogleCalendar`. To retrieve calendar IDs call the :py:meth:`~gcsa.google_calendar.GoogleCalendar.get_calendar_list`. If you want to access the primary calendar of the currently logged-in user, use the "primary" keyword. :param kwargs: Additional API parameters. See https://developers.google.com/calendar/v3/reference/events/instances#optional-parameters :return: Iterable of event objects """ calendar_id = calendar_id or self.default_calendar try: event_id = self._get_resource_id(recurring_event) except ValueError: raise ValueError("Recurring event has to have id to retrieve its instances.") yield from self._list_events( self.service.events().instances, time_min=time_min, time_max=time_max, timezone=timezone, calendar_id=calendar_id, **{ 'eventId': event_id, **kwargs } ) def __iter__(self) -> Iterator[Event]: return iter(self.get_events()) def __getitem__(self, r): if isinstance(r, slice): time_min, time_max, order_by = r.start or None, r.stop or None, r.step or None elif isinstance(r, (date, datetime)): time_min, time_max, order_by = r, None, None else: raise NotImplementedError if ( (time_min and not isinstance(time_min, (date, datetime))) or (time_max and not isinstance(time_max, (date, datetime))) or (order_by and (not isinstance(order_by, str) or order_by not in self._LIST_ORDERS)) ): raise ValueError('Calendar indexing is in the following format: time_min[:time_max[:order_by]],' ' where time_min and time_max are date/datetime objects' ' and order_by is None or one of "startTime" or "updated" strings.') return self.get_events(time_min, time_max, order_by=order_by, single_events=(order_by == "startTime")) def get_event( self, event_id: str, calendar_id: str = None, **kwargs ) -> Event: """Returns the event with the corresponding event_id. :param event_id: The unique event ID. :param kwargs: Additional API parameters. See https://developers.google.com/calendar/v3/reference/events/get#optional-parameters :param calendar_id: Calendar identifier. Default is `default_calendar` specified in `GoogleCalendar`. To retrieve calendar IDs call the :py:meth:`~gcsa.google_calendar.GoogleCalendar.get_calendar_list`. If you want to access the primary calendar of the currently logged-in user, use the "primary" keyword. :return: The corresponding event object. """ calendar_id = calendar_id or self.default_calendar event_resource = self.service.events().get( calendarId=calendar_id, eventId=event_id, **kwargs ).execute() return EventSerializer.to_object(event_resource) def add_event( self, event: Event, send_updates: str = SendUpdatesMode.NONE, calendar_id: str = None, **kwargs ) -> Event: """Creates event in the calendar :param event: Event object. :param send_updates: Whether and how to send updates to attendees. See :py:class:`~gcsa.google_calendar.SendUpdatesMode` Default is "NONE". :param calendar_id: Calendar identifier. Default is `default_calendar` specified in `GoogleCalendar`. To retrieve calendar IDs call the :py:meth:`~gcsa.google_calendar.GoogleCalendar.get_calendar_list`. If you want to access the primary calendar of the currently logged-in user, use the "primary" keyword. :param kwargs: Additional API parameters. See https://developers.google.com/calendar/v3/reference/events/insert#optional-parameters :return: Created event object with id. """ calendar_id = calendar_id or self.default_calendar body = EventSerializer.to_json(event) event_json = self.service.events().insert( calendarId=calendar_id, body=body, conferenceDataVersion=1, sendUpdates=send_updates, **kwargs ).execute() return EventSerializer.to_object(event_json) def add_quick_event( self, event_string: str, send_updates: str = SendUpdatesMode.NONE, calendar_id: str = None, **kwargs ) -> Event: """Creates event in the calendar by string description. Example: Appointment at Somewhere on June 3rd 10am-10:25am :param event_string: String that describes an event :param send_updates: Whether and how to send updates to attendees. See :py:class:`~gcsa.google_calendar.SendUpdatesMode` Default is "NONE". :param calendar_id: Calendar identifier. Default is `default_calendar` specified in `GoogleCalendar`. To retrieve calendar IDs call the :py:meth:`~gcsa.google_calendar.GoogleCalendar.get_calendar_list`. If you want to access the primary calendar of the currently logged-in user, use the "primary" keyword. :param kwargs: Additional API parameters. See https://developers.google.com/calendar/v3/reference/events/quickAdd#optional-parameters :return: Created event object with id. """ calendar_id = calendar_id or self.default_calendar event_json = self.service.events().quickAdd( calendarId=calendar_id, text=event_string, sendUpdates=send_updates, **kwargs ).execute() return EventSerializer.to_object(event_json) def update_event( self, event: Event, send_updates: str = SendUpdatesMode.NONE, calendar_id: str = None, **kwargs ) -> Event: """Updates existing event in the calendar :param event: Event object with set `event_id`. :param send_updates: Whether and how to send updates to attendees. See :py:class:`~gcsa.google_calendar.SendUpdatesMode` Default is "NONE". :param calendar_id: Calendar identifier. Default is `default_calendar` specified in `GoogleCalendar`. To retrieve calendar IDs call the :py:meth:`~gcsa.google_calendar.GoogleCalendar.get_calendar_list`. If you want to access the primary calendar of the currently logged-in user, use the "primary" keyword. :param kwargs: Additional API parameters. See https://developers.google.com/calendar/v3/reference/events/update#optional-parameters :return: Updated event object. """ calendar_id = calendar_id or self.default_calendar event_id = self._get_resource_id(event) body = EventSerializer.to_json(event) event_json = self.service.events().update( calendarId=calendar_id, eventId=event_id, body=body, conferenceDataVersion=1, sendUpdates=send_updates, **kwargs ).execute() return EventSerializer.to_object(event_json) def import_event( self, event: Event, calendar_id: str = None, **kwargs ) -> Event: """Imports an event in the calendar This operation is used to add a private copy of an existing event to a calendar. :param event: Event object. :param calendar_id: Calendar identifier. Default is `default_calendar` specified in `GoogleCalendar`. To retrieve calendar IDs call the :py:meth:`~gcsa.google_calendar.GoogleCalendar.get_calendar_list`. If you want to access the primary calendar of the currently logged-in user, use the "primary" keyword. :param kwargs: Additional API parameters. See https://developers.google.com/calendar/v3/reference/events/import#optional-parameters :return: Created event object with id. """ calendar_id = calendar_id or self.default_calendar body = EventSerializer.to_json(event) event_json = self.service.events().import_( calendarId=calendar_id, body=body, conferenceDataVersion=1, **kwargs ).execute() return EventSerializer.to_object(event_json) def move_event( self, event: Event, destination_calendar_id: str, send_updates: str = SendUpdatesMode.NONE, source_calendar_id: str = None, **kwargs ) -> Event: """Moves existing event from calendar to another calendar :param event: Event object with set event_id. :param destination_calendar_id: ID of the destination calendar. :param send_updates: Whether and how to send updates to attendees. See :py:class:`~gcsa.google_calendar.SendUpdatesMode` Default is "NONE". :param source_calendar_id: Calendar identifier. Default is `default_calendar` specified in `GoogleCalendar`. To retrieve calendar IDs call the :py:meth:`~gcsa.google_calendar.GoogleCalendar.get_calendar_list`. If you want to access the primary calendar of the currently logged-in user, use the "primary" keyword. :param kwargs: Additional API parameters. See https://developers.google.com/calendar/v3/reference/events/move#optional-parameters :return: Moved event object. """ source_calendar_id = source_calendar_id or self.default_calendar event_id = self._get_resource_id(event) moved_event_json = self.service.events().move( calendarId=source_calendar_id, eventId=event_id, destination=destination_calendar_id, sendUpdates=send_updates, **kwargs ).execute() return EventSerializer.to_object(moved_event_json) def delete_event( self, event: Union[Event, str], send_updates: str = SendUpdatesMode.NONE, calendar_id: str = None, **kwargs ): """Deletes an event. :param event: Event's ID or `Event` object with set `event_id`. :param send_updates: Whether and how to send updates to attendees. See :py:class:`~gcsa.google_calendar.SendUpdatesMode` Default is "NONE". :param calendar_id: Calendar identifier. Default is `default_calendar` specified in `GoogleCalendar`. To retrieve calendar IDs call the :py:meth:`~gcsa.google_calendar.GoogleCalendar.get_calendar_list`. If you want to access the primary calendar of the currently logged-in user, use the "primary" keyword. :param kwargs: Additional API parameters. See https://developers.google.com/calendar/v3/reference/events/delete#optional-parameters """ calendar_id = calendar_id or self.default_calendar event_id = self._get_resource_id(event) self.service.events().delete( calendarId=calendar_id, eventId=event_id, sendUpdates=send_updates, **kwargs ).execute()