forked from ScoDoc/ScoDoc
Capacité d'accueil des formsemestre
This commit is contained in:
parent
525d0446cc
commit
3d34f9330d
@ -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]:
|
||||
|
@ -53,6 +53,7 @@ _formsemestreEditor = ndb.EditableTable(
|
||||
"semestre_id",
|
||||
"formation_id",
|
||||
"titre",
|
||||
"capacite_accueil",
|
||||
"date_debut",
|
||||
"date_fin",
|
||||
"gestion_compensation",
|
||||
|
@ -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():
|
||||
|
@ -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}"""
|
||||
|
@ -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 : </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 : </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 : </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 : </td>
|
||||
<td> %(nb_evals_completes)s ok, %(nb_evals_en_cours)s en cours, %(nb_evals_vides)s vides"""
|
||||
% evals
|
||||
)
|
||||
if evals["last_modif"]:
|
||||
|
@ -1844,6 +1844,15 @@ div.formsemestre_status {
|
||||
/* EMO_WARNING, "⚠️" */
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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")
|
||||
|
Loading…
Reference in New Issue
Block a user