diff --git a/app/forms/main/config_assiduites.py b/app/forms/main/config_assiduites.py index 16b6f127..a2130bee 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 7440de93..e182b27f 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 d80dfc48..8206cc03 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 {