From 642c82f7f23da4f0315883f21ef0c4855bcffbdb Mon Sep 17 00:00:00 2001 From: Klafyvel Date: Mon, 18 Dec 2017 14:49:16 +0100 Subject: [PATCH] =?UTF-8?q?T=C3=A9l=C3=A9chargement=20fonctionnel=20+=20cr?= =?UTF-8?q?=C3=A9ation=20d'agenda=20google.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 ++-- main.py | 145 +++++++++++++++++++++++++++++++++++------------------- 2 files changed, 98 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 76271c3..81df670 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ -Un script du cancer codé avec les pieds pour aller chercher l'emploi du temps -sur NTNOE. +Un script pour aller chercher l'emploi du temps +sur NTNOE. Il crée un agenda `ntnoe` dans votre Google agenda pour y +synchroniser l'agenda NTNOE. Vous avec besoin d'une clé API google, allez la chercher [ici](https://developers.google.com/google-apps/calendar/quickstart/python) et @@ -17,6 +18,4 @@ Créez un fichier `ntnoe_credentials` sur le modèle de python3 main.py ``` -Par contre ça gère pas les modifications de cours de façon tip top, donc -synchronisez pas trop dans le future. Pour modifier ce temps, voir la variable -`TIMEDELTA_SYNCHRO`. +Après le tout s'automatise bien avec un petit `cron` des familles. diff --git a/main.py b/main.py index 687cb0e..b34fa8c 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,38 @@ +""" +NTNOE SYNC is a simple script tha allows you to synchronize your NTNOE diary +with Google Calendar. + +It will create a `ntnoe` calendar among your Google calendars and write your +NTNOE calendar into. NTNOE_SYNC is availabe under the MIT license. + + +MIT License + +Copyright (c) 2017 Hugo LEVY-FALK + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + import datetime import httplib2 -import urllib import os import requests -import shutil from apiclient import discovery from oauth2client import client @@ -13,25 +42,29 @@ from oauth2client.file import Storage import icalendar -TIMEDELTA_SYNCHRO = datetime.timedelta(days=7) # Number of days to look for +TIMEDELTA_SYNCHRO = datetime.timedelta(days=15) # Number of days to look for # for synchronization -with open('ntnoe_credentials') as f: - NTNOE_ID,NTNOE_PASS,_ = f.read().split('\n') +with open('ntnoe_credentials') as f: + NTNOE_ID, NTNOE_PASS, _ = f.read().split('\n') SCOPES = 'https://www.googleapis.com/auth/calendar' CLIENT_SECRET_FILE = 'client_secret.json' APPLICATION_NAME = 'Google Calendar API Python Quickstart' - class Event: + """ + The event class allows a simple convertion between `icalendar.cal.Event` + and a formatted `dict` for the google API. + """ + # ColorId corresponding to course code EVENT_COLOR = { - '9' : '9', # Amphi - '11' : '10', # TL - '10' : '6', # TD - '13' : '5', # Autre - '12' : '3', # Exam + '9': '9', # Amphi + '11': '10', # TL + '10': '6', # TD + '13': '5', # Autre + '12': '3', # Exam } @@ -41,28 +74,31 @@ class Event: self.start = e.decoded('DTSTART') self.end = e.decoded('DTEND') self.location = e.decoded('LOCATION').decode('utf-8') - self.colorid = self.EVENT_COLOR.get(e.decoded('DESCRIPTION').decode('utf-8'), '1') + self.colorid = self.EVENT_COLOR.get( + e.decoded('DESCRIPTION').decode('utf-8'), '1') def __str__(self): return str(self.as_google()) def as_google(self): + """Returns the event as a formatted `dict` for the google API.""" return { - 'summary' : self.summary, - 'location' : self.location, - 'start' : { - 'dateTime' : self.start.isoformat(), - 'timeZone' : 'Europe/Paris', + 'summary': self.summary, + 'location': self.location, + 'start': { + 'dateTime': self.start.isoformat(), + 'timeZone': 'Europe/Paris', }, - 'end' : { - 'dateTime' : self.end.isoformat(), - 'timeZone' : 'Europe/Paris', + 'end': { + 'dateTime': self.end.isoformat(), + 'timeZone': 'Europe/Paris', }, - 'colorId' : self.colorid, + 'colorId': self.colorid, 'reminders': { 'useDefault': False, 'overrides': [], }, + 'description': 'ntnoe_calendar', } @@ -94,48 +130,58 @@ def get_credentials(): def get_ntnoe(): - url = "http://ntnoe.metz.supelec.fr/ical/EdTcustom/Eleves/edt_{}.ics" - url = url.format(NTNOE_ID) - - data = {'envoyer':'Utf8_All','submit':'Générer'} - - r = requests.get( + """Retrieves the calendar on NTNOE.""" + r = requests.post( "https://ntnoe.metz.supelec.fr/ical/index.php", - data=urllib.parse.urlencode(data), + data={"envoyer":"Utf8_All","submit":"G%E9n%E9rer"}, auth=(NTNOE_ID, NTNOE_PASS), ) - url = "https://ntnoe.metz.supelec.fr/ical/EdTcustom/Eleves/edt_{id}.ics".format(id=NTNOE_ID) - r = requests.get(url, auth=(NTNOE_ID, NTNOE_PASS), stream=True) - with open("edt_{}.ics".format(NTNOE_ID), 'wb') as f: - f.write(r.content) + url = "https://ntnoe.metz.supelec.fr/ical/EdTcustom/Eleves/edt_{}.ics" + url = url.format(NTNOE_ID) + r = requests.get(url, auth=(NTNOE_ID, NTNOE_PASS)) + return r.content def main(): """Get the events on NTNOE the puts them on Google Calendar. - """ - get_ntnoe() credentials = get_credentials() http = credentials.authorize(httplib2.Http()) service = discovery.build('calendar', 'v3', http=http) - with open("edt_" + NTNOE_ID + ".ics") as f: - c = icalendar.Calendar.from_ical(f.read()) + ical = icalendar.Calendar.from_ical(get_ntnoe()) + + calendars = service.calendarList().list().execute() + ntnoe_calendar_id = None + for c in calendars['items']: + if c['summary'] == 'ntnoe': + ntnoe_calendar_id = c['id'] + + if not ntnoe_calendar_id: + print("Creating ntnoe calendar...") + created = service.calendars().insert(body={ + 'defaultReminders' : [], + 'selected' : True, + 'summary' : 'ntnoe', + }).execute() + ntnoe_calendar_id = created['id'] now = datetime.datetime.now() then = now + TIMEDELTA_SYNCHRO time_search = datetime.datetime(now.year, now.month, now.day, 1) - response = service.events().list( - calendarId='primary', - timeMin=time_search.isoformat()+'Z', - timeMax=then.isoformat()+'Z', + + former_ones = service.events().list( + calendarId=ntnoe_calendar_id, ).execute() - existing_events = set() - for e in response['items']: - if 'summary' in e.keys(): - existing_events.add(e['summary']) - for e in c.walk('VEVENT'): + for event in former_ones['items']: + print('Deleting event : {}'.format(event['id'])) + service.events().delete( + calendarId=ntnoe_calendar_id, + eventId=event['id'] + ).execute() + + for e in ical.walk('VEVENT'): event = Event(e) t = ( event.summary, @@ -146,15 +192,12 @@ def main(): if now >= event.end or event.start >= then: continue - if existing_events.intersection({event.summary}): - continue - event = service.events().insert( - calendarId='primary', + calendarId=ntnoe_calendar_id, body=event.as_google() ).execute() - print("Added event : {}".format(event.get('htmlLink'))) + print("Adding event : {}".format(event['id'])) + if __name__ == '__main__': main() -