diff --git a/README.md b/README.md index cd6614f..5fd66a6 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,38 @@ # UniSquat -Application pour trouver rapidement les salles disponibles dans l'Université de Strasbourg. +Une application pour trouver rapidement les salles disponibles dans l'Université de Strasbourg. C'est utile aux élèves qui cherchent un coin pour travailler ou manger, comme aux enseignants en détresse à cause d'un changement de salle imprévu. -Cette application dispose d'une interface Web fonctionnant avec Flask ( [voir la demo][homepage] ). +Cette application dispose d'une interface Web fonctionnant avec Flask. Une version en ligne est disponible [ici][homepage]. ## Fonctionnalités -- 🔎 Visualiser les salles libres de plusieurs départements en même temps ( par exemple UFR de Math-Info et EOST ) -- ⏰ Pour les salles bientôt occupées, l'heure d'occupation est précisée -- 🔄 Affiche également les salles qui sont bientôt libres, avec l'heure en question -- ⭐ Permet de sélectionner des salles comme favorites - * Ces favoris ne sont conservés que sur la page en question ( les favoris sélectionnés sont stockés dans l'URL ) - * Ainsi, vous pouvez partager vos favoris simplement en partageant l'URL +- 🔎 Visualiser les salles libres de plusieurs départements en même temps (par exemple : l'UFR de Math-Info et l'EOST). +- ⏰ Connaître la période de disponibilité d'une salle. +- 🔜 Les salles prochainement libres sont également affichées, avec l'heure de début de disponibilité. +- ⭐ Marquer des salles comme favorites, pour les afficher en haut de la page. + * Les favoris sont stockés dans l'URL de la page. Cela permet de le partager simplement, puisqu'il suffit de partager l'URL +* 📅 Rechercher les salles libres à une date précise. - 🪶 Application légère pour l'utilisateur : - * Pas de JavaScript, tout les calculs sont fait coté serveur - * Pas de *Local Storage*, *Cookies* ou autres *bibliothèques CSS* + * Pas de JavaScript, tout les calculs sont fait coté serveur + * Pas de *Local Storage*, *Cookies* ou autres *bibliothèques CSS* ## Dépendances -Pour l'instant, ce programme utilise les modules suivants : -- datetime -- icalendar -- requests +Le modules Python suivant sont requis (ils peuvent être installés avec `pip`) : -Pour la version Web (avec Flask) : -- flask +- `datetime` +- `icalendar` +- `requests` +- `flask` (requis pour l'interface Web) + +Vous pouvez aussi installer directement les dépendances avec cette commande : -Vous pouvez aussi installer directement les dépendances avec `pip` : ```python pip install -r requirements.txt ``` -## Comment lancer la version web/Flask +## Comment lancer la version Web (Flask) Dans la racine du dossier : @@ -42,15 +42,15 @@ flask run C'est la version la plus utilisable, n'hésitez pas à héberger votre propre version. -## Version antérieures +## Interfaces obsolètes -Ces versions sont plus anciennes, rudimentaires, et ne sont pas prévues pour être utilisables. +Ces interfaces ont été crées à des fins de test. Elles peuvent être instables, et ne sont plus mises à jour. ### Interface en ligne de commande -Un interface en ligne de commande est disponible. Elle ne nécessite pas de dépendances supplémentaires. +Une interface en ligne de commande est disponible. Elle ne nécessite pas de dépendances supplémentaires. -Pour le lancer : +Pour la lancer : ```python python main_cli.py @@ -58,9 +58,9 @@ python main_cli.py ### Interface Qt5 -Interface graphique utilisant la bibliothèque Qt. Elle nécessite le module `PyQt5`. +Une interface graphique utilisant la bibliothèque Qt. Elle nécessite le module `PyQt5`. -Pour le lancer : +Pour la lancer : ```python python main_gui.py @@ -70,10 +70,10 @@ python main_gui.py Le code est sous licence [GPLv3](https://choosealicense.com/licenses/gpl-3.0/). -UniSquat est créé par deux étudiants de l'université, plus d'information sur [notre site][homepage]. +UniSquat est créé par deux étudiants de l'université de Strasbourg. Rendez-vous sur [la page d'accueil d'UniSquat][homepage] pour plus d'informations. -Notre travail est fait bénévolement, mais si vous voulez nous soutenir, passez au campus d'esplanade nous offrir un chocolat chaud ❤ +Notre travail est fait de façon bénévole, mais si vous souhaitez nous soutenir, n'hésitez pas à passer sur le campus de l'Esplanade pour nous offrir un chocolat chaud ❤ -Vous pouvez nous contacter sur nos comptes gitea respectifs, ou vous pouvez aller voir sur le [blog de @ayte](https://webair.xyz/fr/contact) pour plus d'options. +Vous pouvez nous contacter sur nos comptes Git respectifs. Vous pouvez aussi contacter @ayte [sur son blog](https://webair.xyz/fr/contact). [homepage]: https://unisquat.alwaysdata.net diff --git a/app.py b/app.py index dad2c26..bf532aa 100644 --- a/app.py +++ b/app.py @@ -3,8 +3,8 @@ ################ """ - Indique toutes les salles disponibles dans les différents départements de - l'Université de Strasbourg. + Une application pour afficher les salles libres dans les différents + départements de l'Université de Strasbourg. """ @@ -13,7 +13,6 @@ # Modules : import datetime as dti -import time from flask import Flask from flask import render_template @@ -41,6 +40,8 @@ logs = [] # Stoque les différentes requêtes faite sur la route /free_rooms/, s app = Flask(__name__) +# Fonctions : + @app.route("/") def home() : """ @@ -48,13 +49,12 @@ def home() : Parameters ---------- - None. + None Returns ------- flask.render_template """ - return render_template("index.html", **GLOBAL_CONTEXT) @@ -66,13 +66,12 @@ def select_dept() : Parameters ---------- - None. + None Returns ------- flask.render_template """ - dept_filen = "data/dept_list.txt" dept_list = ro.get_depts(dept_filen) @@ -82,6 +81,7 @@ def select_dept() : url_for("static", filename="style.css") return render_template("dept-select.html", **content, **GLOBAL_CONTEXT) + @app.route("/stats") def stats(): """ @@ -89,14 +89,13 @@ def stats(): Parameters ---------- - None. (Reads from the global "logs") + None (utilise la variable globale 'log') Returns ------- flask.render_template """ - - # Compte le nombre de fois que les différents départements ont été cherché + # Compte le nombre de fois que les différents départements ont été cherchés pings = 0 counts = {} for log in logs: @@ -118,17 +117,16 @@ def stats(): def free_rooms() : """ Affiche les salles libres dans les départements sélectionnés - dans la page précédente. + dans la page des départements. Parameters ---------- - None. + None Returns ------- flask.render_template """ - # Récupération des ID des départements depuis le formulaire : dident_list = request.args.getlist("dept") if len(dident_list)>MAX_DEPT: @@ -136,7 +134,7 @@ def free_rooms() : if len(dident_list)==0: return render_template("error.html", error="Il faut choisir au moins un département !") - # Récupération de l'éventuelle date personnalisée (depuis la page de sélection de date : + # Récupération de l'éventuelle date personnalisée (depuis la page de sélection de date) : date_uf = request.args.get("date") if date_uf == None : date_uf = [""] @@ -175,9 +173,9 @@ def free_rooms() : try: int(d) except: - return render_template("error.html",error="Identifiant de département invalide !", **GLOBAL_CONTEXT) - if int(d)<0 or int(d)>=len(dept_list): - return render_template("error.html",error="Identifiant de département invalide !", **GLOBAL_CONTEXT) + return render_template("error.html", error="Identifiant de département invalide !", **GLOBAL_CONTEXT) + if int(d) < 0 or int(d) >= len(dept_list) : + return render_template("error.html", error="Identifiant de département invalide !", **GLOBAL_CONTEXT) dident_list.sort() # Récupération des départements choisis à partir des données du formulaire : diff --git a/date_tools.py b/date_tools.py index c56528b..3cd1abf 100644 --- a/date_tools.py +++ b/date_tools.py @@ -11,8 +11,8 @@ Created on Thu Feb 24 16:36:32 2022 ################ """ - Indique toutes les salles disponibles dans les différents départements de - l'Université de Strasbourg. + Une application pour afficher les salles libres dans les différents + départements de l'Université de Strasbourg. """ @@ -36,13 +36,9 @@ def minutes_convert(time_min) : Returns ------- - int - Temps en heures. - int - Temps en minutes. - + tuple + Temps en heures et en minutes. """ - return int(time_min // 60), int(time_min % 60) @@ -60,9 +56,7 @@ def bissextile(year) : ------- bool 'True' si 'year' est bissextile, 'False' sinon. - """ - return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0 @@ -81,9 +75,7 @@ def month_days(month, year) : ------- int Nombre de jours dans 'month'. - """ - if month == 2 : if bissextile(year) : return 29 @@ -105,7 +97,6 @@ def date_input() : datetime.datetime() Date entrée au format datetime. """ - year = int(input("Entrer l'année.\n> ")) month = 0 @@ -143,7 +134,6 @@ def hour_disp(time) : time_str : str Heure en chaîne de caractères. """ - time_str = str(time.hour) + ":" if time.minute < 10 : # Ajout du zéro au début du nombre de minutes @@ -171,12 +161,17 @@ def remain_time(date, rdate) : remain_time_str : str Temps restant. """ - deltasec = rdate.timestamp() - date.timestamp() - remain_time_str = str(int(deltasec / 60 + 0.5)) + " minutes" + if int(deltasec / 60 + 0.5) > 1 : + remain_time_str = str(int(deltasec / 60 + 0.5)) + " minutes" + else : + remain_time_str = str(int(deltasec / 60 + 0.5)) + " minute" if deltasec / 60 + 0.5 >= 60 : # Conversion en heures:minutes si les minutes dépassent 60 deltasec = minutes_convert(deltasec / 60 + 0.5) - remain_time_str = str(deltasec[0]) + " heures" + if deltasec[0] > 1 : + remain_time_str = str(deltasec[0]) + " heures" + else : + remain_time_str = str(deltasec[0]) + " heure" if deltasec[1] > 0 : remain_time_str += " " + str(deltasec[1]) + " minutes" diff --git a/main_cli.py b/main_cli.py index 0f14083..2d3acbf 100644 --- a/main_cli.py +++ b/main_cli.py @@ -11,8 +11,8 @@ Created on Thu Feb 24 17:14:05 2022 ################ """ - Indique toutes les salles disponibles dans les différents départements de - l'Université de Strasbourg. + Une application pour afficher les salles libres dans les différents + départements de l'Université de Strasbourg. """ @@ -21,7 +21,6 @@ Created on Thu Feb 24 17:14:05 2022 # Modules : import datetime -import time # Fichiers locaux : import date_tools diff --git a/main_gui.py b/main_gui.py index bd43cc3..1b2ebbf 100644 --- a/main_gui.py +++ b/main_gui.py @@ -11,8 +11,8 @@ Created on Thu Mar 3 08:47:47 2022 ################ """ - Indique toutes les salles disponibles dans les différents départements de - l'Université de Strasbourg. + Une application pour afficher les salles libres dans les différents + départements de l'Université de Strasbourg. """ @@ -43,7 +43,6 @@ def main() : date = datetime.datetime.today() available_rooms = ro.getrooms(date,links=links) - button = qt.QtWidgets.QPushButton("Hello World !") label1 = qt.QtWidgets.QLabel() label1.setText("Maintenant :") label2 = qt.QtWidgets.QLabel() diff --git a/objects.py b/objects.py index b6ca73a..14504ab 100644 --- a/objects.py +++ b/objects.py @@ -11,10 +11,12 @@ Created on Sat May 7 17:29:11 2022 ################ """ - Indique toutes les salles disponibles dans les différents départements de - l'Université de Strasbourg. + Une application pour afficher les salles libres dans les différents + départements de l'Université de Strasbourg. """ +### Définition des objets ### + # Modules import random # Nécessaire pour la génération d'ID des salles @@ -23,9 +25,6 @@ ID_CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" # Ca ID_LEN = 4 # Nombres de caractères composant l'ID -### Fichier contenant les classes des salles et des départements ### - - # Objets : class Room : @@ -34,7 +33,6 @@ class Room : Attributes ---------- - name : string Le nom de la salle. @@ -48,14 +46,10 @@ class Room : is_free : bool Indique si la salle est libre ('True') ou non ('False'). - - count : int - Compte le nombre d'occurences de la salle dans l'emploi du temps; id : string - Identifiant 'unique' ( avec un très faible risque de collision ) de la salle ( généré à partir de son nom ) + Identifiant 'unique' (avec un très faible risque de conflit) de la salle (généré à partir de son nom) """ - def __init__(self, name, start, end, is_free) : self.name = name self.start = start @@ -66,8 +60,8 @@ class Room : def getId(self,name): random.seed(name) id = "" - for i in range(ID_LEN): - id+=random.choice(ID_CHARS) + for i in range(ID_LEN) : + id += random.choice(ID_CHARS) return id # A peu près une chance sur 15 millions d'être unique, je considère ça viable @@ -77,7 +71,6 @@ class Dept : Attributes ---------- - ident : int Identifiant du département. @@ -90,7 +83,6 @@ class Dept : rooms : list La liste des salles de ce département """ - def __init__(self, ident, name, link, rooms) : self.ident = ident self.name = name diff --git a/rooms_get.py b/rooms_get.py index 735495e..4f77356 100644 --- a/rooms_get.py +++ b/rooms_get.py @@ -10,12 +10,12 @@ Created on Thu Feb 24 08:51:58 2022 ################ """ - Indique toutes les salles disponibles dans les différents départements de - l'Université de Strasbourg. + Une application pour afficher les salles libres dans les différents + départements de l'Université de Strasbourg. """ -### Fichier du backend (récupération des salles libres et de départements ### +### Fichier du backend (récupération des salles libres et des départements ### # Modules : @@ -42,36 +42,39 @@ def reinit_cache() : global last_cache_init """ Vide le dossier CACHE_DIR et l'initialise. - Modifie la variable globale last_cache_init + Modifie la variable globale 'last_cache_init'. Parameters ---------- - Aucun + None + Returns ------- - Rien + None """ 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 ) + Retourne la chaîne de caractères 'link' en minuscule, sans les caractères + spéciaux (utilisé pour le cache des requêtes). Parameters ---------- - link: String + link: str Le lien à simplifier + Returns ------- + str La chaine de caractères correspondante """ - result = "" - for i in link.lower(): + for i in link.lower() : if i not in "/:;\\'\" *?": result+=i @@ -88,25 +91,26 @@ def sched_get(date, link, enddate = None, nocache = False) : 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. + 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. + + Optional : enddate : datetime.datetime() Date de fin du calendrier à télécharger (par défaut, il s'agit de la date de début). + nocache : booléen - Si mit à True, ne modifie pas, ni ne lit, le dossier CACHE_DIR + Si mit à 'True', ne modifie pas, ni ne lit, le dossier CACHE_DIR Returns ------- bytes Le texte du résultat de la requête. """ - link += "&firstDate=$YEAR1$-$MONTH1$-$DAY1$&lastDate=$YEAR2$-$MONTH2$-$DAY2$" day1 = str(date.day) @@ -133,33 +137,32 @@ def sched_get(date, link, enddate = None, nocache = False) : if nocache: return requests.get(finallink).content - else: + else : # Vérifie la TTL - elapsed = time.time()-last_cache_init - print(elapsed) + elapsed = time.time() - last_cache_init 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): + if os.path.isfile(cachepath) : result = "" - with open(cachepath,'rb') as f: + with open(cachepath,'rb') as f : result = f.read() return result else: result = requests.get(finallink).content - with open(cachepath,'wb') as f: + with open(cachepath,'wb') as f : f.write(result) return result + 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. @@ -169,7 +172,6 @@ def get_depts(filename) : dept_list : list Liste des départements. """ - dept_list = list() dept_file = open(filename, "r") @@ -200,8 +202,8 @@ def get_tot_rooms(datet, depts, ignore_list) : datet : datetime.datetime() Date pour la recherche de salles. - dept : list - Liste des départements dans lesquel chercher des salles. + depts : list + Liste des départements dans lesquels chercher des salles. ignore_list : list Liste des noms de salles à ignorer. @@ -211,7 +213,6 @@ def get_tot_rooms(datet, depts, ignore_list) : total_rooms : list Liste des salles. """ - 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) @@ -227,7 +228,10 @@ def get_tot_rooms(datet, depts, ignore_list) : # 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)) + if datet.month < 12 : + result = sched_get(datet, d.link, datet.replace(month = datet.month + margintime)) + else : + result = sched_get(datet, d.link, datet.replace(month = 1, year = datet.year + 1)) cals.append(icalendar.Calendar.from_ical(result)) roomnames = [] # Contient le nom de toutes les salles indiquées dans la section "LOCATION" @@ -267,14 +271,14 @@ def get_tot_rooms(datet, depts, ignore_list) : def getrooms(datet, depts, ignore_list) : """ Ajout des informations supplémentaires à la liste des salles - (heures de début-fin de dispo, indicateur de dispo). + (heures de début et fin de dispo, indicateur de dispo). Parameters ---------- datet : datetime.datetime() Date pour la recherche de salles. - dept : list + depts : list Liste des départements dans lesquel chercher des salles. ignore_list : list @@ -285,7 +289,6 @@ def getrooms(datet, depts, ignore_list) : total_rooms : list Liste des salles. """ - # Création de la liste de toutes les salles : total_rooms = get_tot_rooms(datet, depts, ignore_list)