Réécriture complète du code pour la recherche des salles pour des résultats exacts.

Ajouts de propriétés à la classe Room.
Ajout d'une constante pour l'utilisation du cache.
Corrections mineures.
This commit is contained in:
antux18 2024-02-11 18:25:16 +01:00
parent 4d66319a29
commit 430e8938a2
5 changed files with 100 additions and 95 deletions

8
app.py
View File

@ -15,6 +15,7 @@ import datetime as dti
import pytz import pytz
import json import json
import os import os
import traceback
from flask import Flask from flask import Flask
from flask import render_template from flask import render_template
@ -295,8 +296,11 @@ def free_rooms(api = False, rq = None) :
try : try :
free_rooms = ro.getrooms(date, depts, ignore_list) free_rooms = ro.getrooms(date, depts, ignore_list)
except ValueError as err : except ValueError as err :
return render_template("error.html", error="Le serveur Unistra a rencontré une erreur ! Veuillez réessayer plus tard.") errdetails = str(''.join(traceback.format_exception(None, err, err.__traceback__)))
#return render_template("error.html", error="Le serveur Unistra a rencontré une erreur ! Détails de l'erreur : " + str(''.join(traceback.format_exception(None, err, err.__traceback__)))) if GLOBAL_CONTEXT["DEBUG"] :
print(errdetails)
return render_template("error.html",
error="Le serveur Unistra a rencontré une erreur ! Veuillez réessayer plus tard.")
# Création d'un dictionnaire avec les infos des salles : # Création d'un dictionnaire avec les infos des salles :
frooms_disp = dict() # Mise en forme des infos pour la page Web frooms_disp = dict() # Mise en forme des infos pour la page Web

View File

@ -42,7 +42,16 @@ class Room :
end : datetime.datetime end : datetime.datetime
Salle occupée : heure de fin de la prochaine période de disponibilité. Salle occupée : heure de fin de la prochaine période de disponibilité.
Salle libre : heure de fin de disponibilité. Salle libre : heure de fin de disponibilité (inutilisé s'il n'y en a
pas (vaut alors la date du jour à 23:59:59)).
nostart : bool
Indique si la salle a un début de disponibilité ('False' dans le cas
des salles libres).
noend : bool
Indique si la salle a une fin de disponibilité ('False' dans le cas
la salle est dispo pour le reste de la journée).
is_free : bool is_free : bool
Indique si la salle est libre ('True') ou non ('False'). Indique si la salle est libre ('True') ou non ('False').
@ -55,10 +64,13 @@ class Room :
Le nom du département auquel la salle appartient. Le nom du département auquel la salle appartient.
""" """
def __init__(self, name, start, end, is_free, dept_name="DEFAULT DEPT") : def __init__(self, name, start, end, nostart, noend, is_free,
dept_name="DEFAULT DEPT") :
self.name = name self.name = name
self.start = start self.start = start
self.end = end self.end = end
self.nostart = nostart
self.noend = noend
self.is_free = is_free self.is_free = is_free
self.id = self.getId(name) self.id = self.getId(name)
self.dept_name = dept_name self.dept_name = dept_name

View File

@ -17,7 +17,7 @@ Created on Thu Feb 24 08:51:58 2022
# Modules : # Modules :
import requests import requests
import icalendar import icalendar
# import ics as icalendar import ics
import pytz import pytz
import os import os
import shutil import shutil
@ -36,6 +36,9 @@ CACHE_TTL = 5
# Nombres maximum de fichier dans le cache : # Nombres maximum de fichier dans le cache :
CACHE_SIZE = 10 CACHE_SIZE = 10
# Flag pour utiliser le cache :
NO_CACHE = False
# Globales : # Globales :
last_cache_init = -999 last_cache_init = -999
@ -111,7 +114,7 @@ def sched_get(date, link, enddate = None, nocache = False) :
Returns Returns
------- -------
bytes str
Le texte du résultat de la requête. Le texte du résultat de la requête.
""" """
@ -140,7 +143,13 @@ def sched_get(date, link, enddate = None, nocache = False) :
finallink = finallink.replace("$YEAR2$", year1) finallink = finallink.replace("$YEAR2$", year1)
if nocache : if nocache :
return requests.get(finallink).content # Utilisation du module 'ics' pour le tri du calendrier dans l'ordre
# chronologique :
result = requests.get(finallink).text
cal = ics.Calendar(result)
cal.events = sorted(cal.events)
result = cal.serialize()
return result
else : else :
# Vérifie la TTL : # Vérifie la TTL :
elapsed = time.time() - last_cache_init elapsed = time.time() - last_cache_init
@ -155,12 +164,17 @@ def sched_get(date, link, enddate = None, nocache = False) :
cachepath = os.path.join(CACHE_DIR, trim(finallink)) cachepath = os.path.join(CACHE_DIR, trim(finallink))
if os.path.isfile(cachepath) : if os.path.isfile(cachepath) :
result = "" result = ""
with open(cachepath,'rb') as f : with open(cachepath,'r') as f :
result = f.read() result = f.read()
return result return result
else: else :
result = requests.get(finallink).content result = requests.get(finallink).text
with open(cachepath,'wb') as f : # Utilisation du module 'ics' pour le tri du calendrier dans l'ordre
# chronologique :
cal = ics.Calendar(result)
cal.events = sorted(cal.events)
result = cal.serialize()
with open(cachepath,'w') as f :
f.write(result) f.write(result)
return result return result
@ -221,52 +235,57 @@ def getrooms(datet, depts, ignore_list) :
# Marge de temps (en mois) pour le début du calendrier. # Marge de temps (en mois) pour le début du calendrier.
# Certaines salles ne sont pas utilisées tous les jours, # Certaines salles ne sont pas utilisées tous les jours,
# donc on télécharge l'EDT d'un mois complet pour être tranquille. # donc on télécharge l'EDT du reste du mois et du suivant
# pour être tranquille.
margintime = 1 margintime = 1
# Récupération des calendriers correspondants aux liens des départements, # Récupération du calendrier de chaque département,
# sur une période de 'margintime' mois : # sur une période de 'margintime' mois :
cals = list() # Liste des EDT des départements choisis cals = list() # Liste des EDT des départements choisis
for d in depts : for d in depts :
if datet.month < 12 : if datet.month < 12 :
result = sched_get(datet, d.link, result = sched_get(datet, d.link,
datet.replace(month = datet.month + margintime)) datet.replace(month = datet.month + margintime),
NO_CACHE)
else : else :
result = sched_get(datet, d.link, result = sched_get(datet, d.link,
datet.replace(month = 1, year = datet.year + 1)) datet.replace(month = 1, year = datet.year + 1),
NO_CACHE)
# # Utilisation du module 'ics' pour le tri du calendrier dans l'ordre
# # chronologique :
# cal = ics.Calendar(result)
# cal.events = sorted(cal.events)
cals.append(icalendar.Calendar.from_ical(result)) cals.append(icalendar.Calendar.from_ical(result))
# cals.append(icalendar.Calendar(result.decode("utf-8"))) # cals.append(icalendar.Calendar(result.decode("utf-8")))
# Parcours de ces calendriers : # Parcours de ces calendriers :
dept_index = 0 dept_index = 0
for cal in cals : for cal in cals :
# Première boucle pour la liste des salles + déterminer les occupées :
for comp in cal.walk() : # Événements for comp in cal.walk() : # Événements
if comp.name == "VEVENT" : if comp.name == "VEVENT" :
# Récupération des infos : # Récupération des infos :
roomname = str(comp.get("location")) evtloc = str(comp.get("location"))
datestart = comp.decoded("dtstart") evtstart = comp.decoded("dtstart")
dateend = comp.decoded("dtend") evtend = comp.decoded("dtend")
roomname = str(comp.get("location"))
# Contient le nom de toutes les salles indiquées # Contient le nom de toutes les salles indiquées
# dans la section "LOCATION" : # dans la section "LOCATION" :
rnamelist = list() rnamelist = list()
# Séparation des salles multiples, le cas échéant : # Séparation des salles multiples, le cas échéant :
if "," in roomname : if "," in evtloc :
rnamelist = roomname.split(",") rnamelist = evtloc.split(",")
else : else :
rnamelist.append(roomname) rnamelist.append(evtloc)
for rname in rnamelist : for roomname in rnamelist :
rname = rname.strip() roomname = roomname.strip()
if rname not in ignore_list : if roomname not in ignore_list :
# Création d'une nouvelle salle # Création d'une nouvelle salle
# si elle n'existe pas déjà : # si elle n'existe pas déjà :
exists = False exists = False
for room in total_rooms : for room in total_rooms :
if room.name == rname : if room.name == roomname :
exists = True exists = True
r = room r = room
@ -277,28 +296,45 @@ def getrooms(datet, depts, ignore_list) :
# - L'heure de fin de la prochaine période # - L'heure de fin de la prochaine période
# de disponibilité est aujourd'hui à 23:59. # de disponibilité est aujourd'hui à 23:59.
# - La salle est libre. # - La salle est libre.
r = Room(rname, r = Room(roomname,
datet.replace(hour = 0, minute = 0, second = 0), datet.replace(hour = 0, minute = 0, second = 0),
datet.replace(hour = 23, minute = 59, second = 59), datet.replace(hour = 23, minute = 59, second = 59),
True, True,
True,
True,
depts[dept_index].name depts[dept_index].name
) )
# Si l'événement se passe aujourd'hui : # Si l'événement se passe aujourd'hui :
if datestart.day == datet.day and \ if evtstart.day == datet.day and \
datestart.month == datet.month and \ evtstart.month == datet.month and \
datestart.year == datet.year : evtstart.year == datet.year :
if rname == "Grand Amphi MAI Frenkel" :
print(r.start.hour, r.end.hour, datestart.hour+2, dateend.hour+2)
# Si l'événement se passe maintenant # Si l'événement se passe maintenant
# (salle occupée maintenant) : # (salle occupée maintenant) :
if datestart.timestamp() <= datet.timestamp() and \ if evtstart.timestamp() <= datet.timestamp() and \
dateend.timestamp() > datet.timestamp() : evtend.timestamp() > datet.timestamp() :
r.is_free = False r.is_free = False
r.nostart = False
# L'heure de début de la prochaine dispo est # L'heure de début de la prochaine dispo est
# la fin de l'événement, # la fin de l'événement :
# si différente de la fin de dispo : r.start = evtend
r.start = dateend # Si l'événement se passe prochainement
# (salle occupée à l'occasion de cet événement)
# et que le début de l'événement est avant la date
# de fin de dispo actuelle (on cherche la date la
# plus proche de maintenant) :
elif evtstart.timestamp() > datet.timestamp() and \
evtstart.timestamp() < r.end.timestamp() :
if evtstart.timestamp() == r.start.timestamp() :
r.nostart = False
# Dans ce cas, l'événement en cours suit
# celui qui a défini le début de dispo
# de la salle. Alors, c'est la fin de cet
# événement qui marque le début de la dispo.
r.start = evtend
else :
r.noend = False
r.end = evtstart
# Réglage du fuseau horaire : # Réglage du fuseau horaire :
r.start = r.start.astimezone(pytz.timezone('Europe/Paris')) r.start = r.start.astimezone(pytz.timezone('Europe/Paris'))
@ -307,53 +343,6 @@ def getrooms(datet, depts, ignore_list) :
if not exists : if not exists :
total_rooms.append(r) total_rooms.append(r)
# Deuxième boucle, pour déterminer les périodes de dispo :
for comp in cal.walk() :
if comp.name == "VEVENT" :
# Récupération des infos :
roomname = str(comp.get("location"))
datestart = comp.decoded("dtstart")
dateend = comp.decoded("dtend")
roomname = str(comp.get("location"))
# Contient le nom de toutes les salles indiquées
# dans la section "LOCATION" :
rnamelist = list()
# Séparation des salles multiples, le cas échéant :
if "," in roomname :
rnamelist = roomname.split(",")
else :
rnamelist.append(roomname)
for rname in rnamelist :
rname = rname.strip()
if rname not in ignore_list :
# Recherche de la salle :
for room in total_rooms :
if room.name == rname :
r = room
# Si l'événement se passe aujourd'hui :
if datestart.day == datet.day and \
datestart.month == datet.month and \
datestart.year == datet.year :
if rname == "Grand Amphi MAI Frenkel" :
print(r.start.hour, r.end.hour, datestart.hour+2, dateend.hour+2)
# Si l'événement se passe prochainement
# (salle occupée à l'occasion de cet événement) :
if datestart.timestamp() > datet.timestamp() :
if datestart.timestamp() == r.start.timestamp() :
r.start = dateend
elif datestart.timestamp() < r.end.timestamp() :
r.end = datestart
# Réglage du fuseau horaire :
r.start = r.start.astimezone(pytz.timezone('Europe/Paris'))
r.end = r.end.astimezone(pytz.timezone('Europe/Paris'))
dept_index += 1 dept_index += 1
return total_rooms return total_rooms

View File

@ -262,12 +262,12 @@ footer {
display:flex; display:flex;
flex-wrap: wrap; flex-wrap: wrap;
} }
.room-collumn{ .room-column{
width: 50%; width: 50%;
flex-grow: 1; flex-grow: 1;
} }
} }
.room-collumn{ .room-column{
margin-bottom: 10px; margin-bottom: 10px;
} }

View File

@ -35,7 +35,7 @@
{% if favs: %} {% if favs: %}
<div class="flex-pc"> <div class="flex-pc">
{% if favs_free_rooms|length>0: %} {% if favs_free_rooms|length>0: %}
<div class="room-collumn"> <div class="room-column">
<br> <br>
<h1>Favoris disponibles maintenant</h1> <h1>Favoris disponibles maintenant</h1>
<div class="flex-container"> <div class="flex-container">
@ -45,7 +45,7 @@
<div class="room-row"> <div class="room-row">
<div> <div>
{{ room.name }} {% if DEBUG :%}( {{ room.id }} ){% endif %} {{ room.name }} {% if DEBUG :%}( {{ room.id }} ){% endif %}
{% if not(room.end.hour == 23 and room.end.minute == 59 and room.end.second == 59) : %} {% if not(room.noend) : %}
<p class=details>Jusqu'à {{ frooms_disp[room.name]["end"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p> <p class=details>Jusqu'à {{ frooms_disp[room.name]["end"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p>
{% endif %} {% endif %}
</div> </div>
@ -60,7 +60,7 @@
</div> </div>
{% endif %} {% endif %}
{% if favs_soon_rooms|length>0: %} {% if favs_soon_rooms|length>0: %}
<div class="room-collumn"> <div class="room-column">
<br> <br>
<h1>Favoris disponibles prochainement</h1> <h1>Favoris disponibles prochainement</h1>
<div class="flex-container"> <div class="flex-container">
@ -70,7 +70,7 @@
<div class="room-row"> <div class="room-row">
<div> <div>
{{ room.name }} {% if DEBUG :%}( {{ room.id }} ){% endif %} {{ room.name }} {% if DEBUG :%}( {{ room.id }} ){% endif %}
{% if room.end.hour == 23 and room.end.minute == 59 and room.end.second == 59 : %} {% if room.noend : %}
<p class=details>À {{ frooms_disp[room.name]["start"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p> <p class=details>À {{ frooms_disp[room.name]["start"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p>
{% else %} {% else %}
<p class=details>De {{ frooms_disp[room.name]["start"] }} à {{ frooms_disp[room.name]["end"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p> <p class=details>De {{ frooms_disp[room.name]["start"] }} à {{ frooms_disp[room.name]["end"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p>
@ -90,7 +90,7 @@
{% endif %} {% endif %}
<div class="flex-pc"> <div class="flex-pc">
{% if free_rooms|length>0 %} {% if free_rooms|length>0 %}
<div class="room-collumn"> <div class="room-column">
<br> <br>
<h1>Disponibles maintenant</h1> <h1>Disponibles maintenant</h1>
<div class="flex-container"> <div class="flex-container">
@ -100,7 +100,7 @@
<div class="room-row"> <div class="room-row">
<div> <div>
{{ room.name }} {% if DEBUG :%}( {{ room.id }} ){% endif %} {{ room.name }} {% if DEBUG :%}( {{ room.id }} ){% endif %}
{% if not(room.end.hour == 23 and room.end.minute == 59 and room.end.second == 59) : %} {% if not room.noend : %}
<p class=details>Jusqu'à {{ frooms_disp[room.name]["end"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p> <p class=details>Jusqu'à {{ frooms_disp[room.name]["end"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p>
{% endif %} {% endif %}
</div> </div>
@ -115,7 +115,7 @@
</div> </div>
{% endif %} {% endif %}
{% if soon_rooms|length>0 %} {% if soon_rooms|length>0 %}
<div class="room-collumn"> <div class="room-column">
<br> <br>
<h1>Disponibles prochainement</h1> <h1>Disponibles prochainement</h1>
<div class="flex-container"> <div class="flex-container">
@ -125,7 +125,7 @@
<div class="room-row"> <div class="room-row">
<div> <div>
{{ room.name }} {% if DEBUG :%}( {{ room.id }} ){% endif %} {{ room.name }} {% if DEBUG :%}( {{ room.id }} ){% endif %}
{% if room.end.hour == 23 and room.end.minute == 59 and room.end.second == 59 : %} {% if room.noend : %}
<p class=details>À {{ frooms_disp[room.name]["start"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p> <p class=details>À {{ frooms_disp[room.name]["start"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p>
{% else %} {% else %}
<p class=details>De {{ frooms_disp[room.name]["start"] }} à {{ frooms_disp[room.name]["end"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p> <p class=details>De {{ frooms_disp[room.name]["start"] }} à {{ frooms_disp[room.name]["end"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p>