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.

271 lines
8.6 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: 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 = event_service.insert(
  58. calendarId=calendar_id, body=body).execute()
  59. return event['id']
  60. def update_event(self, calendar_id, event_id, body):
  61. try:
  62. calendar_id = self.ids[calendar_id]
  63. except KeyError:
  64. pass
  65. service = self.service
  66. event_service = service.events()
  67. try:
  68. event = event_service.get(
  69. calendarId=calendar_id, eventId=event_id).execute()
  70. except HttpError as e:
  71. if e.resp.status == 404:
  72. return self.create_event(calendar_id, body)
  73. updated_event = service.events().update(
  74. calendarId=calendar_id, eventId=event['id'], body=body).execute()
  75. return updated_event["id"]
  76. def get_calendars(self):
  77. page_token = None
  78. cl = []
  79. first = True
  80. while page_token or first:
  81. first = False
  82. calendar_list_service = self.service.calendarList()
  83. calendar_list = calendar_list_service.list(
  84. pageToken=page_token).execute()
  85. cl += list(
  86. calendar_entry for calendar_entry in calendar_list['items'])
  87. page_token = calendar_list.get('nextPageToken')
  88. return cl
  89. def get_events(self, cal_id):
  90. service = self.service
  91. try:
  92. cal_id = self.ids[cal_id]
  93. except KeyError:
  94. pass
  95. page_token = None
  96. ret = []
  97. while True:
  98. event_service = service.events()
  99. events = event_service.list(
  100. calendarId=cal_id, pageToken=page_token).execute()
  101. ret += events['items']
  102. page_token = events.get('nextPageToken')
  103. if not page_token:
  104. break
  105. return ret
  106. def get_event_by_id(self, cal_id, event_id):
  107. """Retrieves event from cal_id and event_id"""
  108. service = self.service
  109. event_service = service.events()
  110. return event_service.get(calendarId=cal_id, eventId=event_id).execute()
  111. class Event:
  112. """Model for a calendar event that can be uploaded"""
  113. def __init__(self,
  114. start,
  115. end,
  116. name,
  117. description=None,
  118. recurrence=None,
  119. reminders=None,
  120. attendees=None,
  121. location=None,
  122. id=None
  123. ):
  124. self.start = start
  125. self.attendees = attendees
  126. self.end = end
  127. self.name = name
  128. self.description = description
  129. self.location = location
  130. self.id = id
  131. if reminders is None:
  132. self.reminders = {'useDefault': True}
  133. else:
  134. self.reminders = reminders
  135. def add_reminder(self, until, method='popup'):
  136. """Add a reminder minutes before an event.
  137. Use either a notification (popup) or email"""
  138. assert method in ("email", "popup")
  139. self.reminders['useDefault'] = False
  140. if isinstance(until, datetime.timedelta):
  141. minutes_until = until.days * 24 * 60
  142. minutes_until += until.seconds//60
  143. else:
  144. minutes_until = until
  145. self.reminders.setdefault('overrides', []).append({
  146. 'method': method,
  147. 'minutes': minutes_until,
  148. })
  149. def add_weekly_recurrence(self, until: datetime.datetime, *days):
  150. ret = rrule.rrule(freq=rrule.WEEKLY, dtstart=self.start,
  151. wkst=SUN, until=until, byweekday=days)
  152. ret_str = str(ret).split('\n')[-1]
  153. ret_str = re.sub(r'(UNTIL=[^;]+)', r'\1Z', ret_str)
  154. try:
  155. self.recurrence
  156. except AttributeError:
  157. self.recurrence = []
  158. self.recurrence.append(ret_str)
  159. def to_json(self):
  160. keys = ('attendees', 'description', 'location',
  161. 'recurrence', 'reminders')
  162. ret = {
  163. 'summary': self.name,
  164. 'start': to_dateTime(self.start),
  165. 'end': to_dateTime(self.end),
  166. }
  167. for key in keys:
  168. try:
  169. value = self.__getattribute__(key)
  170. if value:
  171. ret[key] = value
  172. except AttributeError:
  173. pass
  174. return ret
  175. def add_attendees(self):
  176. pass
  177. @classmethod
  178. def from_json(cls, body):
  179. args = {}
  180. args['name'] = body.get('summary', 'unnamed')
  181. args['start'] = from_dateTime(body['start'])
  182. args['end'] = from_dateTime(body['end'])
  183. keys = ('attendees', 'description', 'location',
  184. 'recurrence', 'reminders', 'id')
  185. for key in keys:
  186. try:
  187. args[key] = body[key]
  188. except KeyError:
  189. pass
  190. instance = cls(**args)
  191. return instance
  192. @classmethod
  193. def from_id(cls, api: calendar_api, calendar_id, event_id):
  194. """creates an event model from specified calendar and event_id"""
  195. event = api.get_event_by_id(calendar_id, event_id)
  196. return cls.from_json(event)
  197. def upload(self, api: calendar_api, calendar_id):
  198. """Upload an event to calendar.
  199. Either modifies an event in place or creates a new event."""
  200. if self.id is not None:
  201. event_id = api.update_event(calendar_id, self.id, self.to_json())
  202. else:
  203. event_id = api.create_event(calendar_id, self.to_json())
  204. return event_id
  205. if __name__ == "__main__":
  206. import os
  207. # ~~BODY EXAMPLE~~##~~BODY EXAMPLE~~
  208. example = {
  209. 'attendees': [{'email': 'lpage@example.com'},
  210. {'email': 'sbrin@example.com'}],
  211. 'description': "A chance to hear more about Google's\
  212. developer products.",
  213. 'end': {'dateTime': '2015-05-28T17:00:00-07:00',
  214. 'timeZone': 'America/Los_Angeles'},
  215. 'location': '800 Howard St., San Francisco, CA 94103',
  216. 'recurrence': ['RRULE:FREQ=DAILY;COUNT=2'],
  217. 'reminders': {'overrides': [
  218. {
  219. 'method': 'email',
  220. 'minutes': 1440
  221. },
  222. {
  223. 'method': 'popup',
  224. 'minutes': 10
  225. }
  226. ],
  227. 'useDefault': False},
  228. 'start': {'dateTime': '2015-05-28T09:00:00-07:00',
  229. 'timeZone': 'America/Los_Angeles'},
  230. 'summary': 'Google I/O 2015'
  231. }
  232. e = Event(
  233. date_parse('march 16, 2019 10:00 am'),
  234. date_parse('march 16, 2019 3:30 pm'),
  235. 'Hang out with Matt'
  236. )
  237. path = r"C:\Users\Raphael\Documents\local_repo\cred"
  238. my_api = my_api = calendar_api('python', os.path.join(
  239. path, 'client_secret.json', path), path)
  240. cal_id = 'raphael.roberts48@gmail.com'
  241. e2 = Event.from_id(my_api, cal_id, 'qmrsd88ma8ko67ri98d8pbhd7s')
  242. # e2.upload(my_api, cal_id)