2024-01-02 23:05:08 +01:00
|
|
|
##############################################################################
|
|
|
|
#
|
|
|
|
# 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
|
|
|
|
|
|
|
|
|
2024-01-17 21:58:45 +01:00
|
|
|
def _calendar_factory() -> icalendar.Calendar:
|
2024-01-02 23:05:08 +01:00
|
|
|
"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:
|
2024-01-03 22:11:49 +01:00
|
|
|
ens_edt_ids = sco_edt_cal.extract_event_edt_ids(
|
|
|
|
event, edt_ics_uid_field, edt_ics_uid_pattern
|
2024-01-02 23:05:08 +01:00
|
|
|
)
|
2024-01-03 22:11:49 +01:00
|
|
|
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]))
|
|
|
|
|
2024-01-02 23:05:08 +01:00
|
|
|
_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
|