Compare commits
19 Commits
c2a2971467
...
b7f6096aaf
Author | SHA1 | Date | |
---|---|---|---|
b7f6096aaf | |||
b5820b29be | |||
0776a7c987 | |||
462180986b | |||
5cd946bac5 | |||
d296d65215 | |||
057d0f9c90 | |||
76da00161d | |||
b2517ab152 | |||
f2fc7c8775 | |||
0877cc8210 | |||
104feb31b9 | |||
17130f7c04 | |||
333d2bf3d7 | |||
a23d5735df | |||
4c567ebe56 | |||
3efa3c36be | |||
2a03dde566 | |||
09975eb013 |
12
README.md
12
README.md
@ -5,6 +5,18 @@ C'est utile aux élèves qui cherchent un coin pour travailler ou manger, comme
|
||||
|
||||
Cette application dispose d'une interface Web fonctionnant avec Flask ( [voir la demo][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
|
||||
- 🪶 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*
|
||||
|
||||
## Dépendances
|
||||
|
||||
Pour l'instant, ce programme utilise les modules suivants :
|
||||
|
25
app.py
25
app.py
@ -33,6 +33,7 @@ GLOBAL_CONTEXT = {} # Contexte constant pour les templates Jinja
|
||||
GLOBAL_CONTEXT["SOURCE"] = "https://forge.chapril.org/Wantoo/UniSquat_Python" # Le lien du code source
|
||||
GLOBAL_CONTEXT["CREDITSLINK"] = "https://forge.chapril.org/Wantoo" # Le lien de l'organisation
|
||||
GLOBAL_CONTEXT["CREDITSNAME"] = "Wantoo" # Le nom de l'organisation
|
||||
GLOBAL_CONTEXT["DEBUG"] = False # Fait en sorte que le logiciel soit un peu plus expressif
|
||||
|
||||
# Globales
|
||||
logs = [] # Stoque les différentes requêtes faite sur la route /free_rooms/, sous la forme {"timestamp":timestamp,"depts":[]}
|
||||
@ -148,6 +149,11 @@ def free_rooms() :
|
||||
else :
|
||||
time_uf = time_uf.split(":")
|
||||
|
||||
# Récupére les IDs des salles favorites
|
||||
favs_ids = request.args.getlist("favs")
|
||||
if favs_ids==None:
|
||||
favs_ids = []
|
||||
|
||||
date = dti.datetime.now()
|
||||
|
||||
date_str = "" # Date affichée sur la page (si personnalisée)
|
||||
@ -204,6 +210,7 @@ def free_rooms() :
|
||||
"end":date_tools.hour_disp(r.end),
|
||||
"rtime":remain_time_str}
|
||||
|
||||
|
||||
change_date_str = "?"
|
||||
i = 0
|
||||
for v in dident_list:
|
||||
@ -211,7 +218,23 @@ def free_rooms() :
|
||||
if i<len(dident_list)-1:
|
||||
change_date_str += "&"
|
||||
i+=1
|
||||
context = {"free_rooms":free_rooms, "frooms_disp":frooms_disp, "depts_str":depts_str, "dident_list":dident_list, "date_str":date_str, "change_date_str":change_date_str}
|
||||
|
||||
# Générer le lien pour enlever les favoris séléctionnés
|
||||
nofavslink = "/app/free-rooms?"
|
||||
for dept in dident_list:
|
||||
nofavslink+="dept="+str(dept)+"&"
|
||||
nofavslink = nofavslink[:-1] # Enlever le dernier &
|
||||
|
||||
# Trier les salles selon leurs catégories
|
||||
favs_free_rooms = []
|
||||
favs_soon_rooms = []
|
||||
soon_rooms = []
|
||||
final_rooms = []
|
||||
|
||||
for r in free_rooms:
|
||||
[[soon_rooms,final_rooms],[favs_soon_rooms,favs_free_rooms]][r.id in favs_ids][r.is_free].append(r)
|
||||
|
||||
context = {"favs_free_rooms":favs_free_rooms, "favs_soon_rooms":favs_soon_rooms, "free_rooms":final_rooms, "soon_rooms":soon_rooms, "frooms_disp":frooms_disp, "depts_str":depts_str, "dident_list":dident_list, "date_str":date_str, "change_date_str":change_date_str, "favs":len(favs_ids)>0,"nofavslink":nofavslink}
|
||||
|
||||
# Crée un log de la date et des départements demandés ( pour des futures statistiques )
|
||||
log = {}
|
||||
|
18
objects.py
18
objects.py
@ -15,6 +15,13 @@ Created on Sat May 7 17:29:11 2022
|
||||
l'Université de Strasbourg.
|
||||
"""
|
||||
|
||||
# Modules
|
||||
import random # Nécessaire pour la génération d'ID des salles
|
||||
|
||||
# Constantes
|
||||
ID_CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" # Caractères disponibles pour la création d'ID
|
||||
ID_LEN = 4 # Nombres de caractères composant l'ID
|
||||
|
||||
|
||||
### Fichier contenant les classes des salles et des départements ###
|
||||
|
||||
@ -44,6 +51,9 @@ class Room :
|
||||
|
||||
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 )
|
||||
"""
|
||||
|
||||
def __init__(self, name, start, end, is_free) :
|
||||
@ -51,6 +61,14 @@ class Room :
|
||||
self.start = start
|
||||
self.end = end
|
||||
self.is_free = is_free
|
||||
self.id = self.getId(name)
|
||||
|
||||
def getId(self,name):
|
||||
random.seed(name)
|
||||
id = ""
|
||||
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
|
||||
|
||||
|
||||
class Dept :
|
||||
|
@ -50,10 +50,6 @@ nav {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#body {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: justify;
|
||||
}
|
||||
@ -69,6 +65,13 @@ main p {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.room-row{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Style The Dropdown Button */
|
||||
.dropbtn {
|
||||
color: var(--fg);
|
||||
@ -126,6 +129,7 @@ h1, h2, h3, h4 {
|
||||
border-left: solid;
|
||||
border-width: 5px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
background: var(--bg-dark);
|
||||
display: inline-block;
|
||||
border-color: var(--hl);
|
||||
@ -145,18 +149,6 @@ header a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
img {
|
||||
margin: 10px;
|
||||
align-self: flex-start;
|
||||
max-width: 200px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
iframe {
|
||||
max-width: 200px;
|
||||
max-height: 500px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
@ -170,9 +162,10 @@ dt {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
dt .details {
|
||||
.details {
|
||||
font-weight: normal;
|
||||
font-size: 80%;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
input[type="submit"], button, .button{
|
||||
@ -211,6 +204,10 @@ input[type="checkbox"] {
|
||||
height: 0px;
|
||||
}
|
||||
|
||||
input[type=checkbox]:checked ~ .remove-check {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@ -238,11 +235,6 @@ input[type="checkbox"] + label {
|
||||
box-shadow: 5px 5px black;
|
||||
}
|
||||
|
||||
.done {
|
||||
background: var(--bg-dark);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
input {
|
||||
background: var(--bg-dark);
|
||||
}
|
||||
@ -260,6 +252,18 @@ footer {
|
||||
color: var(--bg-light);
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-width: 769px) {
|
||||
/* Medium devices (landscape tablets, 768px and up) */
|
||||
@media only screen and (min-width: 1000px) {
|
||||
.flex-pc{
|
||||
display:flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.room-collumn{
|
||||
width: 50%;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.room-collumn{
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
@ -9,45 +9,144 @@
|
||||
<body>
|
||||
{% include "base.html" %}
|
||||
<main>
|
||||
Départements sélectionnés :
|
||||
<b>{{ depts_str }}</b>
|
||||
<div class="flex">
|
||||
<p>
|
||||
Départements sélectionnés : <br>
|
||||
<b>{{ depts_str }}</b>
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex">
|
||||
{% if date_str != "" : %}
|
||||
<b>Le {{ date_str }}</b>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a class="button" href='/app/date-select{{change_date_str}}'>Choisir une date</a>
|
||||
</div>
|
||||
<h1>Disponibles maintenant</h1>
|
||||
<div class="flex-container">
|
||||
<ul>
|
||||
{% for room in free_rooms : %}
|
||||
{% if room.is_free : %}
|
||||
<dt>{{ room.name }}
|
||||
{% if not(room.end.hour == 23 and room.end.minute == 59 and room.end.second == 59) : %}
|
||||
<br><span class=details>Jusqu'à {{ frooms_disp[room.name]["end"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</span>
|
||||
{% endif %}
|
||||
</dt>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<h1>Disponibles prochainement</h1>
|
||||
<div class="flex-container">
|
||||
<ul>
|
||||
{% for room in free_rooms : %}
|
||||
{% if not room.is_free : %}
|
||||
<dt>{{ room.name }}
|
||||
{% if room.end.hour == 23 and room.end.minute == 59 and room.end.second == 59 : %}
|
||||
<br><span class=details>À {{ frooms_disp[room.name]["start"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</span>
|
||||
{% else %}
|
||||
<br><span class=details>De {{ frooms_disp[room.name]["start"] }} à {{ frooms_disp[room.name]["end"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</span>
|
||||
{% endif %}
|
||||
</dt>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% if favs: %}
|
||||
<div class="flex">
|
||||
<a class="button" href="{{ nofavslink }}">Retirer les favoris</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<form action="/app/free-rooms" method="get">
|
||||
{% if favs: %}
|
||||
<div class="flex-pc">
|
||||
{% if favs_free_rooms|length>0: %}
|
||||
<div class="room-collumn">
|
||||
<br>
|
||||
<h1>Favoris disponibles maintenant</h1>
|
||||
<div class="flex-container">
|
||||
<ul>
|
||||
{% for room in favs_free_rooms : %}
|
||||
<dt>
|
||||
<div class="room-row">
|
||||
<div>
|
||||
{{ room.name }} {% if DEBUG :%}( {{ room.id }} ){% endif %}
|
||||
{% if not(room.end.hour == 23 and room.end.minute == 59 and room.end.second == 59) : %}
|
||||
<p class=details>Jusqu'à {{ frooms_disp[room.name]["end"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<input class="fav" type="checkbox" id="{{ room.id }}" name="favs" value="{{ room.id }}" checked> <label style="width:30px;height:30px" for="{{ room.id }}">⭐</label>
|
||||
</div>
|
||||
</div>
|
||||
</dt>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if favs_soon_rooms|length>0: %}
|
||||
<div class="room-collumn">
|
||||
<br>
|
||||
<h1>Favoris disponibles prochainement</h1>
|
||||
<div class="flex-container">
|
||||
<ul>
|
||||
{% for room in favs_soon_rooms: %}
|
||||
<dt>
|
||||
<div class="room-row">
|
||||
<div>
|
||||
{{ room.name }} {% if DEBUG :%}( {{ room.id }} ){% endif %}
|
||||
{% if room.end.hour == 23 and room.end.minute == 59 and room.end.second == 59 : %}
|
||||
<p class=details>À {{ frooms_disp[room.name]["start"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p>
|
||||
{% else %}
|
||||
<p class=details>De {{ frooms_disp[room.name]["start"] }} à {{ frooms_disp[room.name]["end"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<input class="fav" type="checkbox" id="{{ room.id }}" name="favs" value="{{ room.id }}" checked> <label style="width:30px;height:30px" for="{{ room.id }}">⭐</label>
|
||||
</div>
|
||||
</div>
|
||||
</dt>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="flex-pc">
|
||||
{% if free_rooms|length>0 %}
|
||||
<div class="room-collumn">
|
||||
<br>
|
||||
<h1>Disponibles maintenant</h1>
|
||||
<div class="flex-container">
|
||||
<ul>
|
||||
{% for room in free_rooms: %}
|
||||
<dt>
|
||||
<div class="room-row">
|
||||
<div>
|
||||
{{ room.name }} {% if DEBUG :%}( {{ room.id }} ){% endif %}
|
||||
{% if not(room.end.hour == 23 and room.end.minute == 59 and room.end.second == 59) : %}
|
||||
<p class=details>Jusqu'à {{ frooms_disp[room.name]["end"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<input class="fav" type="checkbox" id="{{ room.id }}" name="favs" value="{{ room.id }}"> <label style="width:30px;height:30px" for="{{ room.id }}">⭐</label>
|
||||
</div>
|
||||
</div>
|
||||
</dt>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if soon_rooms|length>0 %}
|
||||
<div class="room-collumn">
|
||||
<br>
|
||||
<h1>Disponibles prochainement</h1>
|
||||
<div class="flex-container">
|
||||
<ul>
|
||||
{% for room in soon_rooms: %}
|
||||
<dt>
|
||||
<div class="room-row">
|
||||
<div>
|
||||
{{ room.name }} {% if DEBUG :%}( {{ room.id }} ){% endif %}
|
||||
{% if room.end.hour == 23 and room.end.minute == 59 and room.end.second == 59 : %}
|
||||
<p class=details>À {{ frooms_disp[room.name]["start"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p>
|
||||
{% else %}
|
||||
<p class=details>De {{ frooms_disp[room.name]["start"] }} à {{ frooms_disp[room.name]["end"] }} (dans {{ frooms_disp[room.name]["rtime"] }})</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<input class="fav" type="checkbox" id="{{ room.id }}" name="favs" value="{{ room.id }}"> <label style="width:30px;height:30px" for="{{ room.id }}">⭐</label>
|
||||
</div>
|
||||
</div>
|
||||
</dt>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="flex">
|
||||
{% for d in dident_list : %} <!-- Magie noire pour conserver les départements séléctionnés -->
|
||||
<span style="display: none;"><input type="text" name="dept" value="{{ d }}"/></span>
|
||||
{% endfor %}
|
||||
|
||||
<input type="submit" value="Valider les favoris">
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
{% include "footer.html" %}
|
||||
</body>
|
||||
|
Loading…
Reference in New Issue
Block a user