############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Emmanuel Viennet emmanuel.viennet@viennet.net # ############################################################################## """Accès aux emplois du temps Génération des ics par enseignant à partir des ics par formation. A utiliser quand les edt des enseignants ne sont pas fournis par l'extérieur. Utilise la configuration globale pour extraire l'enseignant de chaque évènement. Les évènements sans enseignant identifiable sont ignorés. Tous les ics de `edt_ics_path`/*.ics sont lus. L'edt de chaque enseignant est enregistré dans `edt_ics_user_path`/user_edt_id.ics La construction des ics se fait en mémoire, les fichiers sont écrits à la fin. """ from collections import defaultdict import time import icalendar from flask import flash from app import log from app.auth.models import User from app.models import ScoDocSiteConfig from app.scodoc import sco_edt_cal from app.scodoc.sco_exceptions import ScoValueError import sco_version def _calendar_factory(): "Create a new calendar" cal = icalendar.Calendar() cal.add( "prodid", f"-//{sco_version.SCONAME} {sco_version.SCOVERSION}//scodoc.org//" ) cal.add("version", "2.0") return cal def generate_ens_calendars(): """Regénère les calendriers de tous les enseignants""" t0 = time.time() edt_ics_uid_field = ScoDocSiteConfig.get("edt_ics_uid_field") if not edt_ics_uid_field: log("generate_ens_calendars: no edt_ics_uid_field, aborting") return edt_ics_user_path = ScoDocSiteConfig.get("edt_ics_user_path") if not edt_ics_user_path: log("generate_ens_calendars: no edt_ics_user_path, aborting") return edt_ics_uid_pattern = sco_edt_cal.get_ics_uid_pattern() edt_by_uid = defaultdict(_calendar_factory) edt2user: dict[str, User | None] = {} # construit au fur et à mesure (cache) nb_events = 0 # to log ics_filenames = sco_edt_cal.list_edt_calendars() for ics_filename in ics_filenames: try: _, calendar = sco_edt_cal.load_calendar(ics_filename) except ScoValueError: continue # skip, ignoring errors if not calendar: continue events = [e for e in calendar.walk() if e.name == "VEVENT"] 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 ) 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) _write_user_calendars(edt_by_uid) log( f"""generate_ens_calendars: done in {(time.time()-t0):g}s, processed { nb_events} events from {len(ics_filenames)} calendars, for {len(edt_by_uid)} users""" ) def _write_user_calendars(edt_by_uid: defaultdict): """Write all ics files, one per user""" for edt_id, cal in edt_by_uid.items(): if not len(cal): # nb: empty cals are True continue # safeguard, never generate empty cals filename = sco_edt_cal.get_ics_user_edt_filename(edt_id) if filename: log(f"generate_ens_calendars: writing {filename}") try: with open(filename, "wb") as f: f.write(cal.to_ical()) except PermissionError: log("_write_user_calendars: permission denied") flash("Erreur: permission écriture calendrier non accordée") return # abort except FileNotFoundError: log("_write_user_calendars: No such file or directory") flash("Erreur: chemin incorrect pour écrire le calendrier") return # abort