BUT/parcours: inscriptions, visualisation des parcours

This commit is contained in:
Emmanuel Viennet 2022-08-31 19:14:21 +02:00
parent 8411292255
commit d78fb13821
10 changed files with 130 additions and 99 deletions

View File

@ -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

View File

@ -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(

View File

@ -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})

View File

@ -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:

View File

@ -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)

View File

@ -105,8 +105,9 @@ def formsemestre_editwithmodules(formsemestre_id):
]
if not sem["etat"]:
H.append(
"""<p>%s<b>Ce semestre est verrouillé.</b></p>"""
% scu.icontag("lock_img", border="0", title="Semestre verrouillé")
f"""<p>{scu.icontag(
"lock_img", border="0", title="Semestre verrouillé")
}<b>Ce semestre est verrouillé.</b></p>"""
)
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="%s" %s>' % (select_name, disabled)
fcg = f'<select name="{select_name}" {disabled}>'
default_group_id = sco_groups.get_default_group(formsemestre_id)
fcg += '<option value="%s" %s>Tous</option>' % (
default_group_id,
opt_selected(default_group_id),
)
fcg += '<option value="" %s>Aucun</option>' % 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 += '<option value="%s" %s>%s %s</option>' % (
group["group_id"],
opt_selected(group["group_id"]),
p["partition_name"],
group["group_name"],
)
fcg += f"""<option value="{default_group_id}" {
opt_selected(default_group_id)}>Tous</option>"""
fcg += f'<option value="" {opt_selected("")}>Aucun</option>'
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"""<option value="{group.id}" {selected
}>{partition.partition_name} {group.group_name}</option>"""
fcg += "</select>"
itemtemplate = f"""<tr {tr_class}>
<td class="tf-fieldlabel">%(label)s</td>
@ -657,12 +669,12 @@ def do_formsemestre_createwithmodules(edit=False):
</tr>"""
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"

View File

@ -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

View File

@ -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

View File

@ -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("<h3>Inscrits au semestre: %d étudiants</h3>" % len(inscrits))
H.append(f"<h3>Inscrits au semestre: {len(inscrits)} étudiants</h3>")
if options:
H.append("<h3>Modules auxquels tous les étudiants ne sont pas inscrits:</h3>")
H.append(
'<table class="formsemestre_status formsemestre_inscr"><tr><th>UE</th><th>Code</th><th>Inscrits</th><th></th></tr>'
)
for mod in options:
for modimpl in options:
if can_change:
c_link = '<a class="discretelink" href="moduleimpl_inscriptions_edit?moduleimpl_id=%s">%s</a>' % (
mod["moduleimpl_id"],
mod["descri"] or "<i>(inscrire des étudiants)</i>",
)
c_link = f"""<a class="discretelink" href="{url_for(
'notes.moduleimpl_inscriptions_edit',
scodoc_dept=g.scodoc_dept,
moduleimpl_id=modimpl.id)
}">{mod_description[modimpl.id] or "<i>(inscrire des étudiants)</i>"}</a>
"""
else:
c_link = mod["descri"]
c_link = mod_description[modimpl.id]
H.append(
'<tr class="formsemestre_status"><td>%s</td><td class="formsemestre_status_code">%s</td><td class="formsemestre_status_inscrits">%s</td><td>%s</td></tr>'
% (
mod["ue"]["acronyme"] or "",
mod["module"]["code"] or "(module sans code)",
mod["nb_inscrits"],
c_link,
)
f"""<tr class="formsemestre_status"><td>{
modimpl.module.ue.acronyme or ""
}</td><td class="formsemestre_status_code">{
modimpl.module.code or "(module sans code)"
}</td><td class="formsemestre_status_inscrits">{
mod_nb_inscrits[modimpl.id]}</td><td>{c_link}</td></tr>"""
)
H.append("</table>")
else:
H.append(
'<span style="font-size:110%; font-style:italic; color: red;"">Tous les étudiants sont inscrits à tous les modules.</span>'
"""<span style="font-size:110%; font-style:italic; color: red;"
>Tous les étudiants sont inscrits à tous les modules.</span>"""
)
if commons:
H.append(
"<h3>Modules communs (auxquels tous les étudiants sont inscrits):</h3>"
"""<h3>Modules communs (auxquels tous les étudiants sont inscrits):</h3>
<table class="formsemestre_status formsemestre_inscr">
<tr><th>UE</th><th>Code</th><th>Module</th>"""
)
H.append(
'<table class="formsemestre_status formsemestre_inscr"><tr><th>UE</th><th>Code</th><th>Module</th></tr>'
)
for mod in commons:
if is_apc:
H.append("<th>Parcours</th>")
H.append("""</tr>""")
for modimpl in commons:
if can_change:
c_link = (
'<a class="discretelink" href="moduleimpl_inscriptions_edit?moduleimpl_id=%s">%s</a>'
% (mod["moduleimpl_id"], mod["module"]["titre"])
)
c_link = f"""<a class="discretelink" href="{
url_for("notes.moduleimpl_inscriptions_edit",
scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id)
}">{modimpl.module.titre}</a>"""
else:
c_link = mod["module"]["titre"]
c_link = modimpl.module.titre
H.append(
'<tr class="formsemestre_status_green"><td>%s</td><td class="formsemestre_status_code">%s</td><td>%s</td></tr>'
% (
mod["ue"]["acronyme"],
mod["module"]["code"] or "(module sans code)",
c_link,
)
f"""<tr class="formsemestre_status_green"><td>{
modimpl.module.ue.acronyme or ""
}</td><td class="formsemestre_status_code">{
modimpl.module.code or "(module sans code)"
}</td><td>{c_link}</td>"""
)
if is_apc:
H.append(
f"""<td><em>{', '.join(p.code for p in modimpl.module.parcours)}</em></td>"""
)
H.append("</tr>")
H.append("</table>")
# Etudiants "dispensés" d'une UE (capitalisée)
@ -427,9 +435,7 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
return "\n".join(H)
def descr_inscrs_module(
sem, moduleimpl_id, set_all, partitions, partitions_etud_groups
):
def descr_inscrs_module(moduleimpl_id, set_all, partitions):
"""returns tous_inscrits, nb_inscrits, descr"""
ins = sco_moduleimpl.do_moduleimpl_inscription_list(moduleimpl_id=moduleimpl_id)
set_m = set([x["etudid"] for x in ins]) # ens. des inscrits au module

View File

@ -3173,6 +3173,7 @@ def check_integrity_all():
def moduleimpl_list(
moduleimpl_id=None, formsemestre_id=None, module_id=None, format="json"
):
# TODO DEPRECATED A RETIRER
try:
data = sco_moduleimpl.moduleimpl_list(
moduleimpl_id=moduleimpl_id,
@ -3193,6 +3194,7 @@ def moduleimpl_withmodule_list(
moduleimpl_id=None, formsemestre_id=None, module_id=None, format="json"
):
"""API ScoDoc 7"""
# TODO DEPRECATED A RETIRER
data = sco_moduleimpl.moduleimpl_withmodule_list(
moduleimpl_id=moduleimpl_id,
formsemestre_id=formsemestre_id,