2022-02-24 17:21:25 +01:00
#!/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.
"""
2022-05-12 15:32:12 +02:00
### Fichier du backend (récupération des salles libres et de départements ###
2022-02-24 17:21:25 +01:00
# Modules :
import requests
import icalendar
2022-05-12 15:32:12 +02:00
import pytz
2022-05-14 16:26:12 +02:00
import os
import shutil
import time
2022-05-07 18:51:23 +02:00
2022-05-12 15:32:12 +02:00
# Fichiers locaux :
2022-05-07 18:51:23 +02:00
from objects import Room
from objects import Dept
2022-02-24 17:21:25 +01:00
2022-05-14 16:26:12 +02:00
# Constantes :
CACHE_DIR = " cache "
CACHE_TTL = 3 # Intervalle de temps entre les réinitialisations du cache, en minutes
# Globales
last_cache_init = - 999
2022-02-26 15:26:34 +01:00
2022-05-12 15:32:12 +02:00
# Fonctions :
2022-05-14 16:26:12 +02:00
def reinit_cache ( ) :
global last_cache_init
"""
Vide le dossier CACHE_DIR et l ' initialise.
Modifie la variable globale last_cache_init
Parameters
- - - - - - - - - -
Aucun
Returns
- - - - - - -
Rien
"""
if os . path . isdir ( CACHE_DIR ) :
shutil . rmtree ( CACHE_DIR )
os . mkdir ( CACHE_DIR )
last_cache_init = time . time ( )
def trim ( link ) :
"""
Retourne le texte en minuscule , sans les caractères spéciaux .
( Utilisé pour le cache des requêtes )
Parameters
- - - - - - - - - -
link : String
Le lien à simplifier
Returns
- - - - - - -
La chaine de caractères correspondante
"""
result = " "
for i in link . lower ( ) :
if i not in " /:; \\ ' \" *? " :
result + = i
return result
def sched_get ( date , link , enddate = None , nocache = False ) :
2022-02-24 17:21:25 +01:00
"""
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 ( )
2022-02-26 19:33:53 +01:00
Date du calendrier à télécharger ( date de début si une date de fin
' enddate ' est indiquée ) .
2022-02-24 17:21:25 +01:00
2022-02-25 15:19:14 +01:00
Optionnels :
link :
2022-02-26 15:26:34 +01:00
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.
2022-02-26 19:33:53 +01:00
enddate : datetime . datetime ( )
Date de fin du calendrier à télécharger ( par défaut , il s ' agit de
la date de début ) .
2022-05-14 16:26:12 +02:00
nocache : booléen
Si mit à True , ne modifie pas , ni ne lit , le dossier CACHE_DIR
2022-02-25 15:19:14 +01:00
2022-02-24 17:21:25 +01:00
Returns
- - - - - - -
2022-02-26 15:26:34 +01:00
bytes
Le texte du résultat de la requête .
2022-02-24 17:21:25 +01:00
"""
2022-05-07 18:51:23 +02:00
link + = " &firstDate=$YEAR1$-$MONTH1$-$DAY1$&lastDate=$YEAR2$-$MONTH2$-$DAY2$ "
2022-02-26 19:33:53 +01:00
day1 = str ( date . day )
month1 = str ( date . month )
year1 = str ( date . year )
2022-03-01 11:05:58 +01:00
2022-02-26 19:33:53 +01:00
finallink = link . replace ( " $DAY1$ " , day1 )
finallink = finallink . replace ( " $MONTH1$ " , month1 )
finallink = finallink . replace ( " $YEAR1$ " , year1 )
2022-03-01 11:05:58 +01:00
2022-02-26 19:33:53 +01:00
if enddate != None :
day2 = str ( enddate . day )
month2 = str ( enddate . month )
year2 = str ( enddate . year )
2022-03-01 11:05:58 +01:00
2022-02-26 19:33:53 +01:00
finallink = finallink . replace ( " $DAY2$ " , day2 )
finallink = finallink . replace ( " $MONTH2$ " , month2 )
finallink = finallink . replace ( " $YEAR2$ " , year2 )
2022-03-01 11:05:58 +01:00
2022-02-26 19:33:53 +01:00
else :
finallink = finallink . replace ( " $DAY2$ " , day1 )
finallink = finallink . replace ( " $MONTH2$ " , month1 )
finallink = finallink . replace ( " $YEAR2$ " , year1 )
2022-05-07 18:51:23 +02:00
2022-05-14 16:26:12 +02:00
if nocache :
return requests . get ( finallink ) . content
else :
# Vérifie la TTL
elapsed = time . time ( ) - last_cache_init
print ( elapsed )
if elapsed > CACHE_TTL * 60 :
reinit_cache ( )
# Vérifie que le lien est dans le cache
cachepath = os . path . join ( CACHE_DIR , trim ( finallink ) )
if os . path . isfile ( cachepath ) :
result = " "
with open ( cachepath , ' rb ' ) as f :
result = f . read ( )
return result
else :
result = requests . get ( finallink ) . content
with open ( cachepath , ' wb ' ) as f :
f . write ( result )
return result
2022-02-26 15:26:34 +01:00
2022-05-07 18:51:23 +02:00
def get_depts ( filename ) :
"""
Crée une liste de tous les départements disponibles .
Parameters
- - - - - - - - - -
filename : str
Nom du fichier contenant les départements , et les liens
permettant d ' accéder au fichier iCal des salles du département.
Returns
- - - - - - -
dept_list : list
Liste des départements .
2022-02-24 17:21:25 +01:00
"""
2022-05-07 18:51:23 +02:00
dept_list = list ( )
dept_file = open ( filename , " r " )
i = 0
# dept = Dept("", "", [])
dfile_content = dept_file . readlines ( )
2022-05-10 22:35:16 +02:00
ident = 0 # Compteur pour les identifiants des départements
2022-05-07 18:51:23 +02:00
2022-05-10 22:35:16 +02:00
for i in range ( 0 , len ( dfile_content ) - 1 , 2 ) :
dept_list . append ( Dept ( ident , dfile_content [ i ] , dfile_content [ i + 1 ] , [ ] ) )
ident + = 1
2022-05-07 18:51:23 +02:00
dept_file . close ( )
return dept_list
2022-05-10 20:55:19 +02:00
def get_tot_rooms ( datet , depts , ignore_list ) :
2022-05-07 18:51:23 +02:00
"""
2022-05-10 20:55:19 +02:00
Crée une liste de toutes les salles des départements choisis .
2022-02-24 17:21:25 +01:00
Parameters
- - - - - - - - - -
datet : datetime . datetime ( )
2022-05-07 18:51:23 +02:00
Date pour la recherche de salles .
2022-05-10 20:55:19 +02:00
dept : list
Liste des départements dans lesquel chercher des salles .
2022-05-07 18:51:23 +02:00
ignore_list : list
Liste des noms de salles à ignorer .
2022-02-24 17:21:25 +01:00
Returns
- - - - - - -
2022-05-07 18:51:23 +02:00
total_rooms : list
Liste des salles .
2022-02-24 17:21:25 +01:00
"""
2022-05-07 18:51:23 +02:00
total_rooms = list ( )
margintime = 1 # Marge de temps (en mois) pour le début du calendrier (il se peut que des salles existent et soient dispos, mais qu'elles ne sont pas affichées dans l'EDT du jour choisi, donc on prend l'EDT du mois)
cal_start = datet
if cal_start . month == 1 :
cal_start . replace ( year = cal_start . year - 1 )
cal_start . replace ( month = 12 )
else :
cal_start . replace ( month = cal_start . month - margintime )
2022-05-10 20:55:19 +02:00
# Récupération des calendriers correspondants aux liens des départements, sur une période de 'margintime' mois :
cals = list ( ) # Liste des emplois du temps des départements choisis
for d in depts :
result = sched_get ( datet , d . link , datet . replace ( month = datet . month + margintime ) )
cals . append ( icalendar . Calendar . from_ical ( result ) )
2022-05-07 18:51:23 +02:00
2022-05-12 15:32:12 +02:00
roomnames = [ ] # Contient le nom de toutes les salles indiquées dans la section "LOCATION"
2022-05-07 18:51:23 +02:00
# Parcours de ces calendriers, pour faire la liste de toutes les salles :
2022-05-10 20:55:19 +02:00
for cal in cals :
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 " ) )
2022-05-12 15:32:12 +02:00
rnamelist = list ( ) # Contient le nom de toutes les salles indiquées dans la section "LOCATION" (il peut y en avoir plusieurs, séparées par des virgules)
if " , " in roomname :
rnamelist = roomname . split ( " , " )
else :
2022-05-10 20:55:19 +02:00
rnamelist . append ( roomname )
2022-05-12 15:32:12 +02:00
for rname in rnamelist :
if rname not in roomnames :
roomnames . append ( rname )
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
# Réglage du fuseau horaire :
start = start . astimezone ( pytz . timezone ( ' Europe/Paris ' ) )
end = end . astimezone ( pytz . timezone ( ' Europe/Paris ' ) )
total_rooms . append ( Room ( rname , start , end , is_free ) )
2022-05-07 18:51:23 +02:00
return total_rooms
2022-02-24 18:46:53 +01:00
2022-05-10 20:55:19 +02:00
def getrooms ( datet , depts , ignore_list ) :
2022-05-07 18:51:23 +02:00
"""
Ajout des informations supplémentaires à la liste des salles
( heures de début - fin de dispo , indicateur de dispo ) .
2022-03-01 11:05:58 +01:00
2022-05-07 18:51:23 +02:00
Parameters
- - - - - - - - - -
datet : datetime . datetime ( )
Date pour la recherche de salles .
2022-05-10 20:55:19 +02:00
dept : list
Liste des départements dans lesquel chercher des salles .
2022-05-07 18:51:23 +02:00
ignore_list : list
Liste des noms de salles à ignorer .
Returns
- - - - - - -
total_rooms : list
Liste des salles .
"""
# Création de la liste de toutes les salles :
2022-05-10 20:55:19 +02:00
total_rooms = get_tot_rooms ( datet , depts , ignore_list )
2022-05-07 18:51:23 +02:00
# Récupération des calendriers correspondants au lien du département :
2022-05-10 20:55:19 +02:00
cals = list ( ) # Liste des emplois du temps des départements choisis
for d in depts :
2022-05-10 22:35:16 +02:00
result = sched_get ( datet , d . link , datet )
2022-05-10 20:55:19 +02:00
cals . append ( icalendar . Calendar . from_ical ( result ) )
2022-05-07 18:51:23 +02:00
2022-02-26 19:33:53 +01:00
# Ajout des infos supplémentaires sur les salles (heures de début-fin de dispo, indicateur de dispo), s'il y en a :
2022-05-10 20:55:19 +02:00
for cal in cals :
# Première boucle, pour déterminer les salles occupées :
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 " ) )
2022-05-12 15:32:12 +02:00
rnamelist = list ( ) # Contient le nom de toutes les salles indiquées dans la section "LOCATION" (il peut y en avoir plusieurs, séparées par des virgules)
if " , " in roomname :
rnamelist = roomname . split ( " , " )
else :
rnamelist . append ( roomname )
for rname in rnamelist :
# 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
# Réglage du fuseau horaire :
start = start . astimezone ( pytz . timezone ( ' Europe/Paris ' ) )
end = end . astimezone ( pytz . timezone ( ' Europe/Paris ' ) )
for r in total_rooms :
if r . name == rname :
r . start = start
r . end = end
r . is_free = is_free
2022-05-10 20:55:19 +02:00
# Deuxième boucle, pour ajouter les heures de dispos des salles :
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 " ) )
2022-05-12 15:32:12 +02:00
rnamelist = list ( ) # Contient le nom de toutes les salles indiquées dans la section "LOCATION" (il peut y en avoir plusieurs, séparées par des virgules)
if " , " in roomname :
rnamelist = roomname . split ( " , " )
else :
rnamelist . append ( roomname )
for rname in rnamelist :
# L'événement se passe prochainement (salle occupée à l'occasion de cet événement) :
if datestart . timestamp ( ) > datet . timestamp ( ) :
for r in total_rooms :
if r . name == roomname :
if datestart . timestamp ( ) < r . end . timestamp ( ) :
2022-05-13 16:27:21 +02:00
if not ( r . is_free ) and ( datestart . timestamp ( ) == r . start . timestamp ( ) ) :
2022-05-12 15:32:12 +02:00
start = dateend
end = r . end
else :
2022-05-13 16:27:21 +02:00
start = r . start
2022-05-12 15:32:12 +02:00
end = datestart
# Réglage du fuseau horaire :
start = start . astimezone ( pytz . timezone ( ' Europe/Paris ' ) )
end = end . astimezone ( pytz . timezone ( ' Europe/Paris ' ) )
r . start = start
r . end = end
2022-05-07 18:51:23 +02:00
2022-02-25 10:51:34 +01:00
return total_rooms