From 96aaca97462f24ecf2db305bc859fd0c470ee994 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 3 Jan 2024 14:43:26 +0100 Subject: [PATCH 1/3] =?UTF-8?q?EDT:=20normalise=20identifiants=20pour=20am?= =?UTF-8?q?=C3=A9liorer=20correspondance=20ics/ScoDoc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/formsemestre.py | 5 ++++- app/models/groups.py | 7 +++++-- app/models/moduleimpls.py | 9 ++++----- app/models/modules.py | 5 ++++- app/scodoc/sco_edt_cal.py | 24 +++++++++++++++--------- app/scodoc/sco_utils.py | 12 ++++++++++++ 6 files changed, 44 insertions(+), 18 deletions(-) diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 994e201d2..4506eed01 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -280,7 +280,10 @@ class FormSemestre(db.Model): raise ScoValueError("Le semestre n'a pas de groupe par défaut") def get_edt_ids(self) -> list[str]: - "l'ids pour l'emploi du temps: à défaut, les codes étape Apogée" + """Les ids pour l'emploi du temps: à défaut, les codes étape Apogée. + Les edt_id de formsemestres ne sont pas normalisés afin de contrôler + précisément l'accès au fichier ics. + """ return ( scu.split_id(self.edt_id) or [e.etape_apo.strip() for e in self.etapes if e.etape_apo] diff --git a/app/models/groups.py b/app/models/groups.py index 6c112058f..7250f1e67 100644 --- a/app/models/groups.py +++ b/app/models/groups.py @@ -251,8 +251,11 @@ class GroupDescr(ScoDocModel): return d def get_edt_ids(self) -> list[str]: - "les ids pour l'emploi du temps: à défaut, le nom scodoc du groupe" - return scu.split_id(self.edt_id) or [self.group_name] or [] + "les ids normalisés pour l'emploi du temps: à défaut, le nom scodoc du groupe" + return [ + scu.normalize_edt_id(x) + for x in scu.split_id(self.edt_id) or [self.group_name] or [] + ] def get_nb_inscrits(self) -> int: """Nombre inscrits à ce group et au formsemestre. diff --git a/app/models/moduleimpls.py b/app/models/moduleimpls.py index a98fcf0f2..b674ed996 100644 --- a/app/models/moduleimpls.py +++ b/app/models/moduleimpls.py @@ -67,11 +67,10 @@ class ModuleImpl(db.Model): def get_edt_ids(self) -> list[str]: "les ids pour l'emploi du temps: à défaut, les codes Apogée" - return ( - scu.split_id(self.edt_id) - or scu.split_id(self.code_apogee) - or self.module.get_edt_ids() - ) + return [ + scu.normalize_edt_id(x) + for x in scu.split_id(self.edt_id) or scu.split_id(self.code_apogee) + ] or self.module.get_edt_ids() def get_evaluations_poids(self) -> pd.DataFrame: """Les poids des évaluations vers les UE (accès via cache)""" diff --git a/app/models/modules.py b/app/models/modules.py index 0410395ca..430835a91 100644 --- a/app/models/modules.py +++ b/app/models/modules.py @@ -292,7 +292,10 @@ class Module(db.Model): def get_edt_ids(self) -> list[str]: "les ids pour l'emploi du temps: à défaut, le 1er code Apogée" - return scu.split_id(self.edt_id) or scu.split_id(self.code_apogee) or [] + return [ + scu.normalize_edt_id(x) + for x in scu.split_id(self.edt_id) or scu.split_id(self.code_apogee) or [] + ] def get_parcours(self) -> list[ApcParcours]: """Les parcours utilisant ce module. diff --git a/app/scodoc/sco_edt_cal.py b/app/scodoc/sco_edt_cal.py index 8f3c07152..7440de935 100644 --- a/app/scodoc/sco_edt_cal.py +++ b/app/scodoc/sco_edt_cal.py @@ -343,7 +343,7 @@ def load_and_convert_ics(formsemestre: FormSemestre) -> tuple[list[dict], list[s group_name: _COLOR_PALETTE[i % (len(_COLOR_PALETTE) - 1) + 1] for i, group_name in enumerate(edt2group) } - edt_groups_ids = set() # les ids de groupes tels que dans l'ics + edt_groups_ids = set() # les ids de groupes normalisés tels que dans l'ics default_group = formsemestre.get_default_group() edt2modimpl = formsemestre_retreive_modimpls_from_edt_id(formsemestre) edt2user: dict[str, User | None] = {} # construit au fur et à mesure (cache) @@ -442,14 +442,17 @@ def extract_event_data( ics_field: str, pattern: re.Pattern, none_if_no_match=False, -) -> str: - """Extrait la chaine (id) de l'évènement.""" +) -> str | None: + """Extrait la chaine (id) de l'évènement et la normalise. + Si l'event n'a pas le champs: "-" + Si pas de match: None + """ if not event.has_key(ics_field): return "-" data = event.decoded(ics_field).decode("utf-8") # assume ics in utf8 m = pattern.search(data) if m and len(m.groups()) > 0: - return m.group(1) + return scu.normalize_edt_id(m.group(1)) # fallback: if not none_if_no_match, ics field complete return None if none_if_no_match else data @@ -457,7 +460,7 @@ def extract_event_data( def formsemestre_retreive_modimpls_from_edt_id( formsemestre: FormSemestre, ) -> dict[str, ModuleImpl]: - """Construit un dict donnant le moduleimpl de chaque edt_id""" + """Construit un dict donnant le moduleimpl de chaque edt_id (normalisé)""" edt2modimpl = {} for modimpl in formsemestre.modimpls: for edt_id in modimpl.get_edt_ids(): @@ -469,10 +472,13 @@ def formsemestre_retreive_modimpls_from_edt_id( def formsemestre_retreive_groups_from_edt_id( formsemestre: FormSemestre, ) -> dict[str, GroupDescr]: - """Construit un dict donnant le groupe de chaque edt_id""" + """Construit un dict donnant le groupe de chaque edt_id + La clé edt_id est sans accents, lowercase. + """ edt2group = {} + group: GroupDescr for partition in formsemestre.partitions: - for g in partition.groups: - for edt_id in g.get_edt_ids(): - edt2group[edt_id] = g + for group in partition.groups: + for edt_id in group.get_edt_ids(): + edt2group[edt_id] = group return edt2group diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index 7f23f8465..62e832001 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -841,6 +841,18 @@ def suppress_accents(s): return s # may be int +def normalize_edt_id(edt_id: str) -> str: + """Normalize les identifiants edt pour faciliter la correspondance + entre les identifiants ScoDoc et ceux dans l'ics: + Passe tout en majuscules sans accents ni espaces. + """ + return ( + None + if edt_id is None + else suppress_accents(edt_id or "").upper().replace(" ", "") + ) + + class PurgeChars: """delete all chars except those belonging to the specified string""" From 6423baa34bef56c98acb1a3d273bab95632568e6 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 3 Jan 2024 22:11:49 +0100 Subject: [PATCH 2/3] =?UTF-8?q?EDT:=20gestion=20de=20plusieurs=20enseignan?= =?UTF-8?q?ts=20par=20=C3=A9v=C3=A8nement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/forms/main/config_assiduites.py | 11 ++-- app/scodoc/sco_edt_cal.py | 78 ++++++++++++++++++++--------- tools/edt/edt_ens.py | 26 ++++++---- 3 files changed, 75 insertions(+), 40 deletions(-) diff --git a/app/forms/main/config_assiduites.py b/app/forms/main/config_assiduites.py index 16b6f1279..a2130bee9 100644 --- a/app/forms/main/config_assiduites.py +++ b/app/forms/main/config_assiduites.py @@ -192,15 +192,16 @@ class ConfigAssiduitesForm(FlaskForm): validators=[Optional(), check_ics_regexp], ) edt_ics_uid_field = StringField( - label="Champ contenant l'enseignant", + label="Champ contenant les enseignants", description="""champ de l'évènement calendrier: DESCRIPTION, SUMMARY, ...""", validators=[Optional(), check_ics_field], ) edt_ics_uid_regexp = StringField( - label="Extraction de l'enseignant", - description=r"""expression régulière python dont le premier groupe doit - correspondre à l'identifiant (edt_id) de l'enseignant associé à l'évènement. - Exemple: Enseignant : ([0-9]+) + label="Extraction des enseignants", + description=r"""expression régulière python permettant d'extraire les + identifiants des enseignants associés à l'évènement. + (contrairement aux autres champs, il peut y avoir plusieurs enseignants par évènement.) + Exemple: [0-9]+ """, validators=[Optional(), check_ics_regexp], ) diff --git a/app/scodoc/sco_edt_cal.py b/app/scodoc/sco_edt_cal.py index 7440de935..e182b27f7 100644 --- a/app/scodoc/sco_edt_cal.py +++ b/app/scodoc/sco_edt_cal.py @@ -132,7 +132,8 @@ def load_calendar( ) from exc except FileNotFoundError as exc: log( - f"formsemestre_load_calendar: ics not found for {formsemestre or ''}\npath='{ics_filename}'" + f"""formsemestre_load_calendar: ics not found for { + formsemestre or ''}\npath='{ics_filename}'""" ) raise ScoValueError( f"Fichier ics introuvable (filename={ics_filename})" @@ -229,7 +230,8 @@ def formsemestre_edt_dict( scu.EMO_WARNING} {event['edt_module']}""" bubble = "code module non trouvé dans ScoDoc. Vérifier configuration." case _: # module EDT bien retrouvé dans ScoDoc - bubble = f"{modimpl.module.abbrev or modimpl.module.titre or ''} ({event['edt_module']})" + bubble = f"""{modimpl.module.abbrev or modimpl.module.titre or '' + } ({event['edt_module']})""" mod_disp = ( f"""{modimpl.module.code}""" ) @@ -249,20 +251,24 @@ def formsemestre_edt_dict( else "" ) - ens_user_name = event["ens"].user_name if event["ens"] else None - ens_nomprenom = event["ens"].get_nomprenom() if event["ens"] else None + ens_nomprenoms = ( + "(" + ", ".join([u.get_nomprenom() for u in event["users"]]) + ")" + if event["users"] + else "" + ) + ens_user_names = ( + ",".join([u.user_name for u in event["users"]]) if event["users"] else "" + ) d = { # Champs utilisés par tui.calendar "calendarId": "cal1", - "title": f"""{title} {group_disp} { - '('+ens_nomprenom+')' if ens_nomprenom else '' - } {link_abs}""", + "title": f"""{title} {group_disp} {ens_nomprenoms} {link_abs}""", "start": event["start"], "end": event["end"], "backgroundColor": event["group_bg_color"], # Infos brutes pour usage API éventuel - "ens_edt": event["edt_ens"], - "ens_user_name": ens_user_name, + "edt_ens_ids": event["edt_ens_ids"], + "ens_user_names": ens_user_names, "group_id": group.id if group else None, "group_edt_id": event["edt_group"], "moduleimpl_id": modimpl.id if modimpl else None, @@ -354,7 +360,7 @@ def load_and_convert_ics(formsemestre: FormSemestre) -> tuple[list[dict], list[s if "DESCRIPTION" in event: # --- Titre de l'évènement title_edt = ( - extract_event_data(event, edt_ics_title_field, edt_ics_title_pattern) + extract_event_edt_id(event, edt_ics_title_field, edt_ics_title_pattern) if edt_ics_title_pattern else "non configuré" ) @@ -362,7 +368,7 @@ def load_and_convert_ics(formsemestre: FormSemestre) -> tuple[list[dict], list[s title = title_edt # --- Group if edt_ics_group_pattern: - edt_group = extract_event_data( + edt_group = extract_event_edt_id( event, edt_ics_group_field, edt_ics_group_pattern ) edt_groups_ids.add(edt_group) @@ -385,7 +391,7 @@ def load_and_convert_ics(formsemestre: FormSemestre) -> tuple[list[dict], list[s # --- ModuleImpl if edt_ics_mod_pattern: - edt_module = extract_event_data( + edt_module = extract_event_edt_id( event, edt_ics_mod_field, edt_ics_mod_pattern ) modimpl: ModuleImpl = edt2modimpl.get(edt_module, None) @@ -394,19 +400,22 @@ def load_and_convert_ics(formsemestre: FormSemestre) -> tuple[list[dict], list[s else: modimpl = False edt_module = "" - # --- Enseignant + # --- Enseignants + users: list[User] = [] if edt_ics_uid_pattern: - edt_ens = extract_event_data( + ens_edt_ids = extract_event_edt_ids( event, edt_ics_uid_field, edt_ics_uid_pattern ) - if edt_ens in edt2user: - ens = edt2user[edt_ens] - else: - ens = User.query.filter_by(edt_id=edt_ens).first() - edt2user[edt_ens] = ens + for ens_edt_id in ens_edt_ids: + if ens_edt_id in edt2user: + ens = edt2user[ens_edt_id] + else: + ens = User.query.filter_by(edt_id=ens_edt_id).first() + edt2user[ens_edt_id] = ens + if ens: + users.append(ens) else: - ens = None - edt_ens = "" + ens_edt_ids = [] # events_sco.append( { @@ -418,8 +427,8 @@ def load_and_convert_ics(formsemestre: FormSemestre) -> tuple[list[dict], list[s "modimpl": modimpl, # False si extracteur non configuré "edt_module": edt_module, # id module edt non traduit # Enseignant - "edt_ens": edt_ens, # id ens edt, non traduit - "ens": ens, + "edt_ens_ids": ens_edt_ids, # ids ens edt, normalisés mais non traduits + "users": users, # heures pour saisie abs: en heure LOCALE DU SERVEUR "heure_deb": event.decoded("dtstart") .replace(tzinfo=timezone.utc) @@ -437,14 +446,14 @@ def load_and_convert_ics(formsemestre: FormSemestre) -> tuple[list[dict], list[s return events_sco, sorted(edt_groups_ids) -def extract_event_data( +def extract_event_edt_id( event: icalendar.cal.Event, ics_field: str, pattern: re.Pattern, none_if_no_match=False, ) -> str | None: """Extrait la chaine (id) de l'évènement et la normalise. - Si l'event n'a pas le champs: "-" + Si l'event n'a pas le champ: "-" Si pas de match: None """ if not event.has_key(ics_field): @@ -457,6 +466,25 @@ def extract_event_data( return None if none_if_no_match else data +def extract_event_edt_ids( + event: icalendar.cal.Event, + ics_field: str, + pattern: re.Pattern, +) -> list[str] | None: + """Extrait les edt_id de l'évènement et les normalise. + Si l'event n'a pas le champ: None + Si pas de match: liste vide + Utilisé pour les enseignants uniquement. + """ + if not event.has_key(ics_field): + return + data = event.decoded(ics_field).decode("utf-8") # assume ics in utf8 + matches = pattern.findall(data) + # nota: pattern may have zero or one group, so the result + # is a list of strings, not a list of matches + return [scu.normalize_edt_id(m) for m in matches if m] + + def formsemestre_retreive_modimpls_from_edt_id( formsemestre: FormSemestre, ) -> dict[str, ModuleImpl]: diff --git a/tools/edt/edt_ens.py b/tools/edt/edt_ens.py index d80dfc48f..8206cc030 100644 --- a/tools/edt/edt_ens.py +++ b/tools/edt/edt_ens.py @@ -86,17 +86,23 @@ def generate_ens_calendars(): nb_events += len(events) ens: User | None = None for event in events: - edt_ens = sco_edt_cal.extract_event_data( - event, edt_ics_uid_field, edt_ics_uid_pattern, none_if_no_match=True + ens_edt_ids = sco_edt_cal.extract_event_edt_ids( + event, edt_ics_uid_field, edt_ics_uid_pattern ) - if edt_ens in edt2user: - ens = edt2user[edt_ens] - else: - ens = User.query.filter_by(edt_id=edt_ens).first() - edt2user[edt_ens] = ens - if ens: # si l'utilisateur est reconnu - event.add("X-ScoDoc-user", ens.user_name) - edt_by_uid[edt_ens].add_component(event) + users = [] + for ens_edt_id in ens_edt_ids: + if ens_edt_id in edt2user: + ens = edt2user[ens_edt_id] + else: + ens = User.query.filter_by(edt_id=ens_edt_id).first() + edt2user[ens_edt_id] = ens + if ens: + users.append(ens) + edt_by_uid[ens_edt_id].add_component(event) + + if users: # si un utilisateur est reconnu dans l'event + event.add("X-ScoDoc-users", ",".join([u.user_name for u in users])) + _write_user_calendars(edt_by_uid) log( f"""generate_ens_calendars: done in {(time.time()-t0):g}s, processed { From fb62904cc9db2271b5628021e860ce0bbff30da4 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 5 Jan 2024 14:10:12 +0100 Subject: [PATCH 3/3] EDT: front: navigation et dates --- app/static/css/edt.css | 61 ++++++++++++++- app/templates/formsemestre/edt.j2 | 122 +++++++++++++++++++++++++++--- app/views/notes_formsemestre.py | 2 + 3 files changed, 173 insertions(+), 12 deletions(-) diff --git a/app/static/css/edt.css b/app/static/css/edt.css index 1564596d1..86ad9f4c0 100644 --- a/app/static/css/edt.css +++ b/app/static/css/edt.css @@ -1,8 +1,67 @@ -#show_modules_titles_form { +#calendar_control_form { display: inline-block; margin-left: 16px; } +/* Style for the dropdown button */ +.dropdown { + position: relative; + display: inline-block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.dropbtn { + background-color: rgb(233,233,233); + color: black; + padding: 2px 32px 2px 4px; + margin-left: 16px; + font-size: 16px; + border: 1px solid black; + border-radius: 5px; /* Rounded corners */ + cursor: pointer; + /* Add arrow to the button */ + background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2212%22%20height%3D%227%22%20viewBox%3D%220%200%2012%207%22%20fill%3D%22none%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Cpath%20d%3D%22M1%201L6%206L11%201%22%20stroke%3D%22black%22%20stroke-width%3D%222%22/%3E%3C/svg%3E'); + background-repeat: no-repeat; + background-position: right 10px center; +} + +/* Dropdown content (hidden by default) */ +.dropdown-content { + display: none; + position: absolute; + background-color: #f9f9f9; + min-width: 210px; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + z-index: 1; + border-radius: 5px; /* Rounded corners */ + overflow: hidden; /* Ensures rounded corners for dropdown items */ +} + +/* Style for the dropdown items */ +.dropdown-content ul { + list-style: none; + padding: 0; + margin: 0; +} + +.dropdown-content ul li { + color: black; + padding: 12px 16px; + text-decoration: none; + display: block; +} + +.dropdown-content ul li a { + color: black; + text-decoration: none; + display: block; +} + +.dropdown-content ul li label { + cursor: pointer; + display: block; +} + .toastui-calendar-template-time { padding: 4px; word-break: break-all; diff --git a/app/templates/formsemestre/edt.j2 b/app/templates/formsemestre/edt.j2 index 55dba3222..fab72f51b 100644 --- a/app/templates/formsemestre/edt.j2 +++ b/app/templates/formsemestre/edt.j2 @@ -16,11 +16,29 @@ {{ form_groups_choice|safe }} -
- noms complets des modules -
+ +
+ + + +
@@ -35,7 +53,7 @@
-
+
    @@ -73,10 +91,23 @@ function getDataAction(target) { return target.dataset ? target.dataset.action : target.getAttribute('data-action'); } +function getNextDayISODate(isoDate) { + // Parse the ISO date string into a Date object + const date = new Date(isoDate); + + // Add one day + date.setDate(date.getDate() + 1); + + // Convert back to ISO date string (YYYY-MM-DD) + return date.toISOString().split('T')[0]; +} + +var calendar; + document.addEventListener('DOMContentLoaded', function() { document.getElementById('menu-navi').addEventListener('click', onClickNavi); const Calendar = tui.Calendar; - const container = document.getElementById('calendar'); + const container = document.getElementById('formsemestre-calendar'); const options = { defaultView: 'week', calendars: [ @@ -118,7 +149,7 @@ document.addEventListener('DOMContentLoaded', function() { }, }; - const calendar = new Calendar(container, options); + calendar = new Calendar(container, options); fetch(`${SCO_URL}/../api/formsemestre/{{formsemestre.id}}/edt?{{groups_query_args|safe}}&show_modules_titles={{show_modules_titles}}`) .then(r=>{return r.json()}) @@ -131,6 +162,12 @@ document.addEventListener('DOMContentLoaded', function() { } }); + {% if current_date %} + // we need to add one day because our weeks are starting on Monday + calendar.setDate( getNextDayISODate("{{current_date}}") ); + {% endif %} + changeCalendarDate(); + function formatDate(date) { let year = date.getFullYear(); let month = (date.getMonth() + 1).toString().padStart(2, '0'); // Months are zero-indexed in JavaScript @@ -145,10 +182,17 @@ document.addEventListener('DOMContentLoaded', function() { var html = []; if (viewName === 'day') { - html.push(currentCalendarDate('YYYY.MM.DD')); + html.push(calendar.getDate().toDate().toLocaleString('fr-Fr', { + year: 'numeric', + month: 'long', + day: 'numeric'})); } else if (viewName === 'month' && (!options.month.visibleWeeksCount || options.month.visibleWeeksCount > 4)) { - html.push(currentCalendarDate('YYYY.MM')); + html.push( + calendar.getDate().toDate().toLocaleString('fr-Fr', { + year: 'numeric', + month: 'long', + })); } else { html.push(formatDate(calendar.getDateRangeStart())); html.push(' - '); @@ -173,9 +217,65 @@ document.addEventListener('DOMContentLoaded', function() { return; } - setRenderRangeText(); // setSchedules(); + changeCalendarDate(); } + // Update current URL when date change (newDate=ISO string) + function updateCurrentDateInUrl(newDate) { + // Parse the current URL + const currentUrl = new URL(window.location.href); + + // Access and modify the search parameters + const searchParams = currentUrl.searchParams; + searchParams.set('current_date', newDate); // Set the new date + + // Create the new URL + const newUrl = `${currentUrl.origin}${currentUrl.pathname}?${searchParams.toString()}`; + + // Update the URL without reloading the page + window.history.pushState({ path: newUrl }, '', newUrl); + } + // Update "current" date (URL and title) + function changeCalendarDate() { + setRenderRangeText(); + // current calendar date, ISO, without time + const iso_date = calendar.getDateRangeStart().toDate().toISOString().split('T')[0]; + updateCurrentDateInUrl(iso_date); + calendar_control_form.current_date = iso_date; + } + // View menu + const dropdown = document.querySelector('.dropdown'); + const dropbtn = document.querySelector('.dropbtn'); + const dropdownContent = document.querySelector('.dropdown-content'); + + dropbtn.addEventListener('click', function(event) { + dropdownContent.style.display = dropdownContent.style.display === 'block' ? 'none' : 'block'; + event.stopPropagation(); + }); + + document.querySelectorAll('.dropdown-content a').forEach(item => { + item.addEventListener('click', function(e) { + e.preventDefault(); + const selectedText = this.textContent; + const selectedView = this.getAttribute('data-view'); + calendar.changeView(selectedView); // Change the calendar view + dropbtn.textContent = selectedText; // Update the button text + dropdownContent.style.display = 'none'; + }); + }); + + const showModulesTitlesCheckbox = document.getElementById('showModulesTitles'); + showModulesTitlesCheckbox.addEventListener('change', function() { + calendar_control_form.show_modules_titles.value = (calendar_control_form.show_modules_titles.value=="1") ? "0" : "1"; + calendar_control_form.submit(); + }); + + // Close dropdown when clicking outside + window.addEventListener('click', function() { + if (dropdownContent.style.display === 'block') { + dropdownContent.style.display = 'none'; + } + }); }); diff --git a/app/views/notes_formsemestre.py b/app/views/notes_formsemestre.py index b683d0a22..678bbbf60 100644 --- a/app/views/notes_formsemestre.py +++ b/app/views/notes_formsemestre.py @@ -163,6 +163,7 @@ def formsemestre_edit_modimpls_codes(formsemestre_id: int): @permission_required(Permission.ScoView) def formsemestre_edt(formsemestre_id: int): """Expérimental: affiche emploi du temps du semestre""" + current_date = request.args.get("current_date") show_modules_titles = scu.to_bool(request.args.get("show_modules_titles", False)) formsemestre = FormSemestre.get_formsemestre(formsemestre_id) cfg = ScoDocSiteConfig.query.filter_by(name="assi_morning_time").first() @@ -177,6 +178,7 @@ def formsemestre_edt(formsemestre_id: int): ) return render_template( "formsemestre/edt.j2", + current_date=current_date, formsemestre=formsemestre, hour_start=hour_start, hour_end=hour_end,