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 {