diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py
index 1016a5f09b..ef700ace5a 100644
--- a/app/models/formsemestre.py
+++ b/app/models/formsemestre.py
@@ -125,6 +125,7 @@ class FormSemestre(db.Model):
"Partition",
backref=db.backref("formsemestre", lazy=True),
lazy="dynamic",
+ order_by="Partition.numero",
)
# Ancien id ScoDoc7 pour les migrations de bases anciennes
# ne pas utiliser après migrate_scodoc7_dept_archives
diff --git a/app/models/groups.py b/app/models/groups.py
index 1a5dc257d5..5d2026eb95 100644
--- a/app/models/groups.py
+++ b/app/models/groups.py
@@ -47,6 +47,7 @@ class Partition(db.Model):
backref=db.backref("partition", lazy=True),
lazy="dynamic",
cascade="all, delete-orphan",
+ order_by="GroupDescr.numero",
)
def __init__(self, **kwargs):
@@ -109,7 +110,7 @@ class GroupDescr(db.Model):
partition_id = db.Column(db.Integer, db.ForeignKey("partition.id"))
# "A", "C2", ... (NULL for 'all'):
group_name = db.Column(db.String(GROUPNAME_STR_LEN))
- # Numero = ordre de presentation)
+ # Numero = ordre de presentation
numero = db.Column(db.Integer)
etuds = db.relationship(
diff --git a/app/models/modules.py b/app/models/modules.py
index 082d5a93ac..9052f94b68 100644
--- a/app/models/modules.py
+++ b/app/models/modules.py
@@ -51,6 +51,7 @@ class Module(db.Model):
secondary=parcours_modules,
lazy="subquery",
backref=db.backref("modules", lazy=True),
+ order_by="ApcParcours.numero",
)
app_critiques = db.relationship(
@@ -115,6 +116,19 @@ class Module(db.Model):
"""
return scu.ModuleType.get_abbrev(self.module_type)
+ def sort_key_apc(self) -> tuple:
+ """Clé de tri pour avoir
+ présentation par type (res, sae), parcours, type, numéro
+ """
+ if (
+ len(self.parcours) == self.formation.referentiel_competence.parcours.count()
+ or len(self.parcours) == 0
+ ):
+ key_parcours = ""
+ else:
+ key_parcours = "/".join([p.code for p in self.parcours])
+ return self.module_type, key_parcours, self.numero
+
def set_ue_coef(self, ue, coef: float) -> None:
"""Set coef module vers cette UE"""
self.update_ue_coef_dict({ue.id: coef})
diff --git a/app/scodoc/sco_formations.py b/app/scodoc/sco_formations.py
index 24993922cb..b060ffe081 100644
--- a/app/scodoc/sco_formations.py
+++ b/app/scodoc/sco_formations.py
@@ -290,7 +290,7 @@ def formation_import_xml(doc: str, import_tags=True):
module.parcours.append(parcours)
db.session.add(module)
else:
- log("Warning: parcours {code_parcours} inexistant !")
+ log(f"Warning: parcours {code_parcours} inexistant !")
if import_tags and tag_names:
sco_tag_module.module_tag_set(mod_id, tag_names)
if module.is_apc() and ue_coef_dict:
diff --git a/app/scodoc/sco_formsemestre.py b/app/scodoc/sco_formsemestre.py
index ce7f2c10de..a4f9ad5008 100644
--- a/app/scodoc/sco_formsemestre.py
+++ b/app/scodoc/sco_formsemestre.py
@@ -254,6 +254,7 @@ def do_formsemestre_create(args, silent=False):
formsemestre_id,
default=True,
redirect=0,
+ numero=1000000, # à la fin
)
_group_id = sco_groups.create_group(partition_id, default=True)
diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py
index bbf80097d9..2f9ea31c1b 100644
--- a/app/scodoc/sco_formsemestre_edit.py
+++ b/app/scodoc/sco_formsemestre_edit.py
@@ -105,8 +105,9 @@ def formsemestre_editwithmodules(formsemestre_id):
]
if not sem["etat"]:
H.append(
- """
%sCe semestre est verrouillé.
"""
- % scu.icontag("lock_img", border="0", title="Semestre verrouillé")
+ f"""{scu.icontag(
+ "lock_img", border="0", title="Semestre verrouillé")
+ }Ce semestre est verrouillé.
"""
)
else:
r = do_formsemestre_createwithmodules(edit=1)
@@ -159,7 +160,7 @@ def do_formsemestre_createwithmodules(edit=False):
"vous n'avez pas le droit d'effectuer cette opération"
)
- # Liste des enseignants avec forme pour affichage / saisie avec suggestion
+ # Liste des enseignants avec form pour affichage / saisie avec suggestion
# attention: il faut prendre ici tous les utilisateurs, même inactifs, car
# les responsables de modules d'anciens semestres peuvent ne plus être actifs.
# Mais la suggestion utilise get_user_list_xml() qui ne suggérera que les actifs.
@@ -226,7 +227,11 @@ def do_formsemestre_createwithmodules(edit=False):
semestre_id_labels.append(f"S{sid}")
# Liste des modules dans cette formation
if is_apc:
- modules = formation.modules.order_by(Module.module_type, Module.numero)
+ # BUT: trie par type (res, sae), parcours, numéro
+ modules = sorted(
+ formation.modules,
+ key=lambda m: m.sort_key_apc(),
+ )
else:
modules = (
Module.query.filter(
@@ -235,11 +240,10 @@ def do_formsemestre_createwithmodules(edit=False):
.order_by(Module.module_type, UniteEns.numero, Module.numero)
.all()
)
- mods = [mod.to_dict() for mod in modules]
# Pour regroupement des modules par semestres:
semestre_ids = {}
- for mod in mods:
- semestre_ids[mod["semestre_id"]] = 1
+ for mod in modules:
+ semestre_ids[mod.semestre_id] = 1
semestre_ids = list(semestre_ids.keys())
semestre_ids.sort()
@@ -607,16 +611,16 @@ def do_formsemestre_createwithmodules(edit=False):
},
)
)
- for mod in mods:
- if mod["semestre_id"] == semestre_id and (
+ for mod in modules:
+ if mod.semestre_id == semestre_id and (
(not edit) # creation => tous modules
or (not is_apc) # pas BUT, on peut mixer les semestres
or (semestre_id == formsemestre.semestre_id) # module du semestre
- or (mod["module_id"] in module_ids_set) # module déjà présent
+ or (mod.id in module_ids_set) # module déjà présent
):
nbmod += 1
if edit:
- select_name = f"{mod['module_id']}!group_id"
+ select_name = f"{mod.id}!group_id"
def opt_selected(gid):
if gid == vals.get(select_name):
@@ -624,26 +628,34 @@ def do_formsemestre_createwithmodules(edit=False):
else:
return ""
- if mod["module_id"] in module_ids_set:
+ if mod.id in module_ids_set:
+ # pas de menu inscription si le module est déjà présent
disabled = "disabled"
else:
disabled = ""
- fcg = '' % (select_name, disabled)
+ fcg = f''
default_group_id = sco_groups.get_default_group(formsemestre_id)
- fcg += 'Tous ' % (
- default_group_id,
- opt_selected(default_group_id),
- )
- fcg += 'Aucun ' % opt_selected("")
- for p in sco_groups.get_partitions_list(formsemestre_id):
- if p["partition_name"] != None:
- for group in sco_groups.get_partition_groups(p):
- fcg += '%s %s ' % (
- group["group_id"],
- opt_selected(group["group_id"]),
- p["partition_name"],
- group["group_name"],
- )
+ fcg += f"""Tous """
+
+ fcg += f'Aucun '
+ for partition in formsemestre.partitions:
+ if partition.partition_name is not None:
+ for group in partition.groups:
+ # Si le module n'est associé qu'à un parcours, propose d'y inscrire les étudiants directement
+ if (
+ partition.partition_name == scu.PARTITION_PARCOURS
+ and len(mod.parcours) == 1
+ and group.group_name == mod.parcours[0].code
+ ):
+ selected = "selected"
+ else:
+ selected = opt_selected(group.id)
+ # print(
+ # f"{partition.partition_name} {group.group_name} {selected}"
+ # )
+ fcg += f"""{partition.partition_name} {group.group_name} """
fcg += " "
itemtemplate = f"""
%(label)s
@@ -657,12 +669,12 @@ def do_formsemestre_createwithmodules(edit=False):
"""
modform.append(
(
- "MI" + str(mod["module_id"]),
+ "MI" + str(mod.id),
{
"input_type": "text_suggest",
"size": 50,
"withcheckbox": True,
- "title": "%s %s" % (mod["code"] or "", mod["titre"] or ""),
+ "title": "%s %s" % (mod.code or "", mod.titre or ""),
"allowed_values": allowed_user_names,
"template": itemtemplate,
"text_suggest_options": {
@@ -689,11 +701,6 @@ def do_formsemestre_createwithmodules(edit=False):
)
)
if edit:
- # modform.append( ('inscrire_etudslist',
- # { 'input_type' : 'checkbox',
- # 'allowed_values' : ['X'], 'labels' : [ '' ],
- # 'title' : '' ,
- # 'explanation' : 'inscrire tous les étudiants du semestre aux modules ajoutés'}) )
submitlabel = "Modifier ce semestre"
else:
submitlabel = "Créer ce semestre de formation"
diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py
index 6e008e9c1b..ae8f57c8ee 100644
--- a/app/scodoc/sco_groups.py
+++ b/app/scodoc/sco_groups.py
@@ -137,7 +137,9 @@ def get_partition(partition_id):
def get_partitions_list(formsemestre_id, with_default=True) -> list[dict]:
- """Liste des partitions pour ce semestre (list of dicts)"""
+ """Liste des partitions pour ce semestre (list of dicts),
+ triées par numéro, avec la partition par défaut en fin de liste.
+ """
partitions = ndb.SimpleDictFetch(
"""SELECT p.id AS partition_id, p.*
FROM partition p
@@ -205,7 +207,7 @@ def get_partition_groups(partition):
FROM group_descr gd, partition p
WHERE gd.partition_id=%(partition_id)s
AND gd.partition_id=p.id
- ORDER BY group_name
+ ORDER BY gd.numero
""",
partition,
)
@@ -575,7 +577,6 @@ def XMLgetGroupsInPartition(partition_id): # was XMLgetGroupesTD
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
etuds_set = {ins.etudid for ins in formsemestre.inscriptions}
- sem = formsemestre.get_infos_dict() # transition TODO
groups = get_partition_groups(partition)
# Build XML:
t1 = time.time()
@@ -593,7 +594,6 @@ def XMLgetGroupsInPartition(partition_id): # was XMLgetGroupesTD
x_response.append(x_group)
for e in get_group_members(group["group_id"]):
etud = sco_etud.get_etud_info(etudid=e["etudid"], filled=True)[0]
- # etud = sco_etud.get_etud_info_filled_by_etudid(e["etudid"], cnx)
x_group.append(
Element(
"etud",
@@ -602,7 +602,7 @@ def XMLgetGroupsInPartition(partition_id): # was XMLgetGroupesTD
sexe=etud["civilite_str"], # compat
nom=sco_etud.format_nom(etud["nom"]),
prenom=sco_etud.format_prenom(etud["prenom"]),
- origin=comp_origin(etud, sem),
+ origin=_comp_etud_origin(etud, formsemestre),
)
)
if e["etudid"] in etuds_set:
@@ -620,7 +620,6 @@ def XMLgetGroupsInPartition(partition_id): # was XMLgetGroupesTD
doc.append(x_group)
for etudid in etuds_set:
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
- # etud = sco_etud.get_etud_info_filled_by_etudid(etudid, cnx)
x_group.append(
Element(
"etud",
@@ -628,7 +627,7 @@ def XMLgetGroupsInPartition(partition_id): # was XMLgetGroupesTD
sexe=etud["civilite_str"],
nom=sco_etud.format_nom(etud["nom"]),
prenom=sco_etud.format_prenom(etud["prenom"]),
- origin=comp_origin(etud, sem),
+ origin=_comp_etud_origin(etud, formsemestre),
)
)
t2 = time.time()
@@ -640,14 +639,14 @@ def XMLgetGroupsInPartition(partition_id): # was XMLgetGroupesTD
return response
-def comp_origin(etud, cur_sem):
+def _comp_etud_origin(etud: dict, cur_formsemestre: FormSemestre):
"""breve description de l'origine de l'étudiant (sem. precedent)
(n'indique l'origine que si ce n'est pas le semestre precedent normal)
"""
# cherche le semestre suivant le sem. courant dans la liste
cur_sem_idx = None
for i in range(len(etud["sems"])):
- if etud["sems"][i]["formsemestre_id"] == cur_sem["formsemestre_id"]:
+ if etud["sems"][i]["formsemestre_id"] == cur_formsemestre.id:
cur_sem_idx = i
break
@@ -655,8 +654,8 @@ def comp_origin(etud, cur_sem):
return "" # on pourrait indiquer le bac mais en general on ne l'a pas en debut d'annee
prev_sem = etud["sems"][cur_sem_idx + 1]
- if prev_sem["semestre_id"] != (cur_sem["semestre_id"] - 1):
- return " (S%s)" % prev_sem["semestre_id"]
+ if prev_sem["semestre_id"] != (cur_formsemestre.semestre_id - 1):
+ return f" (S{prev_sem['semestre_id']})"
else:
return "" # parcours normal, ne le signale pas
diff --git a/app/scodoc/sco_moduleimpl.py b/app/scodoc/sco_moduleimpl.py
index e0dab5f429..97fb7df067 100644
--- a/app/scodoc/sco_moduleimpl.py
+++ b/app/scodoc/sco_moduleimpl.py
@@ -133,7 +133,7 @@ def moduleimpl_withmodule_list(
- pour les formations classiques: semestre/UE/numero_matiere/numero_module;
- pour le BUT: ignore UEs sauf si sort_by_ue et matières dans le tri.
- Attention: Cette fonction fait partie de l'API ScoDoc 7 et est publiée.
+ NB: Cette fonction faisait partie de l'API ScoDoc 7.
"""
from app.scodoc import sco_edit_ue
from app.scodoc import sco_edit_matiere
diff --git a/app/scodoc/sco_moduleimpl_inscriptions.py b/app/scodoc/sco_moduleimpl_inscriptions.py
index 76871cf354..b2be0e50e6 100644
--- a/app/scodoc/sco_moduleimpl_inscriptions.py
+++ b/app/scodoc/sco_moduleimpl_inscriptions.py
@@ -214,7 +214,7 @@ def moduleimpl_inscriptions_edit(moduleimpl_id, etuds=[], submitted=False):
return "\n".join(H)
-def _make_menu(partitions, title="", check="true"):
+def _make_menu(partitions: list[dict], title="", check="true") -> str:
"""Menu with list of all groups"""
items = [{"title": "Tous", "attr": "onclick=\"group_select('', -1, %s)\"" % check}]
p_idx = 0
@@ -258,8 +258,8 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
"""
authuser = current_user
-
- sem = sco_formsemestre.get_formsemestre(formsemestre_id)
+ formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
+ is_apc = formsemestre.formation.is_apc()
inscrits = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
args={"formsemestre_id": formsemestre_id}
)
@@ -268,85 +268,93 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
formsemestre_id
)
- can_change = authuser.has_permission(Permission.ScoEtudInscrit) and sem["etat"]
-
- # Liste des modules
- Mlist = sco_moduleimpl.moduleimpl_withmodule_list(
- formsemestre_id=formsemestre_id, sort_by_ue=True
+ can_change = (
+ authuser.has_permission(Permission.ScoEtudInscrit) and formsemestre.etat
)
- # Decrit les inscriptions aux modules:
+
+ # Décrit les inscriptions aux modules:
commons = [] # modules communs a tous les etuds du semestre
options = [] # modules ou seuls quelques etudiants sont inscrits
- for mod in Mlist:
+ mod_description = {} # modimplid : str
+ mod_nb_inscrits = {} # modimplid : int
+ for modimpl in formsemestre.modimpls_sorted:
tous_inscrits, nb_inscrits, descr = descr_inscrs_module(
- sem,
- mod["moduleimpl_id"],
+ modimpl.id,
set_all,
partitions,
- partitions_etud_groups,
)
if tous_inscrits:
- commons.append(mod)
+ commons.append(modimpl)
else:
- mod["descri"] = descr
- mod["nb_inscrits"] = nb_inscrits
- options.append(mod)
+ mod_description[modimpl.id] = descr
+ mod_nb_inscrits[modimpl.id] = nb_inscrits
+ options.append(modimpl)
+
# Page HTML:
H = [html_sco_header.html_sem_header("Inscriptions aux modules du semestre")]
- H.append("Inscrits au semestre: %d étudiants " % len(inscrits))
+ H.append(f"Inscrits au semestre: {len(inscrits)} étudiants ")
if options:
H.append("Modules auxquels tous les étudiants ne sont pas inscrits: ")
H.append(
'")
else:
H.append(
- 'Tous les étudiants sont inscrits à tous les modules. '
+ """Tous les étudiants sont inscrits à tous les modules. """
)
if commons:
H.append(
- "Modules communs (auxquels tous les étudiants sont inscrits): "
+ """Modules communs (auxquels tous les étudiants sont inscrits):
+
+