ScoDoc/tools/edt/edt_ens.py

132 lines
5.0 KiB
Python
Raw Permalink Normal View History

##############################################################################
#
# 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() -> icalendar.Calendar:
"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:
ens_edt_ids = sco_edt_cal.extract_event_edt_ids(
event, edt_ics_uid_field, edt_ics_uid_pattern
)
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 {
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