forked from ScoDoc/ScoDoc
Compare commits
7 Commits
f99be92fad
...
a42e23b0ec
Author | SHA1 | Date | |
---|---|---|---|
a42e23b0ec | |||
aab014f5ef | |||
a2467fd236 | |||
8b332120bf | |||
988f577f6e | |||
77c9a48d02 | |||
093ab253f3 |
@ -8,8 +8,10 @@
|
|||||||
"""
|
"""
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from flask import render_template
|
from flask import render_template, url_for
|
||||||
|
|
||||||
|
import app
|
||||||
|
from app import Departement
|
||||||
from app.models import FormSemestre, FormSemestreInscription
|
from app.models import FormSemestre, FormSemestreInscription
|
||||||
from app.views import ScoData
|
from app.views import ScoData
|
||||||
|
|
||||||
@ -18,9 +20,13 @@ def feuille_preparation_jury_but(formsemestre_id: int):
|
|||||||
formsemestre: FormSemestre = FormSemestre.query.filter_by(
|
formsemestre: FormSemestre = FormSemestre.query.filter_by(
|
||||||
id=formsemestre_id
|
id=formsemestre_id
|
||||||
).first_or_404()
|
).first_or_404()
|
||||||
|
departement: Departement = Departement.query.filter_by(
|
||||||
|
id=formsemestre.dept_id
|
||||||
|
).first_or_404()
|
||||||
return render_template(
|
return render_template(
|
||||||
"but/jury_export.j2",
|
"but/jury_export.j2",
|
||||||
datetime=datetime,
|
datetime=datetime,
|
||||||
formsemestre=formsemestre,
|
formsemestre=formsemestre,
|
||||||
|
dept = departement.acronym,
|
||||||
sco=ScoData(formsemestre=formsemestre),
|
sco=ScoData(formsemestre=formsemestre),
|
||||||
)
|
)
|
||||||
|
@ -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:
|
||||||
@ -1040,6 +1042,33 @@ class FormSemestre(db.Model):
|
|||||||
nb_recorded += 1
|
nb_recorded += 1
|
||||||
return nb_recorded
|
return nb_recorded
|
||||||
|
|
||||||
|
def change_formation(self, formation_dest: Formation):
|
||||||
|
"""Associe ce formsemestre à une autre formation.
|
||||||
|
Ce n'est possible que si la formation destination possède des modules de
|
||||||
|
même code que ceux utilisés dans la formation d'origine du formsemestre.
|
||||||
|
S'il manque un module, l'opération est annulée.
|
||||||
|
Commit (or rollback) session.
|
||||||
|
"""
|
||||||
|
ok = True
|
||||||
|
for mi in self.modimpls:
|
||||||
|
dest_modules = formation_dest.modules.filter_by(code=mi.module.code).all()
|
||||||
|
match len(dest_modules):
|
||||||
|
case 1:
|
||||||
|
mi.module = dest_modules[0]
|
||||||
|
db.session.add(mi)
|
||||||
|
case 0:
|
||||||
|
print(f"Argh ! no module found with code={mi.module.code}")
|
||||||
|
ok = False
|
||||||
|
case _:
|
||||||
|
print(f"Arg ! several modules found with code={mi.module.code}")
|
||||||
|
ok = False
|
||||||
|
|
||||||
|
if ok:
|
||||||
|
self.formation_id = formation_dest.id
|
||||||
|
db.session.commit()
|
||||||
|
else:
|
||||||
|
db.session.rollback()
|
||||||
|
|
||||||
|
|
||||||
# Association id des utilisateurs responsables (aka directeurs des etudes) du semestre
|
# Association id des utilisateurs responsables (aka directeurs des etudes) du semestre
|
||||||
notes_formsemestre_responsables = db.Table(
|
notes_formsemestre_responsables = db.Table(
|
||||||
|
@ -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:
|
||||||
|
@ -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.
|
||||||
|
@ -80,7 +80,7 @@ def formation_delete(formation_id=None, dialog_confirmed=False):
|
|||||||
f"""<h2>Confirmer la suppression de la formation
|
f"""<h2>Confirmer la suppression de la formation
|
||||||
{formation.titre} ({formation.acronyme}) ?
|
{formation.titre} ({formation.acronyme}) ?
|
||||||
</h2>
|
</h2>
|
||||||
<p><b>Attention:</b> la suppression d'une formation est <b>irréversible</b>
|
<p><b>Attention:</b> la suppression d'une formation est <b>irréversible</b>
|
||||||
et implique la supression de toutes les UE, matières et modules de la formation !
|
et implique la supression de toutes les UE, matières et modules de la formation !
|
||||||
</p>
|
</p>
|
||||||
""",
|
""",
|
||||||
@ -273,7 +273,8 @@ def formation_edit(formation_id=None, create=False):
|
|||||||
"\n".join(H)
|
"\n".join(H)
|
||||||
+ tf_error_message(
|
+ tf_error_message(
|
||||||
f"""Valeurs incorrectes: il existe déjà <a href="{
|
f"""Valeurs incorrectes: il existe déjà <a href="{
|
||||||
url_for('notes.ue_table', scodoc_dept=g.scodoc_dept, formation_id=other_formations[0].id)
|
url_for('notes.ue_table',
|
||||||
|
scodoc_dept=g.scodoc_dept, formation_id=other_formations[0].id)
|
||||||
}">une formation</a> avec même titre,
|
}">une formation</a> avec même titre,
|
||||||
acronyme et version.
|
acronyme et version.
|
||||||
"""
|
"""
|
||||||
@ -285,11 +286,11 @@ def formation_edit(formation_id=None, create=False):
|
|||||||
if create:
|
if create:
|
||||||
formation = do_formation_create(tf[2])
|
formation = do_formation_create(tf[2])
|
||||||
else:
|
else:
|
||||||
do_formation_edit(tf[2])
|
if do_formation_edit(tf[2]):
|
||||||
flash(
|
flash(
|
||||||
f"""Création de la formation {
|
f"""Modification de la formation {
|
||||||
formation.titre} ({formation.acronyme}) version {formation.version}"""
|
formation.titre} ({formation.acronyme}) version {formation.version}"""
|
||||||
)
|
)
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
url_for(
|
url_for(
|
||||||
"notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation.id
|
"notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation.id
|
||||||
@ -335,8 +336,8 @@ def do_formation_create(args: dict) -> Formation:
|
|||||||
return formation
|
return formation
|
||||||
|
|
||||||
|
|
||||||
def do_formation_edit(args):
|
def do_formation_edit(args) -> bool:
|
||||||
"edit a formation"
|
"edit a formation, returns True if modified"
|
||||||
|
|
||||||
# On ne peut jamais supprimer le code formation:
|
# On ne peut jamais supprimer le code formation:
|
||||||
if "formation_code" in args and not args["formation_code"]:
|
if "formation_code" in args and not args["formation_code"]:
|
||||||
@ -350,11 +351,16 @@ def do_formation_edit(args):
|
|||||||
if "type_parcours" in args:
|
if "type_parcours" in args:
|
||||||
del args["type_parcours"]
|
del args["type_parcours"]
|
||||||
|
|
||||||
|
modified = False
|
||||||
for field in formation.__dict__:
|
for field in formation.__dict__:
|
||||||
if field in args:
|
if field in args:
|
||||||
value = args[field].strip() if isinstance(args[field], str) else args[field]
|
value = args[field].strip() if isinstance(args[field], str) else args[field]
|
||||||
if field and field[0] != "_":
|
if field and field[0] != "_" and getattr(formation, field, None) != value:
|
||||||
setattr(formation, field, value)
|
setattr(formation, field, value)
|
||||||
|
modified = True
|
||||||
|
|
||||||
|
if not modified:
|
||||||
|
return False
|
||||||
|
|
||||||
db.session.add(formation)
|
db.session.add(formation)
|
||||||
try:
|
try:
|
||||||
@ -370,6 +376,7 @@ def do_formation_edit(args):
|
|||||||
),
|
),
|
||||||
) from exc
|
) from exc
|
||||||
formation.invalidate_cached_sems()
|
formation.invalidate_cached_sems()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def module_move(module_id, after=0, redirect=True):
|
def module_move(module_id, after=0, redirect=True):
|
||||||
|
@ -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é
|
scodoc_dept=g.scodoc_dept,
|
||||||
</div>"""
|
formsemestre_id=formsemestre.id,
|
||||||
else:
|
group_ids=group.id,
|
||||||
mod_disp = (
|
heure_deb=event["heure_deb"],
|
||||||
f"""<div class="module-edt mod-name" title="{modimpl.module.abbrev or ""}">{
|
heure_fin=event["heure_fin"],
|
||||||
modimpl.module.code}</div>"""
|
moduleimpl_id=modimpl.id,
|
||||||
if modimpl
|
jour=event["jour"],
|
||||||
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>"""
|
|
||||||
)
|
)
|
||||||
|
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
|
# --- Lien saisie abs
|
||||||
link_abs = (
|
link_abs = (
|
||||||
f"""<div class="module-edt link-abs"><a class="stdlink" href="{
|
f"""<div class="module-edt link-abs"><a class="stdlink" href="{
|
||||||
url_for("assiduites.signal_assiduites_group",
|
url_abs}">absences</a>
|
||||||
scodoc_dept=g.scodoc_dept,
|
|
||||||
formsemestre_id=formsemestre.id,
|
|
||||||
group_ids=group.id,
|
|
||||||
heure_deb=event["heure_deb"],
|
|
||||||
heure_fin=event["heure_fin"],
|
|
||||||
moduleimpl_id=modimpl.id,
|
|
||||||
jour = event["jour"],
|
|
||||||
)}">absences</a>
|
|
||||||
</div>"""
|
</div>"""
|
||||||
if modimpl and group
|
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
|
||||||
|
|
||||||
|
|
||||||
|
@ -307,7 +307,7 @@ def formation_import_xml(doc: str, import_tags=True, use_local_refcomp=False):
|
|||||||
D = sco_xml.xml_to_dicts(f)
|
D = sco_xml.xml_to_dicts(f)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise ScoFormatError(
|
raise ScoFormatError(
|
||||||
"""Ce document xml ne correspond pas à un programme exporté par ScoDoc.
|
"""Ce document xml ne correspond pas à un programme exporté par ScoDoc.
|
||||||
(élément 'formation' inexistant par exemple)."""
|
(élément 'formation' inexistant par exemple)."""
|
||||||
) from exc
|
) from exc
|
||||||
assert D[0] == "formation"
|
assert D[0] == "formation"
|
||||||
@ -322,8 +322,13 @@ def formation_import_xml(doc: str, import_tags=True, use_local_refcomp=False):
|
|||||||
referentiel_competence_id = _formation_retreive_refcomp(f_dict)
|
referentiel_competence_id = _formation_retreive_refcomp(f_dict)
|
||||||
f_dict["referentiel_competence_id"] = referentiel_competence_id
|
f_dict["referentiel_competence_id"] = referentiel_competence_id
|
||||||
# find new version number
|
# find new version number
|
||||||
|
acronyme_lower = f_dict["acronyme"].lower() if f_dict["acronyme"] else ""
|
||||||
|
titre_lower = f_dict["titre"].lower() if f_dict["titre"] else ""
|
||||||
formations: list[Formation] = Formation.query.filter_by(
|
formations: list[Formation] = Formation.query.filter_by(
|
||||||
acronyme=f_dict["acronyme"], titre=f_dict["titre"], dept_id=f_dict["dept_id"]
|
dept_id=f_dict["dept_id"]
|
||||||
|
).filter(
|
||||||
|
db.func.lower(Formation.acronyme) == acronyme_lower,
|
||||||
|
db.func.lower(Formation.titre) == titre_lower,
|
||||||
)
|
)
|
||||||
if formations.count():
|
if formations.count():
|
||||||
version = max(f.version or 0 for f in formations)
|
version = max(f.version or 0 for f in formations)
|
||||||
@ -518,6 +523,7 @@ def formation_list_table() -> GenTable:
|
|||||||
"_titre_link_class": "stdlink",
|
"_titre_link_class": "stdlink",
|
||||||
"_titre_id": f"""titre-{acronyme_no_spaces}""",
|
"_titre_id": f"""titre-{acronyme_no_spaces}""",
|
||||||
"version": formation.version or 0,
|
"version": formation.version or 0,
|
||||||
|
"commentaire": formation.commentaire or "",
|
||||||
}
|
}
|
||||||
# Ajoute les semestres associés à chaque formation:
|
# Ajoute les semestres associés à chaque formation:
|
||||||
row["formsemestres"] = formation.formsemestres.order_by(
|
row["formsemestres"] = formation.formsemestres.order_by(
|
||||||
@ -594,10 +600,12 @@ def formation_list_table() -> GenTable:
|
|||||||
"formation_code",
|
"formation_code",
|
||||||
"version",
|
"version",
|
||||||
"titre",
|
"titre",
|
||||||
|
"commentaire",
|
||||||
"sems_list_txt",
|
"sems_list_txt",
|
||||||
)
|
)
|
||||||
titles = {
|
titles = {
|
||||||
"buttons": "",
|
"buttons": "",
|
||||||
|
"commentaire": "Commentaire",
|
||||||
"acronyme": "Acro.",
|
"acronyme": "Acro.",
|
||||||
"parcours_name": "Type",
|
"parcours_name": "Type",
|
||||||
"titre": "Titre",
|
"titre": "Titre",
|
||||||
|
@ -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:
|
||||||
|
@ -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;
|
||||||
|
@ -2319,7 +2319,10 @@ table.formation_list_table td.buttons span.but_placeholder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.formation_list_table td.titre {
|
.formation_list_table td.titre {
|
||||||
width: 50%;
|
width: 45%;
|
||||||
|
}
|
||||||
|
.formation_list_table td.commentaire {
|
||||||
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.formation_list_table td.sems_list_txt {
|
.formation_list_table td.sems_list_txt {
|
||||||
|
0
app/static/js/export-jury-but.js
Normal file
0
app/static/js/export-jury-but.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -7,34 +7,57 @@
|
|||||||
|
|
||||||
{% block app_content %}
|
{% block app_content %}
|
||||||
|
|
||||||
<releve-but>{{ formsemestre.id }}</releve-but>
|
<H1>Export xlsx de fichier de jury</H1>
|
||||||
|
|
||||||
|
|
||||||
|
<form onsubmit="do_export()">
|
||||||
|
Formsemestre: {{ formsemestre.id }}
|
||||||
|
API root: {{ api_root }}
|
||||||
|
<button>Exporter</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
{{super()}}
|
{{super()}}
|
||||||
|
|
||||||
{# <script src="{{scu.STATIC_DIR}}/js/releve-but.js"></script>#}
|
<script src="{{scu.STATIC_DIR}}/js/export-jury-but.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
//let dataSrc = "{{bul_url|safe}}";
|
let dataSrc = "/ScoDoc/{{dept}}/api/formsemestre/{{formsemestre.id}}/etudiants/long";
|
||||||
//fetch(dataSrc)
|
let formsemestres = [];
|
||||||
// .then(r => { return r.json() })
|
fetch(dataSrc)
|
||||||
// .then(json => {
|
.then(r => { return r.json(); })
|
||||||
// let releve = document.querySelector("releve-but");
|
.then(json => {
|
||||||
// releve.showData = json;
|
let etudiants = json;
|
||||||
// // Syle custom à ajouter
|
etudiants.forEach((etudiant) => {
|
||||||
// let style = document.createElement("style");
|
let url =`/ScoDoc/{{dept}}/api/etudiant/nip/${etudiant.code_nip}/formsemestres`;
|
||||||
// style.textContent = `
|
console.log(url);
|
||||||
// .module>div,
|
etudiant.formsemestres_id = [];
|
||||||
// .dateInscription,
|
fetch(`/ScoDoc/{{dept}}/api/etudiant/nip/${etudiant.code_nip}/formsemestres`)
|
||||||
// .numerosEtudiant,
|
.then(r => {return r.json; })
|
||||||
// .dateNaissance{
|
.then(json => {
|
||||||
// display: none;
|
let forms = json;
|
||||||
// }`;
|
forms.forEach((formsem) => {
|
||||||
// releve.shadowRoot.appendChild(style);
|
if (formsem.is_apc) {
|
||||||
// });
|
etudiant.formsemestres_id.push(formsem.formsemestre_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
//let releve = document.querySelector("releve-but");
|
||||||
|
//releve.showData = json;
|
||||||
|
// Style custom à ajouter
|
||||||
|
// let style = document.createElement("style");
|
||||||
|
// style.textContent = `
|
||||||
|
// .module>div,
|
||||||
|
// .dateInscription,
|
||||||
|
// .numerosEtudiant,
|
||||||
|
// .dateNaissance{
|
||||||
|
// display: none;
|
||||||
|
// }`;
|
||||||
|
//releve.shadowRoot.appendChild(style);
|
||||||
|
})
|
||||||
|
.catch(error => { console.log(error)});
|
||||||
// .catch(error => {
|
// .catch(error => {
|
||||||
// let div = document.createElement("div");
|
// let div = document.createElement("div");
|
||||||
// div.innerText = "Une erreur s'est produite lors du transfert des données.";
|
// div.innerText = "Une erreur s'est produite lors du transfert des données.";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
SCOVERSION = "9.6.56"
|
SCOVERSION = "9.6.58"
|
||||||
|
|
||||||
SCONAME = "ScoDoc"
|
SCONAME = "ScoDoc"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user