forked from ScoDoc/ScoDoc
BUT: Partition de parcours et inscriptions
This commit is contained in:
parent
5af4b5bed6
commit
45449f0465
@ -76,5 +76,6 @@ from app.models.but_refcomp import (
|
||||
ApcCompetence,
|
||||
ApcSituationPro,
|
||||
ApcAppCritique,
|
||||
ApcParcours,
|
||||
)
|
||||
from app.models.config import ScoDocSiteConfig
|
||||
|
@ -5,6 +5,7 @@
|
||||
import datetime
|
||||
from functools import cached_property
|
||||
|
||||
from flask import flash
|
||||
import flask_sqlalchemy
|
||||
from sqlalchemy.sql import text
|
||||
|
||||
@ -13,6 +14,7 @@ from app import log
|
||||
from app.models import APO_CODE_STR_LEN
|
||||
from app.models import SHORT_STR_LEN
|
||||
from app.models import CODE_STR_LEN
|
||||
from app.models.groups import GroupDescr, Partition
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.models.but_refcomp import ApcParcours
|
||||
@ -480,6 +482,85 @@ class FormSemestre(db.Model):
|
||||
"""Map { etudid : inscription } (incluant DEM et DEF)"""
|
||||
return {ins.etud.id: ins for ins in self.inscriptions}
|
||||
|
||||
def setup_parcours_groups(self) -> None:
|
||||
"""Vérifie et créee si besoin la partition et les groupes de parcours BUT."""
|
||||
if not self.formation.is_apc():
|
||||
return
|
||||
partition = Partition.query.filter_by(
|
||||
formsemestre_id=self.id, partition_name=scu.PARTITION_PARCOURS
|
||||
).first()
|
||||
if partition is None:
|
||||
# Création de la partition de parcours
|
||||
partition = Partition(
|
||||
formsemestre_id=self.id,
|
||||
partition_name=scu.PARTITION_PARCOURS,
|
||||
numero=-1,
|
||||
)
|
||||
db.session.add(partition)
|
||||
db.session.flush() # pour avoir un id
|
||||
flash(f"Partition Parcours créée.")
|
||||
|
||||
for parcour in self.parcours:
|
||||
if parcour.code:
|
||||
group = GroupDescr.query.filter_by(
|
||||
partition_id=partition.id, group_name=parcour.code
|
||||
).first()
|
||||
if not group:
|
||||
partition.groups.append(GroupDescr(group_name=parcour.code))
|
||||
db.session.commit()
|
||||
|
||||
def update_inscriptions_parcours_from_groups(self) -> None:
|
||||
"""Met à jour les inscriptions dans les parcours du semestres en
|
||||
fonction des groupes de parcours.
|
||||
Les groupes de parcours sont ceux de la partition scu.PARTITION_PARCOURS
|
||||
et leur nom est le code du parcours (eg "Cyber").
|
||||
"""
|
||||
partition = Partition.query.filter_by(
|
||||
formsemestre_id=self.id, partition_name=scu.PARTITION_PARCOURS
|
||||
).first()
|
||||
if partition is None: # pas de partition de parcours
|
||||
return
|
||||
|
||||
# Efface les inscriptions aux parcours:
|
||||
db.session.execute(
|
||||
text(
|
||||
"""UPDATE notes_formsemestre_inscription
|
||||
SET parcour_id=NULL
|
||||
WHERE formsemestre_id=:formsemestre_id
|
||||
"""
|
||||
),
|
||||
{
|
||||
"formsemestre_id": self.id,
|
||||
},
|
||||
)
|
||||
# Inscrit les étudiants des groupes de parcours:
|
||||
for group in partition.groups:
|
||||
query = ApcParcours.query.filter_by(code=group.group_name)
|
||||
if query.count() != 1:
|
||||
log(
|
||||
f"""update_inscriptions_parcours_from_groups: {
|
||||
query.count()} parcours with code {group.group_name}"""
|
||||
)
|
||||
continue
|
||||
parcour = query.first()
|
||||
db.session.execute(
|
||||
text(
|
||||
"""UPDATE notes_formsemestre_inscription ins
|
||||
SET parcour_id=:parcour_id
|
||||
FROM group_membership gm
|
||||
WHERE formsemestre_id=:formsemestre_id
|
||||
AND gm.etudid = ins.etudid
|
||||
AND gm.group_id = :group_id
|
||||
"""
|
||||
),
|
||||
{
|
||||
"formsemestre_id": self.id,
|
||||
"parcour_id": parcour.id,
|
||||
"group_id": group.id,
|
||||
},
|
||||
)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
# Association id des utilisateurs responsables (aka directeurs des etudes) du semestre
|
||||
notes_formsemestre_responsables = db.Table(
|
||||
|
@ -141,7 +141,9 @@ def do_formsemestre_list(*a, **kw):
|
||||
|
||||
|
||||
def _formsemestre_enrich(sem):
|
||||
"""Ajoute champs souvent utiles: titre + annee et dateord (pour tris)"""
|
||||
"""Ajoute champs souvent utiles: titre + annee et dateord (pour tris).
|
||||
XXX obsolete: préférer formsemestre.to_dict() ou, mieux, les méthodes de FormSemestre.
|
||||
"""
|
||||
# imports ici pour eviter refs circulaires
|
||||
from app.scodoc import sco_formsemestre_edit
|
||||
|
||||
|
@ -548,7 +548,8 @@ def do_formsemestre_createwithmodules(edit=False):
|
||||
"allowed_values": [
|
||||
str(parcour.id) for parcour in ref_comp.parcours
|
||||
],
|
||||
"explanation": "Parcours proposés dans ce semestre.",
|
||||
"explanation": """Parcours proposés dans ce semestre.
|
||||
S'il s'agit d'un semestre de "tronc commun", ne pas indiquer de parcours.""",
|
||||
},
|
||||
)
|
||||
]
|
||||
@ -905,7 +906,7 @@ def do_formsemestre_createwithmodules(edit=False):
|
||||
modargs, formsemestre_id=formsemestre_id
|
||||
)
|
||||
mod = sco_edit_module.module_list({"module_id": module_id})[0]
|
||||
# --- Assocation des parcours
|
||||
# --- Association des parcours
|
||||
formsemestre = FormSemestre.query.get(formsemestre_id)
|
||||
if "parcours" in tf[2]:
|
||||
formsemestre.parcours = [
|
||||
@ -914,6 +915,8 @@ def do_formsemestre_createwithmodules(edit=False):
|
||||
]
|
||||
db.session.add(formsemestre)
|
||||
db.session.commit()
|
||||
# --- Crée ou met à jour les groupes de parcours BUT
|
||||
formsemestre.setup_parcours_groups()
|
||||
# --- Fin
|
||||
if edit:
|
||||
if msg:
|
||||
|
@ -581,14 +581,20 @@ def formsemestre_recap_parcours_table(
|
||||
else:
|
||||
pm = plusminus % sem["formsemestre_id"]
|
||||
|
||||
H.append(
|
||||
'<td class="rcp_type_sem" style="background-color:%s;">%s%s</td>'
|
||||
% (bgcolor, num_sem, pm)
|
||||
inscr = formsemestre.etuds_inscriptions.get(etudid)
|
||||
parcours_name = (
|
||||
f' <span class="code_parcours">{inscr.parcour.code}</span>'
|
||||
if (inscr and inscr.parcour)
|
||||
else ""
|
||||
)
|
||||
H.append('<td class="datedebut">%(mois_debut)s</td>' % sem)
|
||||
H.append(
|
||||
'<td class="rcp_titre_sem"><a class="formsemestre_status_link" href="%sformsemestre_bulletinetud?formsemestre_id=%s&etudid=%s" title="Bulletin de notes">%s</a></td>'
|
||||
% (a_url, sem["formsemestre_id"], etudid, sem["titreannee"])
|
||||
f"""
|
||||
<td class="rcp_type_sem" style="background-color:{bgcolor};">{num_sem}{pm}</td>
|
||||
<td class="datedebut">{sem['mois_debut']}</td>
|
||||
<td class="rcp_titre_sem"><a class="formsemestre_status_link"
|
||||
href="{a_url}formsemestre_bulletinetud?formsemestre_id={formsemestre.id}&etudid={etudid}"
|
||||
title="Bulletin de notes">{formsemestre.titre_annee()}{parcours_name}</a></td>
|
||||
"""
|
||||
)
|
||||
if decision_sem:
|
||||
H.append('<td class="rcp_dec">%s</td>' % decision_sem["code"])
|
||||
|
@ -107,14 +107,19 @@ def get_group(group_id: int):
|
||||
return r[0]
|
||||
|
||||
|
||||
def group_delete(group, force=False):
|
||||
def group_delete(group_id: int):
|
||||
"""Delete a group."""
|
||||
# if not group['group_name'] and not force:
|
||||
# raise ValueError('cannot suppress this group')
|
||||
# remove memberships:
|
||||
ndb.SimpleQuery("DELETE FROM group_membership WHERE group_id=%(group_id)s", group)
|
||||
ndb.SimpleQuery(
|
||||
"DELETE FROM group_membership WHERE group_id=%(group_id)s",
|
||||
{"group_id": group_id},
|
||||
)
|
||||
# delete group:
|
||||
ndb.SimpleQuery("DELETE FROM group_descr WHERE id=%(group_id)s", group)
|
||||
ndb.SimpleQuery(
|
||||
"DELETE FROM group_descr WHERE id=%(group_id)s", {"group_id": group_id}
|
||||
)
|
||||
|
||||
|
||||
def get_partition(partition_id):
|
||||
@ -690,7 +695,12 @@ def change_etud_group_in_partition(etudid, group_id, partition=None):
|
||||
% (formsemestre_id, partition["partition_name"], group["group_name"]),
|
||||
)
|
||||
cnx.commit()
|
||||
# 4- invalidate cache
|
||||
|
||||
# 5- Update parcours
|
||||
formsemestre = FormSemestre.query.get(formsemestre_id)
|
||||
formsemestre.update_inscriptions_parcours_from_groups()
|
||||
|
||||
# 6- invalidate cache
|
||||
sco_cache.invalidate_formsemestre(
|
||||
formsemestre_id=formsemestre_id
|
||||
) # > change etud group
|
||||
@ -720,7 +730,7 @@ def setGroups(
|
||||
return response
|
||||
|
||||
partition = get_partition(partition_id)
|
||||
if not partition["group_editable"]:
|
||||
if not partition["groups_editable"]:
|
||||
msg = "setGroups: partition non editable"
|
||||
log(msg)
|
||||
return xml_error(msg, code=403)
|
||||
@ -796,6 +806,10 @@ def setGroups(
|
||||
for etudid in fs[1:-1]:
|
||||
change_etud_group_in_partition(etudid, group_id, partition)
|
||||
|
||||
# Update parcours
|
||||
formsemestre = FormSemestre.query.get(formsemestre_id)
|
||||
formsemestre.update_inscriptions_parcours_from_groups()
|
||||
|
||||
data = (
|
||||
'<?xml version="1.0" encoding="utf-8"?><response>Groupes enregistrés</response>'
|
||||
)
|
||||
@ -835,21 +849,18 @@ def delete_group(group_id, partition_id=None):
|
||||
affectation aux groupes)
|
||||
partition_id est optionnel et ne sert que pour verifier que le groupe
|
||||
est bien dans cette partition.
|
||||
S'il s'agit d'un groupe de parcours, affecte l'inscription des étudiants aux parcours.
|
||||
"""
|
||||
group = get_group(group_id)
|
||||
group = GroupDescr.query.get_or_404(group_id)
|
||||
if partition_id:
|
||||
if partition_id != group["partition_id"]:
|
||||
if partition_id != group.partition_id:
|
||||
raise ValueError("inconsistent partition/group")
|
||||
else:
|
||||
partition_id = group["partition_id"]
|
||||
partition = get_partition(partition_id)
|
||||
if not sco_permissions_check.can_change_groups(partition["formsemestre_id"]):
|
||||
if not sco_permissions_check.can_change_groups(group.partition.formsemestre_id):
|
||||
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
|
||||
log(
|
||||
"delete_group: group_id=%s group_name=%s partition_name=%s"
|
||||
% (group_id, group["group_name"], partition["partition_name"])
|
||||
)
|
||||
group_delete(group)
|
||||
log(f"delete_group: group={group:r} partition={group.partition}")
|
||||
formsemestre = group.partition.formsemestre
|
||||
group_delete(group.id)
|
||||
formsemestre.update_inscriptions_parcours_from_groups()
|
||||
|
||||
|
||||
def partition_create(
|
||||
@ -1097,11 +1108,14 @@ def partition_set_attr(partition_id, attr, value):
|
||||
|
||||
def partition_delete(partition_id, force=False, redirect=1, dialog_confirmed=False):
|
||||
"""Suppress a partition (and all groups within).
|
||||
default partition cannot be suppressed (unless force)"""
|
||||
The default partition cannot be suppressed (unless force).
|
||||
Si la partition de parcours est supprimée, les étudiants sont désinscrits des parcours.
|
||||
"""
|
||||
partition = get_partition(partition_id)
|
||||
formsemestre_id = partition["formsemestre_id"]
|
||||
if not sco_permissions_check.can_change_groups(formsemestre_id):
|
||||
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
|
||||
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
|
||||
if not partition["partition_name"] and not force:
|
||||
raise ValueError("cannot suppress this partition")
|
||||
@ -1127,10 +1141,12 @@ def partition_delete(partition_id, force=False, redirect=1, dialog_confirmed=Fal
|
||||
log("partition_delete: partition_id=%s" % partition_id)
|
||||
# 1- groups
|
||||
for group in groups:
|
||||
group_delete(group, force=force)
|
||||
group_delete(group["group_id"])
|
||||
# 2- partition
|
||||
partitionEditor.delete(cnx, partition_id)
|
||||
|
||||
formsemestre.update_inscriptions_parcours_from_groups()
|
||||
|
||||
# redirect to partition edit page:
|
||||
if redirect:
|
||||
return flask.redirect(
|
||||
@ -1214,7 +1230,8 @@ def partition_rename(partition_id):
|
||||
"default": partition["partition_name"],
|
||||
"allow_null": False,
|
||||
"size": 12,
|
||||
"validator": lambda val, _: len(val) < SHORT_STR_LEN,
|
||||
"validator": lambda val, _: (len(val) < SHORT_STR_LEN)
|
||||
and (val != scu.PARTITION_PARCOURS),
|
||||
},
|
||||
),
|
||||
),
|
||||
@ -1246,6 +1263,8 @@ def partition_set_name(partition_id, partition_name, redirect=1):
|
||||
partition = get_partition(partition_id)
|
||||
if partition["partition_name"] is None:
|
||||
raise ValueError("can't set a name to default partition")
|
||||
if partition_name == scu.PARTITION_PARCOURS:
|
||||
raise ScoValueError(f"nom de partition {scu.PARTITION_PARCOURS} réservé.")
|
||||
formsemestre_id = partition["formsemestre_id"]
|
||||
|
||||
# check unicity
|
||||
@ -1415,7 +1434,7 @@ def groups_auto_repartition(partition_id=None):
|
||||
group_names = sorted(set([x.strip() for x in groupNames.split(",")]))
|
||||
# Détruit les groupes existant de cette partition
|
||||
for old_group in get_partition_groups(partition):
|
||||
group_delete(old_group)
|
||||
group_delete(old_group["group_id"])
|
||||
# Crée les nouveaux groupes
|
||||
group_ids = []
|
||||
for group_name in group_names:
|
||||
|
@ -43,6 +43,7 @@ def affect_groups(partition_id):
|
||||
formsemestre_id = partition["formsemestre_id"]
|
||||
if not sco_groups.sco_permissions_check.can_change_groups(formsemestre_id):
|
||||
raise AccessDenied("vous n'avez pas la permission de modifier les groupes")
|
||||
partition.formsemestre.setup_parcours_groups()
|
||||
return render_template(
|
||||
"scolar/affect_groups.html",
|
||||
sco_header=html_sco_header.sco_header(
|
||||
|
@ -2267,6 +2267,14 @@ span.missing_value {
|
||||
color: red;
|
||||
}
|
||||
|
||||
span.code_parcours {
|
||||
color: white;
|
||||
background-color: rgb(254, 95, 246);
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
tr#tf_module_parcours>td {
|
||||
background-color: rgb(229, 229, 229);
|
||||
}
|
||||
|
@ -918,18 +918,7 @@ def create_partition_parcours(formsemestre_id):
|
||||
"""Création d'une partitions nommée "Parcours" (PARTITION_PARCOURS)
|
||||
avec un groupe par parcours."""
|
||||
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if scu.PARTITION_PARCOURS in (p.partition_name for p in formsemestre.partitions):
|
||||
flash(f"""Partition "{scu.PARTITION_PARCOURS}" déjà existante""")
|
||||
else:
|
||||
partition_id = sco_groups.partition_create(
|
||||
formsemestre_id, partition_name=scu.PARTITION_PARCOURS, redirect=False
|
||||
)
|
||||
n = 0
|
||||
for parcour in formsemestre.parcours:
|
||||
if parcour.code:
|
||||
_ = sco_groups.create_group(partition_id, group_name=parcour.code)
|
||||
n += 1
|
||||
flash(f"Partition Parcours créée avec {n} groupes.")
|
||||
formsemestre.setup_parcours_groups()
|
||||
return flask.redirect(
|
||||
url_for(
|
||||
"scolar.edit_partition_form",
|
||||
|
Loading…
Reference in New Issue
Block a user