Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
6 changed files with 86 additions and 48 deletions
Showing only changes of commit 77c9a48d02 - Show all commits

View File

@ -269,10 +269,12 @@ class FormSemestre(db.Model):
return default_partition.groups.first() return default_partition.groups.first()
raise ScoValueError("Le semestre n'a pas de groupe par défaut") raise ScoValueError("Le semestre n'a pas de groupe par défaut")
def get_edt_id(self) -> str: def get_edt_ids(self) -> list[str]:
"l'id pour l'emploi du temps: à défaut, le 1er code étape Apogée" "l'ids pour l'emploi du temps: à défaut, les codes étape Apogée"
return ( return (
self.edt_id or "" or (self.etapes[0].etape_apo if len(self.etapes) else "") scu.split_id(self.edt_id)
or [e.etape_apo.strip() for e in self.etapes if e.etape_apo]
or []
) )
def get_infos_dict(self) -> dict: def get_infos_dict(self) -> dict:

View File

@ -58,12 +58,12 @@ class ModuleImpl(db.Model):
return {x.strip() for x in self.code_apogee.split(",") if x} return {x.strip() for x in self.code_apogee.split(",") if x}
return self.module.get_codes_apogee() return self.module.get_codes_apogee()
def get_edt_id(self) -> str: def get_edt_ids(self) -> list[str]:
"l'id pour l'emploi du temps: à défaut, le 1er code Apogée" "les ids pour l'emploi du temps: à défaut, les codes Apogée"
return ( return (
self.edt_id scu.split_id(self.edt_id)
or (self.code_apogee.split(",")[0] if self.code_apogee else "") or scu.split_id(self.code_apogee)
or self.module.get_edt_id() or self.module.get_edt_ids()
) )
def get_evaluations_poids(self) -> pd.DataFrame: def get_evaluations_poids(self) -> pd.DataFrame:

View File

@ -285,13 +285,9 @@ class Module(db.Model):
return {x.strip() for x in self.code_apogee.split(",") if x} return {x.strip() for x in self.code_apogee.split(",") if x}
return set() return set()
def get_edt_id(self) -> str: def get_edt_ids(self) -> list[str]:
"l'id pour l'emploi du temps: à défaut, le 1er code Apogée" "les ids pour l'emploi du temps: à défaut, le 1er code Apogée"
return ( return scu.split_id(self.edt_id) or scu.split_id(self.code_apogee) or []
self.edt_id
or (self.code_apogee.split(",")[0] if self.code_apogee else "")
or ""
)
def get_parcours(self) -> list[ApcParcours]: def get_parcours(self) -> list[ApcParcours]:
"""Les parcours utilisant ce module. """Les parcours utilisant ce module.

View File

@ -34,7 +34,7 @@ from datetime import timezone
import re import re
import icalendar import icalendar
from flask import flash, g, url_for from flask import g, url_for
from app import log from app import log
from app.models import FormSemestre, GroupDescr, ModuleImpl, ScoDocSiteConfig from app.models import FormSemestre, GroupDescr, ModuleImpl, ScoDocSiteConfig
from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_exceptions import ScoValueError
@ -56,12 +56,14 @@ def formsemestre_load_calendar(
Raises ScoValueError if not configured or not available or invalid format. Raises ScoValueError if not configured or not available or invalid format.
""" """
if edt_id is None and formsemestre: if edt_id is None and formsemestre:
edt_id = formsemestre.get_edt_id() edt_ids = formsemestre.get_edt_ids()
if not edt_id: if not edt_ids:
raise ScoValueError( raise ScoValueError(
"accès aux emplois du temps non configuré pour ce semestre (pas d'edt_id)" "accès aux emplois du temps non configuré pour ce semestre (pas d'edt_id)"
) )
ics_filename = get_ics_filename(edt_id) # Ne charge qu'un seul ics pour le semestre, prend uniquement
# le premier edt_id
ics_filename = get_ics_filename(edt_ids[0])
if ics_filename is None: if ics_filename is None:
raise ScoValueError("accès aux emplois du temps non configuré (pas de chemin)") raise ScoValueError("accès aux emplois du temps non configuré (pas de chemin)")
try: try:
@ -147,40 +149,51 @@ def formsemestre_edt_dict(
if group and group_ids_set and group.id not in group_ids_set: if group and group_ids_set and group.id not in group_ids_set:
continue # ignore cet évènement continue # ignore cet évènement
modimpl: ModuleImpl | bool = event["modimpl"] modimpl: ModuleImpl | bool = event["modimpl"]
if modimpl is False: url_abs = (
mod_disp = f"""<div class="module-edt" url_for(
title="extraction emploi du temps non configurée"> "assiduites.signal_assiduites_group",
{scu.EMO_WARNING} non configuré
</div>"""
else:
mod_disp = (
f"""<div class="module-edt mod-name" title="{modimpl.module.abbrev or ""}">{
modimpl.module.code}</div>"""
if modimpl
else f"""<div class="module-edt mod-etd" title="code module non trouvé dans ScoDoc.
Vérifier configuration.">{
scu.EMO_WARNING} {event['edt_module']}</div>"""
)
# --- Lien saisie abs
link_abs = (
f"""<div class="module-edt link-abs"><a class="stdlink" href="{
url_for("assiduites.signal_assiduites_group",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre.id, formsemestre_id=formsemestre.id,
group_ids=group.id, group_ids=group.id,
heure_deb=event["heure_deb"], heure_deb=event["heure_deb"],
heure_fin=event["heure_fin"], heure_fin=event["heure_fin"],
moduleimpl_id=modimpl.id, moduleimpl_id=modimpl.id,
jour = event["jour"], jour=event["jour"],
)}">absences</a> )
</div>"""
if modimpl and group if modimpl and group
else None
)
match modimpl:
case False: # EDT non configuré
mod_disp = f"""<span>{scu.EMO_WARNING} non configuré</span>"""
bubble = "extraction emploi du temps non configurée"
case None: # Module edt non trouvé dans ScoDoc
mod_disp = f"""<span class="mod-etd">{
scu.EMO_WARNING} {event['edt_module']}</span>"""
bubble = "code module non trouvé dans ScoDoc. Vérifier configuration."
case _: # module EDT bien retrouvé dans ScoDoc
mod_disp = f"""<span class="mod-name mod-code" title="{
modimpl.module.abbrev or ""} ({event['edt_module']})">{
modimpl.module.code}</span>"""
bubble = f"{modimpl.module.abbrev or ''} ({event['edt_module']})"
title = f"""<div class = "module-edt" title="{bubble} {event['title_edt']}">
<a class="discretelink" href="{url_abs or ''}">{mod_disp} <span>{event['title']}</span></a>
</div>
"""
# --- Lien saisie abs
link_abs = (
f"""<div class="module-edt link-abs"><a class="stdlink" href="{
url_abs}">absences</a>
</div>"""
if url_abs
else "" else ""
) )
d = { d = {
# Champs utilisés par tui.calendar # Champs utilisés par tui.calendar
"calendarId": "cal1", "calendarId": "cal1",
"title": event["title"] + group_disp + mod_disp + link_abs, "title": f"""{title} {group_disp} {link_abs}""",
"start": event["start"], "start": event["start"],
"end": event["end"], "end": event["end"],
"backgroundColor": event["group_bg_color"], "backgroundColor": event["group_bg_color"],
@ -245,11 +258,13 @@ def _load_and_convert_ics(formsemestre: FormSemestre) -> list[dict]:
for event in events: for event in events:
if "DESCRIPTION" in event: if "DESCRIPTION" in event:
# --- Titre de l'évènement # --- Titre de l'évènement
title = ( title_edt = (
extract_event_data(event, edt_ics_title_field, edt_ics_title_pattern) extract_event_data(event, edt_ics_title_field, edt_ics_title_pattern)
if edt_ics_title_pattern if edt_ics_title_pattern
else "non configuré" else "non configuré"
) )
# title remplacé par le nom du module scodoc quand il est trouvé
title = title_edt
# --- Group # --- Group
if edt_ics_group_pattern: if edt_ics_group_pattern:
edt_group = extract_event_data( edt_group = extract_event_data(
@ -278,6 +293,8 @@ def _load_and_convert_ics(formsemestre: FormSemestre) -> list[dict]:
event, edt_ics_mod_field, edt_ics_mod_pattern event, edt_ics_mod_field, edt_ics_mod_pattern
) )
modimpl: ModuleImpl = edt2modimpl.get(edt_module, None) modimpl: ModuleImpl = edt2modimpl.get(edt_module, None)
if modimpl:
title = modimpl.module.titre_str()
else: else:
modimpl = False modimpl = False
edt_module = "" edt_module = ""
@ -285,7 +302,8 @@ def _load_and_convert_ics(formsemestre: FormSemestre) -> list[dict]:
# #
events_sco.append( events_sco.append(
{ {
"title": title, "title": title, # titre event ou nom module
"title_edt": title_edt, # titre event
"edt_group": edt_group, # id group edt non traduit "edt_group": edt_group, # id group edt non traduit
"group": group, # False si extracteur non configuré "group": group, # False si extracteur non configuré
"group_bg_color": group_bg_color, # associée au groupe "group_bg_color": group_bg_color, # associée au groupe
@ -376,8 +394,11 @@ def formsemestre_retreive_modimpls_from_edt_id(
formsemestre: FormSemestre, formsemestre: FormSemestre,
) -> dict[str, ModuleImpl]: ) -> dict[str, ModuleImpl]:
"""Construit un dict donnant le moduleimpl de chaque edt_id""" """Construit un dict donnant le moduleimpl de chaque edt_id"""
edt2modimpl = {modimpl.get_edt_id(): modimpl for modimpl in formsemestre.modimpls} edt2modimpl = {}
edt2modimpl.pop("", None) for modimpl in formsemestre.modimpls:
for edt_id in modimpl.get_edt_ids():
if edt_id:
edt2modimpl[edt_id] = modimpl
return edt2modimpl return edt2modimpl

View File

@ -764,13 +764,23 @@ FORBIDDEN_CHARS_EXP = re.compile(r"[*\|~\(\)\\]")
ALPHANUM_EXP = re.compile(r"^[\w-]+$", re.UNICODE) ALPHANUM_EXP = re.compile(r"^[\w-]+$", re.UNICODE)
def is_valid_code_nip(s): def is_valid_code_nip(s: str) -> bool:
"""True si s peut être un code NIP: au moins 6 chiffres décimaux""" """True si s peut être un code NIP: au moins 6 chiffres décimaux"""
if not s: if not s:
return False return False
return re.match(r"^[0-9]{6,32}$", s) return re.match(r"^[0-9]{6,32}$", s)
def split_id(ident: str) -> list[str]:
"""ident est une chaine 'X, Y, Z'
Renvoie ['X','Y', 'Z']
"""
if ident:
ident = ident.strip()
return [x.strip() for x in ident.strip().split(",")] if ident else []
return []
def strnone(s): def strnone(s):
"convert s to string, '' if s is false" "convert s to string, '' if s is false"
if s: if s:

View File

@ -6,8 +6,17 @@
font-size: 12pt; font-size: 12pt;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
} }
.module-edt {
display: inline;
}
.mod-code {
font-weight: bold;
color: rgb(21, 21, 116);
font-size: 110%;
}
.group-name { .group-name {
color: rgb(25, 113, 25); color: rgb(25, 113, 25);
display: inline;
} }
.group-edt { .group-edt {
color: red; color: red;