diff --git a/app/scodoc/sco_edt_cal.py b/app/scodoc/sco_edt_cal.py index 7d63add014..476546f9b4 100644 --- a/app/scodoc/sco_edt_cal.py +++ b/app/scodoc/sco_edt_cal.py @@ -41,50 +41,58 @@ from app.scodoc.sco_exceptions import ScoValueError import app.scodoc.sco_utils as scu -def formsemestre_load_calendar( - formsemestre: FormSemestre, -) -> icalendar.cal.Calendar | None: - """Load ics data, return calendar or None if not configured or not available""" - edt_id = formsemestre.get_edt_id() - if not edt_id: - flash( - "accès aux emplois du temps non configuré pour ce semestre (pas d'edt_id)" - ) - return None +def get_ics_filename(edt_id: str) -> str: + "Le chemin vers l'ics de cet edt_id" edt_ics_path = ScoDocSiteConfig.get("edt_ics_path") if not edt_ics_path.strip(): return None - ics_filename = edt_ics_path.format(edt_id=edt_id) + return edt_ics_path.format(edt_id=edt_id) + + +def formsemestre_load_calendar( + formsemestre: FormSemestre = None, edt_id: str = None +) -> tuple[bytes, icalendar.cal.Calendar]: + """Load ics data, return raw ics and decoded calendar. + Raises ScoValueError if not configured or not available or invalid format. + """ + if edt_id is None and formsemestre: + edt_id = formsemestre.get_edt_id() + if not edt_id: + raise ScoValueError( + "accès aux emplois du temps non configuré pour ce semestre (pas d'edt_id)" + ) + ics_filename = get_ics_filename(edt_id) try: with open(ics_filename, "rb") as file: log(f"Loading edt from {ics_filename}") + data = file.read() try: - calendar = icalendar.Calendar.from_ical(file.read()) + calendar = icalendar.Calendar.from_ical(data) except ValueError as exc: log( f"""formsemestre_load_calendar: error importing ics for { - formsemestre}\npath='{ics_filename}'""" + formsemestre or ''}\npath='{ics_filename}'""" ) raise ScoValueError( f"calendrier ics illisible (edt_id={edt_id})" ) from exc except FileNotFoundError as exc: log( - f"formsemestre_load_calendar: ics not found for {formsemestre}\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})" ) from exc except PermissionError as exc: log( - f"""formsemestre_load_calendar: permission denied for {formsemestre + f"""formsemestre_load_calendar: permission denied for {formsemestre or '' }\npath='{ics_filename}'""" ) raise ScoValueError( f"Fichier ics inaccessible: vérifier permissions (filename={ics_filename})" ) from exc - return calendar + return data, calendar # --- Couleurs des évènements emploi du temps @@ -178,7 +186,7 @@ def formsemestre_edt_dict(formsemestre: FormSemestre) -> list[dict]: def _load_and_convert_ics(formsemestre: FormSemestre) -> list[dict]: "chargement fichier, filtrage et extraction des identifiants." # Chargement du calendier ics - calendar = formsemestre_load_calendar(formsemestre) + _, calendar = formsemestre_load_calendar(formsemestre) if not calendar: return [] # --- Paramètres d'extraction @@ -236,7 +244,8 @@ def _load_and_convert_ics(formsemestre: FormSemestre) -> list[dict]: edt_group = extract_event_data( event, edt_ics_group_field, edt_ics_group_pattern ) - # si pas de groupe dans l'event, oi si groupe non reconnu, prend toute la promo ("tous") + # si pas de groupe dans l'event, ou si groupe non reconnu, + # prend toute la promo ("tous") group: GroupDescr = ( edt2group.get(edt_group, default_group) if edt_group diff --git a/app/templates/assiduites/pages/config_assiduites.j2 b/app/templates/assiduites/pages/config_assiduites.j2 index 7522968a9c..3d75e9b8ce 100644 --- a/app/templates/assiduites/pages/config_assiduites.j2 +++ b/app/templates/assiduites/pages/config_assiduites.j2 @@ -10,9 +10,50 @@ div.config-section { margin-right: -15px; margin-left: -15px; } - -{% endblock %} +#zone-test { + margin-bottom: 12px; +} +#raw-ics-sample-zone { + display: none; +} +#raw-ics-sample-zone>div { + font-style: italic; +} +#test_load_ics:disabled { + background-color: gray; + color: white; /* Optional: change text color if needed */ + cursor: not-allowed; +} + +{% endblock styles %} + + +{% block scripts %} +{{ super() }} + + +{% endblock scripts %} {% block app_content %}
@@ -46,6 +87,18 @@ affectent notamment les comptages d'absences de tous les bulletins des {{ wtf.form_field(form.edt_ics_path) }}
+
+ Pour essayer, indiquer un edt_id : + + +
+
Voici un évènement chargé au milieu de ce calendrier: +
+ +
+
Extraction des identifiants depuis les calendriers
Indiquer ici comment récupérer les informations (titre, groupe, module) diff --git a/app/views/scodoc.py b/app/views/scodoc.py index 4d5af816f9..6c30eec34a 100644 --- a/app/views/scodoc.py +++ b/app/views/scodoc.py @@ -81,7 +81,7 @@ from app.models import ( from app.models import departements from app.models.config import PersonalizedLink - +from app.scodoc import sco_edt_cal from app.scodoc import sco_find_etud from app.scodoc import sco_logos from app.scodoc import sco_utils as scu @@ -376,6 +376,24 @@ def config_assiduites(): ) +@bp.route("/ScoDoc/ics_raw_sample/") +@admin_required +def ics_raw_sample(edt_id: str): + "Renvoie un extrait de l'ics brut, pour aider à configurer les extractions" + try: + raw_ics, _ = sco_edt_cal.formsemestre_load_calendar(edt_id=edt_id) + except ScoValueError as exc: + return exc.args[0] + try: + ics = raw_ics.decode(scu.SCO_ENCODING) + except SyntaxError: + return f"Erreur lors de la conversion vers {scu.SCO_ENCODING}" + evs = ics.split("BEGIN:VEVENT") + if len(evs) < 1: + return "pas d'évènements VEVENT détectés dans ce fichier" + return "BEGIN:VEVENT" + evs[len(evs) // 2] + + @bp.route("/ScoDoc/config_codes_decisions", methods=["GET", "POST"]) @admin_required def config_codes_decisions():