#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Thu Feb 24 08:51:58 2022 @author: antoine """ ################ ### UniSquat ### ################ """ Indique toutes les salles disponibles dans les différents départements de l'Université de Strasbourg. """ # Modules : import requests import icalendar import datetime import os # Fonctions : def room(name, start, end, is_free): """ Retourne un dictionnaire contenant le nom, la prochaine date à laquelle elle est occupée, ainsi que la prochaine date à laquelle elle est libre. Parameters ---------- name : str Le nom de la salle. start : datetime.datetime Salle occupée : heure de début de la prochaine période de disponibilité. Salle libre : inutilisé (vaut la date du jour à 00:00). end : datetime.datetime Salle occupée : heure de fin de la prochaine période de disponibilité. Salle libre : heure de fin de disponibilité. is_free : bool Indique si la salle est libre ('True') ou non ('False') Returns ------- dict Un dictionnaire contenant ces 4 informations, avec le même nom en clef. """ # return {"name":name, "occupied_at":occupied_at, "free_at":free_at, "free":free_at.timestamp() > occupied_at.timestamp()} return {"name":name, "start":start, "end":end, "is_free":is_free} def sched_get(date, enddate = None, link = None) : """ Récupère l'emploi du temps de toutes les salles (pour le moment, juste de l'UFR) sur ADE depuis le site de l'Unistra. Parameters ---------- date : datetime.datetime() Date du calendrier à télécharger (date de début si une date de fin 'enddate' est indiquée). Optionnels: link: Un lien vers lequel effectuer la recherche, des informations seront remplacées : $YEAR$ : l'année $MONTH$ : le mois $DAY$ : le jour Par défaut, sera un lien des salles de l'UFR. enddate : datetime.datetime() Date de fin du calendrier à télécharger (par défaut, il s'agit de la date de début). Returns ------- bytes Le texte du résultat de la requête. """ if not link: link = "https://adecons.unistra.fr/jsp/custom/modules/plannings/anonymous_cal.jsp?resources=30626&projectId=8&calType=ical&firstDate=$YEAR1$-$MONTH1$-$DAY1$&lastDate=$YEAR2$-$MONTH2$-$DAY2$" day1 = str(date.day) month1 = str(date.month) year1 = str(date.year) finallink = link.replace("$DAY1$", day1) finallink = finallink.replace("$MONTH1$", month1) finallink = finallink.replace("$YEAR1$", year1) if enddate != None : day2 = str(enddate.day) month2 = str(enddate.month) year2 = str(enddate.year) finallink = finallink.replace("$DAY2$", day2) finallink = finallink.replace("$MONTH2$", month2) finallink = finallink.replace("$YEAR2$", year2) else : finallink = finallink.replace("$DAY2$", day1) finallink = finallink.replace("$MONTH2$", month1) finallink = finallink.replace("$YEAR2$", year1) r = requests.get(finallink) return r.content def getrooms(datet, links=[]) : """ Crée une liste de toutes les salles, avec des informations si elles sont libres ou non. Parameters ---------- datet : datetime.datetime() Date au format datetime. Returns ------- total_rooms : dict Dictionnaire des salles, indexées par leur nom. Toutes les salles mentionnées dans le fichier, avec des informations. """ # Récupération des informations sur l'EDT téléchargé : cals = [] for i in links : cals.append(icalendar.Calendar.from_ical(sched_get(datet), link = i)) if len(links) == 0 : # Par défaut, ne mets pas de lien, ce qui retourne celui de l'UFR cals = [icalendar.Calendar.from_ical(sched_get(datet))] total_rooms = {} # default_hour_margin = 2 # Création du dico de toutes les salles (récupération de l'emploi du temps de l'année entière, pour être sûr d'obtenir toutes les salles disponibles): if datet.month >= 9 : # Si on est au début de l'année scolaire (par ex : en 2021, si l'année scolaire est 2021-2022) year1 = datet.year # Année civile de début de l'année scolaire year2 = datet.year + 1 # Année civile de fin de l'année scolaire else : year1 = datet.year - 1 year2 = datet.year date1 = datetime.datetime(year1, 9, 1) date2 = datetime.datetime(year2, 8, 1) # Création du calendrier de l'année scolaire : # On vérifie si le fichier existe : if not os.path.exists("data/schedule" + str(year1) + "-" + str(year2) + ".ics") : # On télécharge l'emploi du temps de l'année scolaire, s'il n'existe pas : year_cal_file = open("data/schedule" + str(year1) + "-" + str(year2) + ".ics", "w") year_cal = sched_get(date1, date2, links) year_cal_file.writelines(year_cal.decode("utf-8")) year_cal_file.close() year_cal_file = open("data/schedule" + str(year1) + "-" + str(year2) + ".ics", "rb") year_cals = [] for i in links : year_cals.append(icalendar.Calendar.from_ical(year_cal_file.read(), link = i)) if len(links) == 0 : # Par défaut, ne mets pas de lien, ce qui retourne celui de l'UFR year_cals = [icalendar.Calendar.from_ical(year_cal_file.read())] # Ajout de toutes les salles contenues dans le calendrier de l'année scolaire : for cal in year_cals : # Bâtiments for comp in cal.walk() : # Événements if comp.name == "VEVENT" : # Ajout de la salle dans le dictionnaire, si elle n'y est pas : roomname = str(comp.get("location")) if roomname not in total_rooms.keys() : start = datet.replace(hour = 0, minute = 0, second=0) # Par défaut, l'heure de début de disponibilité est aujourd'hui à 00:00 end = datet.replace(hour = 23, minute = 59, second = 59) # Par défaut, l'heure de fin de la prochaine période disponibilité est aujourd'hui à 23:59 is_free = True # Par défaut, la salle est libre total_rooms[roomname] = room(roomname, start, end, is_free) # Ajout des infos supplémentaires sur les salles (heures de début-fin de dispo, indicateur de dispo), s'il y en a : # Première boucle, pour déterminer les salles occupées : for cal in cals : # Bâtiments for comp in cal.walk() : # Événements if comp.name == "VEVENT" : # Récupération des infos : datestart = comp.decoded("dtstart") dateend = comp.decoded("dtend") roomname = str(comp.get("location")) if roomname == "C11 MATH" : print(datestart, dateend) # L'événement se passe maintenant (salle occupée maintenant) : if datestart.timestamp() <= datet.timestamp() and dateend.timestamp() > datet.timestamp() : start = dateend # L'heure de début de la prochaine période de disponibilité est la fin de l'événement end = datet.replace(hour = 23, minute = 59, second = 59) # Par défaut, l'heure de fin de la prochaine période disponibilité est aujourd'hui à 23:59 is_free = False total_rooms[roomname] = room(roomname, start, end, is_free) if roomname == "C11 MATH" : print(total_rooms[roomname]) # Deuxième boucle, pour ajouter les heures de dispos des salles : for cal in cals : # Bâtiments for comp in cal.walk() : # Événements if comp.name == "VEVENT" : # Récupération des infos : datestart = comp.decoded("dtstart") dateend = comp.decoded("dtend") roomname = str(comp.get("location")) if roomname == "C11 MATH" : print(datestart, dateend) # L'événement se passe prochainement (salle occupée à l'occasion de cet événement) : if datestart.timestamp() > datet.timestamp() : if roomname not in total_rooms.keys() : # La salle est forcément libre, car les salles occupées sont déjà toutes enregistrées : start = datet.replace(hour = 0, minute = 0, second=0) # Par défaut, l'heure de début de disponibilité est aujourd'hui à 00:00 end = datestart # L'heure de fin de disponibilité est le début de l'événement is_free = True total_rooms[roomname] = room(roomname, start, end, is_free) elif datestart.timestamp() < total_rooms[roomname]["end"].timestamp() : if datestart.timestamp() == total_rooms[roomname]["start"].timestamp() : start = dateend end = total_rooms[roomname]["end"] else : start = total_rooms[roomname]["start"] end = datestart is_free = total_rooms[roomname]["is_free"] total_rooms[roomname] = room(roomname, start, end, is_free) if roomname == "C11 MATH" : print(total_rooms[roomname]) # Dans les autres cas, l'événement est passé, donc on l'ignore et on passe au suivant. # # On ignore si c'est avant la date actuelle (avec une valeur loin dans le futur) # if datestart.timestamp() < datet.timestamp() : # datestart = datet + datetime.timedelta(hours = default_hour_margin) # Par défaut, si il n'y a rien de précisé pour sa prochaine occupation, elle sera occupée dans 1 ans # if dateend.timestamp() < datet.timestamp() : # dateend = datestart + datetime.timedelta(hours = default_hour_margin) # Par défaut, si il n'y a rien de précisé pour sa prochaine occupation, elle sera occupée dans 1 an après son occupation # Enregistrement dans le dictionnaire : # Plus nécessaire, car on ne s'occupe plus des événements passés : # if roomname in total_rooms.keys() : # if datestart.timestamp() > total_rooms[roomname]["occupied_at"].timestamp() : # occupied_at = total_rooms[roomname]["occupied_at"] # if dateend.timestamp() > total_rooms[roomname]["free_at"].timestamp() : # free_at = total_rooms[roomname]["free_at"] return total_rooms