Capacité d'accueil des formsemestre

This commit is contained in:
ilona 2024-08-11 23:24:40 +02:00
parent 525d0446cc
commit 3d34f9330d
7 changed files with 106 additions and 56 deletions

View File

@ -36,7 +36,7 @@ from app.models.config import ScoDocSiteConfig
from app.models.departements import Departement
from app.models.etudiants import Identite
from app.models.evaluations import Evaluation
from app.models.events import ScolarNews
from app.models.events import Scolog, ScolarNews
from app.models.formations import Formation
from app.models.groups import GroupDescr, Partition
from app.models.moduleimpls import (
@ -45,9 +45,10 @@ from app.models.moduleimpls import (
notes_modules_enseignants,
)
from app.models.modules import Module
from app.models.scolar_event import ScolarEvent
from app.models.ues import UniteEns
from app.models.validations import ScolarFormSemestreValidation
from app.scodoc import codes_cursus, sco_preferences
from app.scodoc import codes_cursus, sco_cache, sco_preferences
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_permissions import Permission
from app.scodoc.sco_utils import MONTH_NAMES_ABBREV, translate_assiduites_metric
@ -69,6 +70,8 @@ class FormSemestre(models.ScoDocModel):
formation_id = db.Column(db.Integer, db.ForeignKey("notes_formations.id"))
semestre_id = db.Column(db.Integer, nullable=False, default=1, server_default="1")
titre = db.Column(db.Text(), nullable=False)
# nb max d'inscriptions (non DEM), null si illimité:
capacite_accueil = db.Column(db.Integer, nullable=True)
date_debut = db.Column(db.Date(), nullable=False)
date_fin = db.Column(db.Date(), nullable=False) # jour inclus
edt_id: str | None = db.Column(db.Text(), index=True, nullable=True)
@ -1019,20 +1022,74 @@ class FormSemestre(models.ScoDocModel):
codes |= {x.strip() for x in self.elt_passage_apo.split(",") if x}
return codes
def get_inscrits(self, include_demdef=False, order=False) -> list[Identite]:
def get_inscrits(
self, include_demdef=False, order=False, etats: set | None = None
) -> list[Identite]:
"""Liste des étudiants inscrits à ce semestre
Si include_demdef, tous les étudiants, avec les démissionnaires
et défaillants.
Si etats, seuls les étudiants dans l'un des états indiqués.
Si order, tri par clé sort_key
"""
if include_demdef:
etuds = [ins.etud for ins in self.inscriptions]
else:
elif not etats:
etuds = [ins.etud for ins in self.inscriptions if ins.etat == scu.INSCRIT]
else:
etuds = [ins.etud for ins in self.inscriptions if ins.etat in etats]
if order:
etuds.sort(key=lambda e: e.sort_key)
return etuds
def inscrit_etudiant(
self,
etud: "Identite",
etat: str = scu.INSCRIT,
etape: str | None = None,
method: str | None = None,
) -> "FormSemestreInscription":
"""Inscrit l'étudiant au semestre, ou renvoie son inscription s'il l'est déjà.
Vérifie la capacité d'accueil si indiquée (non null): si le semestre est plein,
lève une exception. Génère un évènement et un log étudiant.
method: indique origine de l'inscription pour le log étudiant.
"""
# remplace ancien do_formsemestre_inscription_create()
if not self.etat: # check lock
raise ScoValueError("inscrit_etudiant: semestre verrouille")
inscr = FormSemestreInscription.query.filter_by(
formsemestre_id=self.id, etudid=etud.id
).first()
if inscr is not None:
return inscr
if self.capacite_accueil is not None:
inscriptions = self.get_inscrits(etats={scu.INSCRIT, scu.DEMISSION})
if len(inscriptions) >= self.capacite_accueil:
raise ScoValueError(
f"Semestre {self.titre} complet: {len(self.inscriptions)} inscrits"
)
inscr = FormSemestreInscription(
formsemestre_id=self.id, etudid=etud.id, etat=etat, etape=etape
)
db.session.add(inscr)
# Évènement
event = ScolarEvent(
etudid=etud.id,
formsemestre_id=self.id,
event_type="INSCRIPTION",
)
db.session.add(event)
# Log etudiant
Scolog.logdb(
method=method,
etudid=etud.id,
msg=f"inscription en semestre {self.titre_annee()}",
commit=True,
)
sco_cache.invalidate_formsemestre(formsemestre_id=self.id)
return inscr
def get_partitions_list(
self, with_default=True, only_listed=False
) -> list[Partition]:

View File

@ -53,6 +53,7 @@ _formsemestreEditor = ndb.EditableTable(
"semestre_id",
"formation_id",
"titre",
"capacite_accueil",
"date_debut",
"date_fin",
"gestion_compensation",

View File

@ -350,8 +350,6 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
"labels": modalites_titles,
},
),
]
modform.append(
(
"semestre_id",
{
@ -367,10 +365,21 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
"attributes": ['onchange="change_semestre_id();"'] if is_apc else "",
},
),
)
(
"capacite_accueil",
{
"title": "Capacité d'accueil",
"size": 4,
"explanation": "laisser vide si pas de limite au nombre d'inscrits non démissionnaires",
"type": "int",
"allow_null": True,
},
),
]
etapes = sco_portal_apogee.get_etapes_apogee_dept()
# Propose les etapes renvoyées par le portail
# et ajoute les étapes du semestre qui ne sont pas dans la liste (soit la liste a changé, soit l'étape a été ajoutée manuellement)
# et ajoute les étapes du semestre qui ne sont pas dans la liste
# (soit la liste a changé, soit l'étape a été ajoutée manuellement)
etapes_set = {et[0] for et in etapes}
if edit:
for etape_vdi in formsemestre.etapes_apo_vdi():

View File

@ -85,43 +85,6 @@ def do_formsemestre_inscription_listinscrits(formsemestre_id):
return r
def do_formsemestre_inscription_create(args, method=None):
"create a formsemestre_inscription (and sco event)"
cnx = ndb.GetDBConnexion()
log(f"do_formsemestre_inscription_create: args={args}")
sems = sco_formsemestre.do_formsemestre_list(
{"formsemestre_id": args["formsemestre_id"]}
)
if len(sems) != 1:
raise ScoValueError(f"code de semestre invalide: {args['formsemestre_id']}")
sem = sems[0]
# check lock
if not sem["etat"]:
raise ScoValueError("inscription: semestre verrouille")
#
r = _formsemestre_inscriptionEditor.create(cnx, args)
# Evenement
sco_etud.scolar_events_create(
cnx,
args={
"etudid": args["etudid"],
"event_date": time.strftime(scu.DATE_FMT),
"formsemestre_id": args["formsemestre_id"],
"event_type": "INSCRIPTION",
},
)
# Log etudiant
Scolog.logdb(
method=method,
etudid=args["etudid"],
msg=f"inscription en semestre {args['formsemestre_id']}",
commit=True,
)
#
sco_cache.invalidate_formsemestre(formsemestre_id=args["formsemestre_id"])
return r
def do_formsemestre_inscription_delete(oid, formsemestre_id=None):
"delete formsemestre_inscription"
cnx = ndb.GetDBConnexion()
@ -283,20 +246,18 @@ def do_formsemestre_inscription_with_modules(
"""Inscrit cet etudiant à ce semestre et TOUS ses modules STANDARDS
(donc sauf le sport)
Si dept_id est spécifié, utilise ce département au lieu du courant.
Vérifie la capacité d'accueil.
"""
etud = Identite.get_etud(etudid)
group_ids = group_ids or []
if isinstance(group_ids, int):
group_ids = [group_ids]
# Check that all groups exist before creating the inscription
groups = [GroupDescr.query.get_or_404(group_id) for group_id in group_ids]
formsemestre = FormSemestre.get_formsemestre(formsemestre_id, dept_id=dept_id)
# inscription au semestre
# Inscription au semestre
args = {"formsemestre_id": formsemestre_id, "etudid": etudid}
if etat is not None:
args["etat"] = etat
if etape is not None:
args["etape"] = etape
do_formsemestre_inscription_create(args, method=method)
formsemestre.inscrit_etudiant(etud, etat=etat, etape=etape, method=method)
log(
f"""do_formsemestre_inscription_with_modules: etudid={
etudid} formsemestre_id={formsemestre_id}"""

View File

@ -978,8 +978,8 @@ def formsemestre_status_head(formsemestre_id: int = None, page_title: str = None
html_sco_header.html_sem_header(
page_title, with_page_header=False, with_h2=False
),
f"""<table>
<tr><td class="fichetitre2">Formation: </td><td>
f"""<table class="formsemestre_status_head">
<tr><td class="fichetitre2">Formation&nbsp;: </td><td>
<a href="{url_for('notes.ue_table',
scodoc_dept=g.scodoc_dept, formation_id=formsemestre.formation.id)}"
class="discretelink" title="Formation {
@ -1002,15 +1002,24 @@ def formsemestre_status_head(formsemestre_id: int = None, page_title: str = None
sem_parcours = formsemestre.get_parcours_apc()
H.append(
f"""
<tr><td class="fichetitre2">Parcours: </td>
<tr><td class="fichetitre2">Parcours&nbsp;: </td>
<td style="color: blue;">{', '.join(parcours.code for parcours in sem_parcours)}</td>
</tr>
"""
)
if formsemestre.capacite_accueil is not None:
H.append(
f"""
<tr><td class="fichetitre2">Capacité d'accueil&nbsp;: </td>
<td>{formsemestre.capacite_accueil}</td>
</tr>
"""
)
evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre)
H.append(
'<tr><td class="fichetitre2">Évaluations: </td><td> %(nb_evals_completes)s ok, %(nb_evals_en_cours)s en cours, %(nb_evals_vides)s vides'
"""<tr><td class="fichetitre2">Évaluations&nbsp;: </td>
<td> %(nb_evals_completes)s ok, %(nb_evals_en_cours)s en cours, %(nb_evals_vides)s vides"""
% evals
)
if evals["last_modif"]:

View File

@ -1844,6 +1844,15 @@ div.formsemestre_status {
/* EMO_WARNING, "&#9888;&#65039;" */
}
table.formsemestre_status_head {
border-collapse: collapse;
}
table.formsemestre_status_head tr td:nth-child(2) {
padding-left: 1em;
}
table.formsemestre_status {
border-collapse: collapse;
}

View File

@ -1,4 +1,4 @@
"""FormSemestreDescription
"""FormSemestreDescription et capacité d'accueil
Revision ID: 2640b7686de6
Revises: f6cb3d4e44ec
@ -32,7 +32,11 @@ def upgrade():
),
sa.PrimaryKeyConstraint("id"),
)
with op.batch_alter_table("notes_formsemestre", schema=None) as batch_op:
batch_op.add_column(sa.Column("capacite_accueil", sa.Integer(), nullable=True))
def downgrade():
with op.batch_alter_table("notes_formsemestre", schema=None) as batch_op:
batch_op.drop_column("capacite_accueil")
op.drop_table("notes_formsemestre_description")