def read_free_busy(self, calendar_ids=(), from_date=None, to_date=None, last_modified=None, tzid=settings.DEFAULT_TIMEZONE_ID, include_managed=True, localized_times=False, automatic_pagination=True): """Read free/busy blocks for linked account (optionally for the specified calendars). :param tuple calendar_ids: Tuple or list of calendar ids to pass to cronofy. (Optional). :param datetime.date from_date: Start datetime (or ISO8601 string) for query. (Optional). :param datetime.date to_date: End datetime (or ISO8601 string) for query. (Optional). :param string tzid: Timezone ID for query. (Optional, default settings.DEFAULT_TIMEZONE_ID). Should match tzinfo on datetime objects. :param bool include_managed: Include pages created through the API. (Optional, default True) :param bool localized_times: Return time values for event start/end with localization information. This varies across providers. (Optional, default False). :param bool automatic_pagination: Automatically fetch next page when iterating through results (Optional, default True) :return: Wrapped results (Containing first page of free/busy blocks). :rtype: ``Pages`` """ results = self.request_handler.get(endpoint='free_busy', params={ 'tzid': tzid, 'calendar_ids[]': calendar_ids, 'from': format_event_time(from_date), 'to': format_event_time(to_date), 'include_managed': include_managed, 'localized_times': localized_times, }).json() return Pages(self.request_handler, results, 'free_busy', automatic_pagination)
def test_unsupported(): """Test format_event_time throws an exception when passed an unsupported type""" with pytest.raises(Exception) as exception_info: format_event_time(1) assert exception_info.value.message == 'Unsupported type: ``%s``.\nSupported types: ``<datetime.datetime>``, ``<datetime.date>``, ``<dict>``, or ``<str>``.' % repr( type(1)) assert exception_info.value.argument == 1
def upsert_event(self, calendar_id, event): data = event.copy() data['start'] = format_event_time(event['start']) data['end'] = format_event_time(event['end']) self.post("/v1/calendars/%s/events" % calendar_id, data) return self
def upsert_event(self, calendar_id, event): """Inserts or updates an event for the specified calendar. :param string calendar_id: ID of calendar to insert/update event into. :param dict event: Dictionary of event data to send to cronofy. """ event['start'] = format_event_time(event['start']) event['end'] = format_event_time(event['end']) self.request_handler.post( endpoint='calendars/%s/events' % calendar_id, data=event)
def read_events(self, calendar_ids=(), from_date=None, to_date=None, last_modified=None, tzid=settings.DEFAULT_TIMEZONE_ID, only_managed=False, include_managed=True, include_deleted=False, include_moved=False, include_geo=False, localized_times=False, automatic_pagination=True): """Read events for linked account (optionally for the specified calendars). :param tuple calendar_ids: Tuple or list of calendar ids to pass to cronofy. (Optional). :param datetime.date from_date: Start datetime (or ISO8601 string) for query. (Optional). :param datetime.date to_date: End datetime (or ISO8601 string) for query. (Optional). :param datetime.datetime last_modified: Return items modified on or after last_modified. Datetime or ISO8601 string. (Optional). :param string tzid: Timezone ID for query. (Optional, default settings.DEFAULT_TIMEZONE_ID). Should match tzinfo on datetime objects. :param bool only_managed: Only include events created through the API. (Optional, default False) :param bool include_managed: Include events created through the API. (Optional, default True) :param bool include_deleted: Include deleted events. (Optional, default False) :param bool include_moved: Include events that ever existed within the from_date/to_date time window. (Optional, default False) :param bool include_geo: Include any geo location information for events when available (Optional, default False) :param bool localized_times: Return time values for event start/end with localization information. This varies across providers. (Optional, default False). :param bool automatic_pagination: Autonatically fetch next page when iterating through results (Optional, default True) :return: Wrapped results (Containing first page of events). :rtype: ``Pages`` """ results = self.request_handler.get( endpoint='events', params={ 'tzid': tzid, 'calendar_ids[]': calendar_ids, 'from': format_event_time(from_date), 'to': format_event_time(to_date), 'last_modified': format_event_time(last_modified), 'only_managed': only_managed, 'include_managed': include_managed, 'include_deleted': include_deleted, 'include_moved': include_moved, 'include_geo': include_geo, 'localized_times': localized_times, }).json() return Pages(self.request_handler, results, 'events', automatic_pagination)
def get_authorization_from_code(self, code, redirect_uri=''): """Updates the authorization tokens from the user provided code. :param string code: Authorization code to pass to Cronofy. :param string redirect_uri: Optionally override redirect uri obtained from user_auth_link. (They must match however). :return: Dictionary containing auth tokens, expiration info, and response status. :rtype: ``dict`` """ response = self.request_handler.post( endpoint='oauth/token', omit_api_version=True, data={ 'grant_type': 'authorization_code', 'client_id': self.auth.client_id, 'client_secret': self.auth.client_secret, 'code': code, 'redirect_uri': redirect_uri if redirect_uri else self.auth.redirect_uri, }) data = response.json() token_expiration = (datetime.datetime.utcnow() + datetime.timedelta(seconds=data['expires_in'])) self.auth.update( token_expiration=token_expiration, access_token=data['access_token'], refresh_token=data['refresh_token'], ) return { 'access_token': self.auth.access_token, 'refresh_token': self.auth.refresh_token, 'token_expiration': format_event_time(self.auth.token_expiration), }
def refresh_authorization(self): """Refreshes the authorization tokens. :return: Dictionary containing auth tokens, expiration info, and response status. :rtype: ``dict`` """ response = self.request_handler.post( endpoint='oauth/token', omit_api_version=True, data={ 'grant_type': 'refresh_token', 'client_id': self.auth.client_id, 'client_secret': self.auth.client_secret, 'refresh_token': self.auth.refresh_token, } ) data = response.json() token_expiration = (datetime.datetime.utcnow() + datetime.timedelta(seconds=data['expires_in'])) self.auth.update( token_expiration=token_expiration, access_token=data['access_token'], refresh_token=data['refresh_token'], ) return { 'access_token': self.auth.access_token, 'refresh_token': self.auth.refresh_token, 'token_expiration': format_event_time(self.auth.token_expiration), }
def application_calendar(self, application_calendar_id): """Creates and Retrieves authorization for an application calendar :param string application_calendar_id: The Id for this application calendar :return: Dictionary containing auth tokens, expiration info, and response status. :rtype: ``dict`` """ response = self.request_handler.post( endpoint='application_calendar', data={ 'client_id': self.auth.client_id, 'client_secret': self.auth.client_secret, 'application_calendar_id': application_calendar_id, }) data = response.json() token_expiration = (datetime.datetime.utcnow() + datetime.timedelta(seconds=data['expires_in'])) self.auth.update( token_expiration=token_expiration, access_token=data['access_token'], refresh_token=data['refresh_token'], ) return { 'access_token': self.auth.access_token, 'refresh_token': self.auth.refresh_token, 'token_expiration': format_event_time(self.auth.token_expiration), 'sub': data.get('sub'), 'application_calendar_id': data.get('application_calendar_id') }
def test_iso8601_string_in_dict(): """Test format_event_time returns an ISO8601 formatted date string in a dict when passed a iso8601 string""" date = '2016-01-15' params = { 'time': date, 'tzid': 'Etc/UTC', } assert format_event_time(params) == { 'time': '2016-01-15', 'tzid': 'Etc/UTC' }
def test_date_nested_in_dict(): """Test format_event_time returns an ISO8601 formatted date string in a dict when passed a datetime.date object""" date = datetime.date(2016, 1, 15) params = { 'time': date, 'tzid': 'Etc/UTC', } assert format_event_time(params) == { 'time': '2016-01-15', 'tzid': 'Etc/UTC' }
def test_tz_aware_datetime_in_dict(): """Test format_event_time returns an ISO8601 formatted date string in a dict when passed a datetimee object""" date = datetime.datetime(2016, 1, 15, 14, 20, 15, tzinfo=pytz.timezone('EST')) params = { 'time': date, 'tzid': 'Etc/UTC', } assert format_event_time(params) == { 'time': '2016-01-15T19:20:15Z', 'tzid': 'Etc/UTC' }
def translate_available_periods(self, periods): for params in periods: for tp in ['start', 'end']: if params[tp]: params[tp] = format_event_time(params[tp])
def upsert_smart_invite(self, smart_invite_id, recipient, event, callback_url=None, organizer=None): """ Creates or updates smart invite. :param string smart_invite_id - A String uniquely identifying the event for your application (note: this is NOT an ID generated by Cronofy). :param string callback_url - The URL within your application you want Cronofy to send notifications to about user interactions with the Smart Invite. :param dict recipient - A Dict containing the intended recipient of the invite :email - A String for the email address you are going to send the Smart Invite to. :param dict event - A Dict describing the event with symbolized keys: :summary - A String to use as the summary, sometimes referred to as the name or title, of the event. :description - A String to use as the description, sometimes referred to as the notes or body, of the event. :start - The Time or Date the event starts. :end - The Time or Date the event ends. :url - The URL associated with the event. :location - A Dict describing the location of the event with keys (optional): :description - A String describing the location. :lat - A String of the location's latitude. :long - A String of the location's longitude. :reminders - An Array of Dicts describing the desired reminders for the event. Reminders should be specified in priority order as, for example, when the underlying provider only supports a single reminder then the first reminder will be used. :minutes - An Integer specifying the number of minutes before the start of the event that the reminder should occur. :transparency - The transparency state for the event (optional). Accepted values are "transparent" and "opaque". :color - The color of the event (optional). :param dict organizer - A Dict containing the organzier of the invite :name - A String for the name of the organizer. """ event['start'] = format_event_time(event['start']) event['end'] = format_event_time(event['end']) body = { 'smart_invite_id': smart_invite_id, 'event': event } if type(recipient) == dict: body['recipient'] = recipient elif type(recipient) == list: body['recipients'] = recipient if callback_url: body['callback_url'] = callback_url if organizer: body['organizer'] = organizer return self.request_handler.post('smart_invites', data=body, use_api_key=True).json()
def test_datetime(): """Test format_event_time returns an ISO8601 formatted datetime string when passed a datetime.date object, and throws an exception when tzinfo is not set.""" target_datetime = '2016-01-15T09:08:00' d = datetime.datetime.strptime(target_datetime, '%Y-%m-%dT%H:%M:%S') assert format_event_time(d) == ('%sZ' % target_datetime)
def test_tz_aware_datetime(): """Test format_event_time returns an ISO8601 formatted datetime string with UTC timezone when passed a datetime.date object that's set to another timezone.""" d = datetime.datetime(2016, 1, 15, 14, 20, 15, tzinfo=pytz.timezone('EST')) assert format_event_time(d) == '2016-01-15T19:20:15Z'
def test_none(): """Test format_event_time returns None when passed None""" assert format_event_time(None) is None
def test_iso8601_string(): """Test format_event_time returns a string when passed a string""" assert format_event_time('2016-01-15') == '2016-01-15'
def test_date(): """Test format_event_time returns an ISO8601 formatted date string when passed a datetime.date object""" assert format_event_time(datetime.date(2016, 1, 15)) == '2016-01-15'