You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

248 lines
7.7 KiB

  1. from dateutil import rrule, tz
  2. from oauth2client import tools
  3. from dateutil.parser import parse as date_parse
  4. import re
  5. import tzlocal
  6. import argparse
  7. import datetime
  8. if __name__ == "__main__":
  9. from api import API, HttpError
  10. else:
  11. from gapi.api import API, HttpError
  12. flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
  13. APPLICATION_NAME = 'Google Calendar API Python'
  14. SUN = 0
  15. MON = 1
  16. TUE = 2
  17. WED = 3
  18. THU = 4
  19. FRI = 5
  20. SAT = 6
  21. def to_dateTime(datetime):
  22. """converts a datetime into json format for rest api"""
  23. if not datetime.tzinfo:
  24. datetime = datetime.astimezone()
  25. zone = tzlocal.get_localzone().zone
  26. datetime = datetime.isoformat(timespec='seconds')
  27. return {
  28. "timeZone": zone,
  29. "dateTime": datetime,
  30. }
  31. def from_dateTime(dateTime):
  32. """converts to a datetime from json format returned by rest api"""
  33. timezone = tz.gettz(dateTime['timeZone'])
  34. datetime = date_parse(dateTime['dateTime'])
  35. datetime.replace(tzinfo=timezone)
  36. return datetime
  37. class calendar_api(API):
  38. def __init__(self,
  39. app_name,
  40. client_secret_file,
  41. credentials_dir,
  42. scopes='https://www.googleapis.com/auth/calendar',
  43. version='v3',
  44. ):
  45. super().__init__('calendar', scopes, app_name,
  46. client_secret_file, credentials_dir, version)
  47. self.calendars = self.get_calendars()
  48. self.ids = dict((calendar['summary'].lower(), calendar['id'])
  49. for calendar in self.calendars)
  50. def create_event(self, calendar_id, body):
  51. try:
  52. calendar_id = self.ids[calendar_id]
  53. except KeyError:
  54. pass
  55. service = self.service
  56. event_service = service.events()
  57. event_service.insert(calendarId=calendar_id, body=body).execute()
  58. return event['id']
  59. def update_event(self, calendar_id, event_id, body):
  60. try:
  61. calendar_id = self.ids[calendar_id]
  62. except KeyError:
  63. pass
  64. service = self.service
  65. event_service = service.events()
  66. try:
  67. event = event_service.get(
  68. calendarId=calendar_id, eventId=event_id).execute()
  69. except HttpError as e:
  70. if e.resp.status == 404:
  71. return self.create_event(calendar_id, body)
  72. updated_event = service.events().update(
  73. calendarId=calendar_id, eventId=event['id'], body=body).execute()
  74. return updated_event["id"]
  75. def get_calendars(self):
  76. page_token = None
  77. cl = []
  78. first = True
  79. while page_token or first:
  80. first = False
  81. calendar_list_service = self.service.calendarList()
  82. calendar_list = calendar_list_service.list(
  83. pageToken=page_token).execute()
  84. cl += list(
  85. calendar_entry for calendar_entry in calendar_list['items'])
  86. page_token = calendar_list.get('nextPageToken')
  87. return cl
  88. def get_events(self, cal_id):
  89. service = self.service
  90. try:
  91. cal_id = self.ids[cal_id]
  92. except KeyError:
  93. pass
  94. page_token = None
  95. ret = []
  96. while True:
  97. event_service = service.events()
  98. events = event_service.list(
  99. calendarId=cal_id, pageToken=page_token).execute()
  100. ret += events['items']
  101. page_token = events.get('nextPageToken')
  102. if not page_token:
  103. break
  104. return ret
  105. def get_event_by_id(self, cal_id, event_id):
  106. """Retrieves event from cal_id and event_id"""
  107. service = self.service
  108. event_service = service.events()
  109. return event_service.get(cal_id, event_id).execute()
  110. class event:
  111. """Model for a calendar event that can be uploaded"""
  112. def __init__(self, api,
  113. start,
  114. end,
  115. name,
  116. description=None,
  117. recurrence=None,
  118. location=None,
  119. id=None
  120. ):
  121. self.start = start
  122. self.end = end
  123. self.summary = name
  124. self.description = description
  125. self.location = location
  126. self.id = id
  127. self.reminders = {'useDefault': True}
  128. def add_reminder(self, until, method='popup'):
  129. self.reminders['useDefault'] = False
  130. if isinstance(until, datetime.timedelta):
  131. minutes_until = until.days * 24 * 60
  132. minutes_until += until.seconds//60
  133. else:
  134. minutes_until = until
  135. self.reminders.setdefault('overrides', []).append({
  136. 'minuutes': minutes_until,
  137. 'method': method,
  138. })
  139. def add_weekly_recurrence(self, until: datetime.datetime, *days):
  140. ret = rrule.rrule(freq=rrule.WEEKLY, dtstart=self.start,
  141. wkst=SUN, until=until, byweekday=days)
  142. ret_str = str(ret).split('\n')[-1]
  143. ret_str = re.sub(r'(UNTIL=[^;]+)', r'\1Z', ret_str)
  144. try:
  145. self.recurrence
  146. except AttributeError:
  147. self.recurrence = []
  148. self.recurrence.append(ret_str)
  149. def to_json(self):
  150. keys = ('attendees', 'description', 'location',
  151. 'recurrence', 'reminders', 'summary')
  152. ret = {
  153. 'start': to_dateTime(self.start),
  154. 'end': to_dateTime(self.end),
  155. }
  156. for key in keys:
  157. try:
  158. value = self.__getattribute__(key)
  159. if value:
  160. ret[key] = value
  161. except AttributeError:
  162. pass
  163. return ret
  164. def add_attendees(self):
  165. pass
  166. @classmethod
  167. def from_json(cls, body):
  168. args = {}
  169. args['name'] = body.get('summary', 'unnamed')
  170. args['start'] = from_dateTime(body['start'])
  171. args['end'] = from_dateTime(body['end'])
  172. instance = cls(**args)
  173. keys = ('attendees', 'description', 'location',
  174. 'recurrence', 'reminders', 'summary')
  175. for key in keys:
  176. try:
  177. args[key] = body[key]
  178. except KeyError:
  179. pass
  180. return instance
  181. @classmethod
  182. def from_id(cls, api: calendar_api, calendar_id, event_id):
  183. """creates an event model from specified calendar and event_id"""
  184. event = api.get_event_by_id(calendar_id, event_id)
  185. return cls.from_json(event)
  186. def upload(self, api: calendar_api, calendar_id):
  187. """Upload an event to calendar.
  188. Either modifies an event in place or creates a new event."""
  189. if self.id is not None:
  190. api.update_event(calendar_id, self.id, self.to_json)
  191. else:
  192. api.create_event(calendar_id, self.to_json)
  193. if __name__ == "__main__":
  194. # ~~BODY EXAMPLE~~##~~BODY EXAMPLE~~
  195. example = {
  196. 'attendees': [{'email': 'lpage@example.com'},
  197. {'email': 'sbrin@example.com'}],
  198. 'description': "A chance to hear more about Google's\
  199. developer products.",
  200. 'end': {'dateTime': '2015-05-28T17:00:00-07:00',
  201. 'timeZone': 'America/Los_Angeles'},
  202. 'location': '800 Howard St., San Francisco, CA 94103',
  203. 'recurrence': ['RRULE:FREQ=DAILY;COUNT=2'],
  204. 'reminders': {'overrides': [
  205. {
  206. 'method': 'email',
  207. 'minutes': 1440
  208. },
  209. {
  210. 'method': 'popup',
  211. 'minutes': 10
  212. }
  213. ],
  214. 'useDefault': False},
  215. 'start': {'dateTime': '2015-05-28T09:00:00-07:00',
  216. 'timeZone': 'America/Los_Angeles'},
  217. 'summary': 'Google I/O 2015'
  218. }
  219. e = event.from_json(example)