-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathVacantRoomFinder.py
225 lines (178 loc) · 10.4 KB
/
VacantRoomFinder.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
import requests, json, html
from datetime import datetime, timedelta
import re
greenTick = '<:greenTick:876779478733455360>'
redTick = '<:redTick:876779478410465291>'
batiments = {
"Germain": [
["G001 - GERMAIN", "G002 - GERMAIN", "G003 - GERMAIN", "G007 - GERMAIN"],
["G101 - GERMAIN", "G103 - GERMAIN", "G104 - GERMAIN", "G105 - GERMAIN", "G107 - GERMAIN"],
["G201 - GERMAIN", "G203 - GERMAIN", "G204 - GERMAIN", "G205 - GERMAIN", "G206 - GERMAIN", "G207 - GERMAIN", "G209 - GERMAIN", "G210 - GERMAIN"]
],
"Fermat": [
["2102 - FERMAT", "2104 - FERMAT", "2105 - FERMAT", "2106 - FERMAT", "2107 - FERMAT"],
["2201 - FERMAT", "2202 - FERMAT", "2203 - FERMAT", "2204 - FERMAT", "2205 - FERMAT", "2206 - FERMAT"]
],
"Descartes": [
]
}
# TODO: Ajouter les salles Descartes (Jungle, Alsace, etc.) + possibilité de lancer la recherche sur un seul batiment
def get_room_edt(room, day):
""" Requête POST sur CELCAT pour obtenir l'emploi du temps JSON d'une salle pour une jour donné """
url = 'https://edt.uvsq.fr/Home/GetCalendarData'
data = {'start':day,'end':day,'resType':'102','calView':'agendaDay','federationIds[]':room}
response = requests.post(url,data=data)
bytes_value = response.content.decode('utf8')
data = json.loads(bytes_value)
return data
def check_if_empty(room, moment):
""" Renvoie False si la @room est occupé au @moment donné,
et True avec la data du prochain créneau si elle est vacante """
# data = get_room_edt(room, moment.strftime("%Y-%m-%d"))
data = get_room_edt(room, f"{moment.year}-{moment.month}-{moment.day}")
until = None
for module_data in data:
debut = datetime.strptime(module_data["start"], "%Y-%m-%dT%H:%M:%S")
fin = datetime.strptime(module_data["end"], "%Y-%m-%dT%H:%M:%S")
description = html.unescape(module_data["description"])
description = description.replace("\n", "").replace("\r", "")
description = description.replace("<br />", "¨").split("¨")[2]
sub_data = {
"jour":f'{debut.day}/{debut.month}',
"debut":debut,
"module":description
}
# print(sub_data)
# Salle occupée: on renvoie Faux et on quitte la fonction
if moment > debut and moment < fin:
return False, None
# Sinon: on ajoute la data du dernier module à Until
elif moment < debut and (until is None or debut < until['debut']):
until = sub_data
# Salle vacante. Until = None si vacante toute la journée (aucun créneau après)
return True, until
def find_all_rooms(moment, batiment):
""" Renvoie l'ensemble des salles vacantes pour un @moment donné """
print(f"Requesting for {moment}")
output = []
nb_libres = 0
for floor in batiment:
floor_output = []
for room in floor:
empty, until = check_if_empty(room, moment) # until = data du créneau qui occupe la salle
if empty:
floor_output.append([room, until])
nb_libres += 1
output.append(floor_output.copy())
return nb_libres, output
def match_datetime(moment):
""" Renvoie la date formatée, si celle-ci matche. """
try:
match = re.compile(r'\d{1,2}-\d{1,2} \d{1,2}:\d{2}').search(moment) # Search is used otherwise, match can't find it if garbage chars surrounding date.
if match: return(datetime.strptime(f"{datetime.now().year}_{moment[match.start():match.end()]}", '%Y_%m-%d %H:%M'))
match = re.compile(r'\d{1,2}-\d{1,2} \d{1,2}h\d{2}').search(moment)
if match: return(datetime.strptime(f"{datetime.now().year}_{moment[match.start():match.end()]}", '%Y_%m-%d %Hh%M'))
match = re.compile(r'\d{1,2}\/\d{1,2} \d{1,2}:\d{2}').search(moment)
if match: return(datetime.strptime(f"{datetime.now().year}_{moment[match.start():match.end()]}", '%Y_%m/%d %H:%M'))
match = re.compile(r'\d{1,2}\/\d{1,2} \d{1,2}h\d{2}').search(moment)
if match: return(datetime.strptime(f"{datetime.now().year}_{moment[match.start():match.end()]}", '%Y_%m/%d %Hh%M'))
match = re.compile(r'\d{4}-\d{1,2}-\d{1,2}').search(moment)
if match: return(datetime.strptime(moment[match.start():match.end()], '%Y-%m-%d'))
match = re.compile(r'\d{2}-\d{1,2}-\d{1,2}').search(moment)
if match: return(datetime.strptime(moment[match.start():match.end()], '%Y-%m-%d'))
match = re.compile(r'\d{4}\/\d{1,2}\/\d{1,2}').search(moment)
if match: return(datetime.strptime(moment[match.start():match.end()], '%Y/%m/%d'))
match = re.compile(r'\d{2}\/\d{1,2}\/\d{1,2}').search(moment)
if match: return(datetime.strptime(moment[match.start():match.end()], '%Y/%m/%d'))
match = re.compile(r'\d{1,2}-\d{1,2}').search(moment)
if match: return(datetime.strptime(f"{datetime.now().year}_{moment[match.start():match.end()]}", '%Y_%m-%d'))
match = re.compile(r'\d{1,2}\/\d{1,2}').search(moment)
if match: return(datetime.strptime(f"{datetime.now().year}_{moment[match.start():match.end()]}", '%Y_%m/%d'))
except Exception as error:
print(f'\033[91mError: <match_datetime({moment})> {error}\033[0m')
return False
return False
# Discord Bot Part
import discord
from discord.ext import commands
class FreeRoomFinder(commands.Cog):
"""
Outil de recherche d'une salle disponible pour un moment donné
"""
def __init__(self, bot):
super().__init__()
self.bot = bot
@commands.command(name='findroom')
async def find_room(self, ctx, *moments):
moment = " ".join(moments) # Pour accepter les formats en deux parties (ex 10/04 11h40)
# TODO: ajouter slash_commands avec choix pour les batiments ? solution pour le @moment ?
if not moment:
moment = datetime.now() + timedelta(hours=2) # VPS UTC
discord_moment = "En ce moment"
discord_moment2 = "en ce moment"
else:
# TODO: accepter tous les formats d'horaires (HHhMM, HhMM, HH:MM, DD/MM/YY HH:MM, etc.) - Regex ou lib existante ?
# moment = datetime.datetime.strptime(moment, '%Y-%m-%d')
# moment = datetime.strptime(moment, "%d/%m/%Y %H:%M")
res = match_datetime(moment)
if not res:
await ctx.send(f"Désolé, mais la date n'a pas été reconnue.\n**Syntaxe:** `:findroom [mois/jour heure:minutes]`")
return
else:
moment = res
discord_moment = f"<t:{int(moment.timestamp())}:D>"
discord_moment2 = "pour " + discord_moment
# Check dimanche
if moment.date().isoweekday == 7:
await ctx.send(f"L'université est fermée le Dimanche, aucune salle ne peut être disponible {discord_moment2}.\n**Syntaxe:** `:findroom [mois/jour heure:minutes]`")
return
nb_libres_g, output_g = find_all_rooms(moment, batiments["Germain"])
nb_libres_f, output_f = find_all_rooms(moment, batiments["Fermat"])
if len(output_g) == 0 and len(output_f) == 0: # Aucune salle libre nulle part
em = discord.Embed(title=f"<:week:755154675149439088> ViteMaSalle — {discord_moment} (Germain & Fermat)",
description=f"{redTick} Aucune salle libre n'est malheureusement disponible {discord_moment2} dans les **bâtiments Germain et Fermat.**",
color=0xcd5c5c, timestamp=datetime.utcnow())
else:
em_desc = ""
if nb_libres_g == 0:
em_desc = f"{greenTick} **{nb_libres_f} salles** sont disponibles {discord_moment2} dans le **bâtiment Fermat**."
em_desc += f"{redTick} Aucune salle libre n'est malheureusement disponible dans le **bâtiment Germain.**\n"
if nb_libres_f == 0:
em_desc = f"{greenTick} **{nb_libres_g} salles** sont disponibles {discord_moment2} dans le **bâtiment Germain**."
em_desc += f"{redTick} Aucune salle libre n'est malheureusement disponible dans le **bâtiment Fermat.**\n"
if nb_libres_f != 0 and nb_libres_g != 0:
em_desc = f"{greenTick} **{nb_libres_g} salles** sont disponibles {discord_moment2} dans le **bâtiment Germain**.\n"
em_desc += f"{greenTick} **{nb_libres_f} salles** sont disponibles {discord_moment2} dans le **bâtiment Fermat**."
em = discord.Embed(title=f"<:week:755154675149439088> ViteMaSalle — {discord_moment} (Germain & Fermat)",
description=em_desc,
color=0xb2e4d7, timestamp=datetime.utcnow())
for i, floor in enumerate(output_g):
if len(floor):
em_name = f"🟣 Germain — Étage {i}" if i > 0 else "🟣 Germain — Rez-de-chaussée"
em_value = ""
for room in floor:
if room[1]:
room[1]['debut'] = "{:0>2d}:{:0>2d}".format(room[1]['debut'].hour, room[1]['debut'].minute)
em_value += f"{greenTick} Salle `{room[0][:4]}` — Jusqu'à **{room[1]['debut']}**\n"
else:
em_value += f"{greenTick} Salle `{room[0][:4]}` — **Toute la journée**\n"
em.add_field(name=em_name, value=em_value, inline=False)
for i, floor in enumerate(output_f):
if len(floor):
em_name = f"🟤 Fermat — Étage {i}" if i > 0 else "🟤 Fermat — Rez-de-chaussée"
em_value = ""
for room in floor:
if room[1]:
room[1]['debut'] = "{:0>2d}:{:0>2d}".format(room[1]['debut'].hour, room[1]['debut'].minute)
em_value += f"{greenTick} Salle `{room[0][:4]}` — Jusqu'à **{room[1]['debut']}**\n"
else:
em_value += f"{greenTick} Salle `{room[0][:4]}` — **Toute la journée**\n"
em.add_field(name=em_name, value=em_value, inline=False)
await ctx.send(embed=em)
@find_room.error
async def test_on_error(self, ctx, error):
""" Sends the suitable error message to user """
print(f'\033[91mError: <{ctx.command}> {error}\033[0m')
await ctx.send("ERR - `{}`".format(error))
def setup(bot):
bot.add_cog(FreeRoomFinder(bot))