def _periods(ps, timezone=str(get_localzone())): """Converts date period(s) to RDATE format. Period is defined as tuple of starting date/datetime and ending date/datetime or duration as Duration object: (date/datetime, date/datetime/Duration) :param ps: period or list of periods. :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. :return: RDATE string of periods. """ if not isinstance(ps, list): ps = [ps] period_strings = [] for start, end in ps: if not isinstance(start, (date, datetime)): msg = 'The start object(s) must be a date or datetime, not {!r}.'.format( end.__class__.__name__) raise TypeError(msg) start = insure_localisation(start, timezone) if isinstance(end, (date, datetime)): end = insure_localisation(end, timezone) pstr = '{}/{}'.format(start.strftime('%Y%m%dT%H%M%SZ'), end.strftime('%Y%m%dT%H%M%SZ')) elif isinstance(end, Duration): pstr = '{}/{}'.format(start.strftime('%Y%m%dT%H%M%SZ'), end) else: msg = 'The end object(s) must be a date, datetime or Duration, not {!r}.'.format( end.__class__.__name__) raise TypeError(msg) period_strings.append(pstr) return 'VALUE=PERIOD:' + ','.join(period_strings)
def test_to_object(self): event_json = { 'summary': 'Good day', 'description': 'Very good day indeed', 'location': 'Prague', 'start': {'dateTime': '2019-01-01T11:22:33', 'timeZone': TEST_TIMEZONE}, 'end': {'dateTime': '2019-01-01T12:22:33', 'timeZone': TEST_TIMEZONE}, 'recurrence': [ 'RRULE:FREQ=DAILY;WKST=SU', 'EXRULE:FREQ=DAILY;BYDAY=MO;WKST=SU', 'EXDATE:VALUE=DATE:20190419,20190422,20190512' ], 'visibility': 'public', 'reminders': { 'useDefault': False, 'overrides': [ {'method': 'popup', 'minutes': 30}, {'method': 'email', 'minutes': 120} ] }, 'attachments': [ { 'title': 'My file1', 'fileUrl': 'https://file.url1', 'mimeType': 'application/vnd.google-apps.document' }, { 'title': 'My file2', 'fileUrl': 'https://file.url2', 'mimeType': 'application/vnd.google-apps.document' } ] } event = EventSerializer.to_object(event_json) self.assertEqual(event.summary, 'Good day') self.assertEqual(event.start, insure_localisation((1 / Jan / 2019)[11:22:33], TEST_TIMEZONE)) self.assertEqual(event.end, insure_localisation((1 / Jan / 2019)[12:22:33], TEST_TIMEZONE)) self.assertEqual(event.description, 'Very good day indeed') self.assertEqual(event.location, 'Prague') self.assertEqual(len(event.recurrence), 3) self.assertEqual(event.visibility, Visibility.PUBLIC) self.assertIsInstance(event.reminders[0], PopupReminder) self.assertEqual(event.reminders[0].minutes_before_start, 30) self.assertIsInstance(event.reminders[1], EmailReminder) self.assertEqual(event.reminders[1].minutes_before_start, 120) self.assertEqual(len(event.attachments), 2) self.assertIsInstance(event.attachments[0], Attachment) self.assertEqual(event.attachments[0].title, 'My file1')
def get_events(self, time_min=None, time_max=None, order_by='startTime', timezone=str(get_localzone())): """ 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". :param timezone: timezone formatted as an IANA Time Zone Database name, e.g. "Europe/Zurich". By default, the computers configured local timezone(if any) is used. :return: """ time_min = time_min or datetime.utcnow() time_max = time_max or time_min + relativedelta(years=1) if not isinstance(time_min, datetime): time_min = datetime.combine(time_min, datetime.min.time()) if not isinstance(time_max, datetime): time_max = datetime.combine(time_max, datetime.max.time()) time_min = insure_localisation(time_min, timezone).isoformat() time_max = insure_localisation(time_max, timezone).isoformat() res = [] page_token = None while True: events = self.service.events().list( calendarId=self.calendar, timeMin=time_min, timeMax=time_max, orderBy=order_by, singleEvents=True, pageToken=page_token).execute() for event_json in events['items']: event = EventSerializer(event_json).get_object() res.append(event) page_token = events.get('nextPageToken') if not page_token: break return res
def test_init(self): event = Event( 'Breakfast', start=(1 / Feb / 2019)[9:00], end=(31 / Dec / 2019)[23:59], timezone=TEST_TIMEZONE, description='Everyday breakfast', location='Home', recurrence=[ Recurrence.rule(freq=DAILY), Recurrence.exclude_rule(by_week_day=[SU, SA]), Recurrence.exclude_dates([ 19 / Apr / 2019, 22 / Apr / 2019, 12 / May / 2019 ]) ], visibility=Visibility.PRIVATE, minutes_before_popup_reminder=15 ) self.assertEqual(event.summary, 'Breakfast') self.assertEqual(event.start, insure_localisation((1 / Feb / 2019)[9:00], TEST_TIMEZONE)) self.assertEqual(event.description, 'Everyday breakfast') self.assertEqual(event.location, 'Home') self.assertEqual(len(event.recurrence), 3) self.assertEqual(event.visibility, Visibility.PRIVATE) self.assertIsInstance(event.reminders[0], PopupReminder) self.assertEqual(event.reminders[0].minutes_before_start, 15)
def test_init_no_end(self): start = 1 / Jun / 2019 event = Event('Good day', start, timezone=TEST_TIMEZONE) self.assertEqual(event.end, start + 1 * days) start = insure_localisation((1 / Jul / 2019)[12:00], TEST_TIMEZONE) event = Event('Lunch', start, timezone=TEST_TIMEZONE) self.assertEqual(event.end, start + 1 * hours)
def _times(dts, timezone=str(get_localzone())): """Converts datetime(s) set to RDATE format. :param dts: datetime object or list of datetime objects :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. :return: RDATE string of datetimes with specified timezone. """ if not isinstance(dts, list): dts = [dts] localized_datetimes = [] for dt in dts: if not isinstance(dt, (date, datetime)): msg = 'The dts object(s) must be date or datetime, not {!r}.'.format( dt.__class__.__name__) raise TypeError(msg) localized_datetimes.append(insure_localisation(dt, timezone)) return 'TZID={}:{}'.format( timezone, ','.join(d.strftime('%Y%m%dT%H%M%S') for d in localized_datetimes))
def __init__(self, summary, start, end=None, timezone=str(get_localzone()), event_id=None, description=None, location=None, recurrence=None, color=None, visibility=Visibility.DEFAULT, gadget=None, attachments=None, reminders=None, default_reminders=False, minutes_before_popup_reminder=None, minutes_before_email_reminder=None, **other): """ :param summary: title of the event. :param start: starting date/datetime. :param end: ending date/datetime. If 'end' is not specified, event is considered as a 1-day or 1-hour event if 'start' is date or datetime respectively. :param timezone: timezone formatted as an IANA Time Zone Database name, e.g. "Europe/Zurich". By default, the computers configured local timezone(if any) is used. :param event_id: opaque identifier of the event. By default is generated by the server. You can specify id as a 5-1024 long string of characters used in base32hex ([a-vA-V0-9]). The ID must be unique per calendar. :param description: description of the event. :param location: geographic location of the event as free-form text. :param recurrence: RRULE/RDATE/EXRULE/EXDATE string or list of such strings. See :py:mod:`~gcsa.recurrence` :param color: color id referring to an entry from colors endpoint (list_event_colors) :param visibility: visibility of the event. Default is default visibility for events on the calendar. :param gadget: a gadget that extends the event. See :py:class:`~gcsa.gadget.Gadget` :param attachments: attachment or list of attachments. See :py:class:`~gcsa.attachment.Attachment` :param reminders: reminder or list of reminder objects. See :py:mod:`~gcsa.reminders` :param default_reminders: whether the default reminders of the calendar apply to the event. :param minutes_before_popup_reminder: minutes before popup reminder or None if reminder is not needed. :param minutes_before_email_reminder: minutes before email reminder or None if reminder is not needed. :param other: Other fields that should be included in request json. Will be included as they are. """ def assure_list(obj): return [] if obj is None else obj if isinstance(obj, list) else [obj] self.timezone = timezone self.start = start if end: self.end = end elif isinstance(start, datetime): self.end = start + timedelta(hours=1) elif isinstance(start, date): self.end = start + timedelta(days=1) if isinstance(self.start, datetime) and isinstance(self.end, datetime): self.start = insure_localisation(self.start, timezone) self.end = insure_localisation(self.end, timezone) elif isinstance(self.start, datetime) or isinstance( self.end, datetime): raise TypeError( 'Start and end must either both be date or both be datetime.') reminders = assure_list(reminders) if len(reminders) > 5: raise ValueError('The maximum number of override reminders is 5.') if default_reminders and reminders: raise ValueError( 'Cannot specify both default reminders and overrides at the same time.' ) self.event_id = event_id and event_id.lower() self.summary = summary self.description = description self.location = location self.recurrence = assure_list(recurrence) self.color_id = color self.visibility = visibility self.gadget = gadget self.attachments = assure_list(attachments) self.reminders = reminders self.default_reminders = default_reminders self.other = other if minutes_before_popup_reminder is not None: self.add_popup_reminder(minutes_before_popup_reminder) if minutes_before_email_reminder is not None: self.add_email_reminder(minutes_before_email_reminder)
def test_to_object(self): event_json = { 'summary': 'Good day', 'description': 'Very good day indeed', 'location': 'Prague', 'start': { 'dateTime': '2019-01-01T11:22:33', 'timeZone': TEST_TIMEZONE }, 'end': { 'dateTime': '2019-01-01T12:22:33', 'timeZone': TEST_TIMEZONE }, 'recurrence': [ 'RRULE:FREQ=DAILY;WKST=SU', 'EXRULE:FREQ=DAILY;BYDAY=MO;WKST=SU', 'EXDATE:VALUE=DATE:20190419,20190422,20190512' ], 'visibility': 'public', 'attendees': [ { 'email': '*****@*****.**', 'responseStatus': ResponseStatus.NEEDS_ACTION }, { 'email': '*****@*****.**', 'responseStatus': ResponseStatus.ACCEPTED }, ], 'reminders': { 'useDefault': False, 'overrides': [{ 'method': 'popup', 'minutes': 30 }, { 'method': 'email', 'minutes': 120 }] }, 'attachments': [{ 'title': 'My file1', 'fileUrl': 'https://file.url1', 'mimeType': 'application/vnd.google-apps.document' }, { 'title': 'My file2', 'fileUrl': 'https://file.url2', 'mimeType': 'application/vnd.google-apps.document' }] } serializer = EventSerializer(event_json) event = serializer.get_object() self.assertEqual(event.summary, 'Good day') self.assertEqual( event.start, insure_localisation((1 / Jan / 2019)[11:22:33], TEST_TIMEZONE)) self.assertEqual( event.end, insure_localisation((1 / Jan / 2019)[12:22:33], TEST_TIMEZONE)) self.assertEqual(event.description, 'Very good day indeed') self.assertEqual(event.location, 'Prague') self.assertEqual(len(event.recurrence), 3) self.assertEqual(event.visibility, Visibility.PUBLIC) self.assertEqual(len(event.attendees), 2) self.assertIsInstance(event.reminders[0], PopupReminder) self.assertEqual(event.reminders[0].minutes_before_start, 30) self.assertIsInstance(event.reminders[1], EmailReminder) self.assertEqual(event.reminders[1].minutes_before_start, 120) self.assertEqual(len(event.attachments), 2) self.assertIsInstance(event.attachments[0], Attachment) self.assertEqual(event.attachments[0].title, 'My file1') event_json_str = """{ "summary": "Good day", "description": "Very good day indeed", "location": "Prague", "start": {"date": "2020-07-20"}, "end": {"date": "2020-07-22"} }""" event = EventSerializer.to_object(event_json_str) self.assertEqual(event.summary, 'Good day') self.assertEqual(event.description, 'Very good day indeed') self.assertEqual(event.location, 'Prague') self.assertEqual(event.start, 20 / Jul / 2020) self.assertEqual(event.end, 22 / Jul / 2020)