From f690b6ff2a115947739152b85e4c014200558395 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Nils=20Forss=C3=83=C3=A9n?= Date: Tue, 5 Dec 2023 02:24:55 +0100 Subject: [PATCH] Initial commit --- googleCalendar.py | 87 +++++++++++++++++++++++++++++++++ main.py | 119 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100755 googleCalendar.py create mode 100755 main.py diff --git a/googleCalendar.py b/googleCalendar.py new file mode 100755 index 0000000..e87cf64 --- /dev/null +++ b/googleCalendar.py @@ -0,0 +1,87 @@ +from __future__ import print_function +import pickle +import os.path +from googleapiclient.discovery import build +from google_auth_oauthlib.flow import InstalledAppFlow +from google.auth.transport.requests import Request + +""" +Resource to update Google Calendar using credentials.json in Working Directory. + +A token.pickle file will be created for permissions to Google Calendar. + +Author: Nils Forssén, Jämtland County, Sweden +""" + +EVENT_COLORIDS = { + "blue": 1, + "green": 2, + "purple": 3, + "red": 4, + "yellow": 5, + "orange": 6, + "turquoise": 7, + "gray": 8, + "b_blue": 9, + "b_green": 10, + "b_red": 11 +} + + +# Give accesss to complete Google Calendar +SCOPES = ["https://www.googleapis.com/auth/calendar.events"] + +def getCredentials(): + """ + Get the current credentials from the pickle file, + If not available, create new file with credentials + """ + + creds = None + if os.path.exists("token.pickle"): + with open("token.pickle", "rb") as token: + creds = pickle.load(token) + + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES) + creds = flow.run_local_server(port=0) + with open("token.pickle", "wb") as token: + pickle.dump(creds, token) + + return creds + + +service = build('calendar', 'v3', credentials=getCredentials()) + +def createEvent(event): + """ + Create google calendar event using the standard event formatting + """ + + event = service.events().insert(calendarId="primary", body=event).execute() + + return event + + +def deleteEvent(eventId): + """ + Delete event with given id from google calendar + """ + + service.events().delete(calendarId="primary", eventId=eventId).execute() + + +def listEvents(**kwargs): + """ + Return list of all google calendar events + """ + + events_result = service.events().list(calendarId="primary", singleEvents=True, orderBy='startTime', **kwargs).execute() + + events = events_result.get('items', []) + + return events diff --git a/main.py b/main.py new file mode 100755 index 0000000..2429f03 --- /dev/null +++ b/main.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 + +import requests +from bs4 import BeautifulSoup +import datetime +from dataclasses import dataclass +import re +import googleCalendar + +headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36" +} + +TIMEEDIT_URLS = ["https://cloud.timeedit.net/liu/web/schema/ri167XQQ618Z50Qm07060gZ6y6Y7509Q6Y95Y2.html"] + + +@dataclass +class event: + summary: str + description: str + start: datetime.datetime + end: datetime.datetime + t_offset: str = "+01:00" + + def get_gc_event(self): + event = { + "summary": self.summary, + "description": self.description, + "start": { + "dateTime": "{0}T{1}{2}".format( + self.start.date(), self.start.time(), self.t_offset + ) + }, + "end": { + "dateTime": "{0}T{1}{2}".format( + self.end.date(), self.end.time(), self.t_offset + ) + }, + "reminders": { + "useDefault": False, + "overrides": [], # Reminders would drive me crazy + }, + "colorId": googleCalendar.EVENT_COLORIDS["blue"], + } + + return event + + +def get_timeedit_events(session): + for schedule_URL in TIMEEDIT_URLS: + data = session.get( + schedule_URL, + headers=headers, + verify=False + ) + + soup = BeautifulSoup(data.content, features="lxml") + table = soup.find("table", attrs={"class": "restable"}) + rows = table.find_all("tr") + + schedule = [] + active_day = "" + for row in rows[2:]: + date = row.find("td", attrs={"class": "headline t"}) + if date is not None: + match = re.search(r"\d{4}-\d{2}-\d{2}", date.text.strip()) + active_day = match.group() + else: + try: + time = row.find("td", attrs={"class": "time tt c-1"}) + if time is not None: + info = [ + col.text.strip() + for col in row.find_all("td") + if "columnLine" in col["class"] + ] + summary = f"{info[0]} - {info[1]}, {info[2]}" + description = f"{info[0]}, {info[1]}, {info[2]}, {info[3]}, {info[5]}, {info[5]}\nautogen_nils\nCreated: {datetime.datetime.now()}" + active_time = time.text.strip() + start = datetime.datetime.strptime( + active_day + active_time[:5], "%Y-%m-%d%H:%M" + ) + end = datetime.datetime.strptime( + active_day + active_time[8:], "%Y-%m-%d%H:%M" + ) + schedule.append(event(summary, description, start, end)) + except Exception as e: + print(e) + else: + print(f"strange row: {row.content}") + return schedule + + +def reset_gc_events(schedule): + for c_event in googleCalendar.listEvents( + **{ + "timeMin": "{0}T{1}{2}".format( + schedule[0].start.date(), schedule[0].start.time(), schedule[0].t_offset + ) + } + ): + try: + if f"\nautogen_nils" in c_event["description"]: + googleCalendar.deleteEvent(c_event["id"]) + except KeyError: + pass + + for n_event in schedule: + googleCalendar.createEvent(n_event.get_gc_event()) + print(f"Created: {n_event}\n") + + +if __name__ == "__main__": + + with requests.session() as session: + + timeedit_schedule = get_timeedit_events(session) + + reset_gc_events(timeedit_schedule) -- 2.30.2