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.

274 lines
8.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 = 6
  15. MON = 0
  16. TUE = 1
  17. WED = 2
  18. THU = 3
  19. FRI = 4
  20. SAT = 5
  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.recurrence = recurrence
  131. self.id = id
  132. if reminders is None:
  133. self.reminders = {'useDefault': True}
  134. else:
  135. self.reminders = reminders
  136. def add_reminder(self, until, method='popup'):
  137. """Add a reminder minutes before an event.
  138. Use either a notification (popup) or email"""
  139. assert method in ("email", "popup")
  140. self.reminders['useDefault'] = False
  141. if isinstance(until, datetime.timedelta):
  142. minutes_until = until.days * 24 * 60
  143. minutes_until += until.seconds//60
  144. else:
  145. minutes_until = until
  146. self.reminders.setdefault('overrides', []).append({
  147. 'method': method,
  148. 'minutes': minutes_until,
  149. })
  150. def add_weekly_recurrence(self, until: datetime.datetime, *days):
  151. if not until.tzinfo:
  152. until = until.astimezone()
  153. ret = rrule.rrule(freq=rrule.WEEKLY, dtstart=self.start,
  154. wkst=SUN, until=until, byweekday=days)
  155. ret_str = str(ret).split('\n')[-1]
  156. ret_str = re.sub(r'(UNTIL=[^;]+)', r'\1Z', ret_str)
  157. if self.recurrence is None:
  158. self.recurrence = []
  159. self.recurrence.append(ret_str)
  160. def to_json(self):
  161. keys = ('attendees', 'description', 'location',
  162. 'recurrence', 'reminders')
  163. ret = {
  164. 'summary': self.name,
  165. 'start': to_dateTime(self.start),
  166. 'end': to_dateTime(self.end),
  167. }
  168. for key in keys:
  169. try:
  170. value = self.__getattribute__(key)
  171. if value:
  172. ret[key] = value
  173. except AttributeError:
  174. pass
  175. return ret
  176. def add_attendees(self):
  177. pass
  178. @classmethod
  179. def from_json(cls, body):
  180. args = {}
  181. args['name'] = body.get('summary', 'unnamed')
  182. args['start'] = from_dateTime(body['start'])
  183. args['end'] = from_dateTime(body['end'])
  184. keys = ('attendees', 'description', 'location',
  185. 'recurrence', 'reminders', 'id')
  186. for key in keys:
  187. try:
  188. args[key] = body[key]
  189. except KeyError:
  190. pass
  191. instance = cls(**args)
  192. return instance
  193. @classmethod
  194. def from_id(cls, api: calendar_api, calendar_id, event_id):
  195. """creates an event model from specified calendar and event_id"""
  196. event = api.get_event_by_id(calendar_id, event_id)
  197. return cls.from_json(event)
  198. def upload(self, api: calendar_api, calendar_id):
  199. """Upload an event to calendar.
  200. Either modifies an event in place or creates a new event."""
  201. if self.id is not None:
  202. event_id = api.update_event(calendar_id, self.id, self.to_json())
  203. else:
  204. event_id = api.create_event(calendar_id, self.to_json())
  205. return event_id
  206. if __name__ == "__main__":
  207. import os
  208. # ~~BODY EXAMPLE~~##~~BODY EXAMPLE~~
  209. example = {
  210. 'attendees': [{'email': 'lpage@example.com'},
  211. {'email': 'sbrin@example.com'}],
  212. 'description': "A chance to hear more about Google's\
  213. developer products.",
  214. 'end': {'dateTime': '2015-05-28T17:00:00-07:00',
  215. 'timeZone': 'America/Los_Angeles'},
  216. 'location': '800 Howard St., San Francisco, CA 94103',
  217. 'recurrence': ['RRULE:FREQ=DAILY;COUNT=2'],
  218. 'reminders': {'overrides': [
  219. {
  220. 'method': 'email',
  221. 'minutes': 1440
  222. },
  223. {
  224. 'method': 'popup',
  225. 'minutes': 10
  226. }
  227. ],
  228. 'useDefault': False},
  229. 'start': {'dateTime': '2015-05-28T09:00:00-07:00',
  230. 'timeZone': 'America/Los_Angeles'},
  231. 'summary': 'Google I/O 2015'
  232. }
  233. e = Event(
  234. date_parse('march 16, 2019 10:00 am'),
  235. date_parse('march 16, 2019 3:30 pm'),
  236. 'Hang out with Matt'
  237. )
  238. path = r"C:\Users\Raphael\Documents\local_repo\cred"
  239. my_api = my_api = calendar_api('python', os.path.join(
  240. path, 'client_secret.json', path), path)
  241. cal_id = 'raphael.roberts48@gmail.com'
  242. e2 = Event.from_id(my_api, cal_id, 'qmrsd88ma8ko67ri98d8pbhd7s')
  243. until = datetime.datetime.today() + datetime.timedelta(days=20)
  244. e2.add_weekly_recurrence(until, MON, TUE)
  245. # e2.upload(my_api, cal_id)