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.departements import Departement
|
||||||
from app.models.etudiants import Identite
|
from app.models.etudiants import Identite
|
||||||
from app.models.evaluations import Evaluation
|
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.formations import Formation
|
||||||
from app.models.groups import GroupDescr, Partition
|
from app.models.groups import GroupDescr, Partition
|
||||||
from app.models.moduleimpls import (
|
from app.models.moduleimpls import (
|
||||||
@ -45,9 +45,10 @@ from app.models.moduleimpls import (
|
|||||||
notes_modules_enseignants,
|
notes_modules_enseignants,
|
||||||
)
|
)
|
||||||
from app.models.modules import Module
|
from app.models.modules import Module
|
||||||
|
from app.models.scolar_event import ScolarEvent
|
||||||
from app.models.ues import UniteEns
|
from app.models.ues import UniteEns
|
||||||
from app.models.validations import ScolarFormSemestreValidation
|
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_exceptions import ScoValueError
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc.sco_utils import MONTH_NAMES_ABBREV, translate_assiduites_metric
|
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"))
|
formation_id = db.Column(db.Integer, db.ForeignKey("notes_formations.id"))
|
||||||
semestre_id = db.Column(db.Integer, nullable=False, default=1, server_default="1")
|
semestre_id = db.Column(db.Integer, nullable=False, default=1, server_default="1")
|
||||||
titre = db.Column(db.Text(), nullable=False)
|
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_debut = db.Column(db.Date(), nullable=False)
|
||||||
date_fin = db.Column(db.Date(), nullable=False) # jour inclus
|
date_fin = db.Column(db.Date(), nullable=False) # jour inclus
|
||||||
edt_id: str | None = db.Column(db.Text(), index=True, nullable=True)
|
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}
|
codes |= {x.strip() for x in self.elt_passage_apo.split(",") if x}
|
||||||
return codes
|
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
|
"""Liste des étudiants inscrits à ce semestre
|
||||||
Si include_demdef, tous les étudiants, avec les démissionnaires
|
Si include_demdef, tous les étudiants, avec les démissionnaires
|
||||||
et défaillants.
|
et défaillants.
|
||||||
|
Si etats, seuls les étudiants dans l'un des états indiqués.
|
||||||
Si order, tri par clé sort_key
|
Si order, tri par clé sort_key
|
||||||
"""
|
"""
|
||||||
if include_demdef:
|
if include_demdef:
|
||||||
etuds = [ins.etud for ins in self.inscriptions]
|
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]
|
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:
|
if order:
|
||||||
etuds.sort(key=lambda e: e.sort_key)
|
etuds.sort(key=lambda e: e.sort_key)
|
||||||
return etuds
|
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(
|
def get_partitions_list(
|
||||||
self, with_default=True, only_listed=False
|
self, with_default=True, only_listed=False
|
||||||
) -> list[Partition]:
|
) -> list[Partition]:
|
||||||
|
@ -53,6 +53,7 @@ _formsemestreEditor = ndb.EditableTable(
|
|||||||
"semestre_id",
|
"semestre_id",
|
||||||
"formation_id",
|
"formation_id",
|
||||||
"titre",
|
"titre",
|
||||||
|
"capacite_accueil",
|
||||||
"date_debut",
|
"date_debut",
|
||||||
"date_fin",
|
"date_fin",
|
||||||
"gestion_compensation",
|
"gestion_compensation",
|
||||||
|
@ -350,8 +350,6 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
"labels": modalites_titles,
|
"labels": modalites_titles,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
|
||||||
modform.append(
|
|
||||||
(
|
(
|
||||||
"semestre_id",
|
"semestre_id",
|
||||||
{
|
{
|
||||||
@ -367,10 +365,21 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
"attributes": ['onchange="change_semestre_id();"'] if is_apc else "",
|
"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()
|
etapes = sco_portal_apogee.get_etapes_apogee_dept()
|
||||||
# Propose les etapes renvoyées par le portail
|
# 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}
|
etapes_set = {et[0] for et in etapes}
|
||||||
if edit:
|
if edit:
|
||||||
for etape_vdi in formsemestre.etapes_apo_vdi():
|
for etape_vdi in formsemestre.etapes_apo_vdi():
|
||||||
|
@ -85,43 +85,6 @@ def do_formsemestre_inscription_listinscrits(formsemestre_id):
|
|||||||
return r
|
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):
|
def do_formsemestre_inscription_delete(oid, formsemestre_id=None):
|
||||||
"delete formsemestre_inscription"
|
"delete formsemestre_inscription"
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
@ -283,20 +246,18 @@ def do_formsemestre_inscription_with_modules(
|
|||||||
"""Inscrit cet etudiant à ce semestre et TOUS ses modules STANDARDS
|
"""Inscrit cet etudiant à ce semestre et TOUS ses modules STANDARDS
|
||||||
(donc sauf le sport)
|
(donc sauf le sport)
|
||||||
Si dept_id est spécifié, utilise ce département au lieu du courant.
|
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 []
|
group_ids = group_ids or []
|
||||||
if isinstance(group_ids, int):
|
if isinstance(group_ids, int):
|
||||||
group_ids = [group_ids]
|
group_ids = [group_ids]
|
||||||
# Check that all groups exist before creating the inscription
|
# Check that all groups exist before creating the inscription
|
||||||
groups = [GroupDescr.query.get_or_404(group_id) for group_id in group_ids]
|
groups = [GroupDescr.query.get_or_404(group_id) for group_id in group_ids]
|
||||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id, dept_id=dept_id)
|
formsemestre = FormSemestre.get_formsemestre(formsemestre_id, dept_id=dept_id)
|
||||||
# inscription au semestre
|
# Inscription au semestre
|
||||||
args = {"formsemestre_id": formsemestre_id, "etudid": etudid}
|
args = {"formsemestre_id": formsemestre_id, "etudid": etudid}
|
||||||
if etat is not None:
|
formsemestre.inscrit_etudiant(etud, etat=etat, etape=etape, method=method)
|
||||||
args["etat"] = etat
|
|
||||||
if etape is not None:
|
|
||||||
args["etape"] = etape
|
|
||||||
do_formsemestre_inscription_create(args, method=method)
|
|
||||||
log(
|
log(
|
||||||
f"""do_formsemestre_inscription_with_modules: etudid={
|
f"""do_formsemestre_inscription_with_modules: etudid={
|
||||||
etudid} formsemestre_id={formsemestre_id}"""
|
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(
|
html_sco_header.html_sem_header(
|
||||||
page_title, with_page_header=False, with_h2=False
|
page_title, with_page_header=False, with_h2=False
|
||||||
),
|
),
|
||||||
f"""<table>
|
f"""<table class="formsemestre_status_head">
|
||||||
<tr><td class="fichetitre2">Formation: </td><td>
|
<tr><td class="fichetitre2">Formation : </td><td>
|
||||||
<a href="{url_for('notes.ue_table',
|
<a href="{url_for('notes.ue_table',
|
||||||
scodoc_dept=g.scodoc_dept, formation_id=formsemestre.formation.id)}"
|
scodoc_dept=g.scodoc_dept, formation_id=formsemestre.formation.id)}"
|
||||||
class="discretelink" title="Formation {
|
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()
|
sem_parcours = formsemestre.get_parcours_apc()
|
||||||
H.append(
|
H.append(
|
||||||
f"""
|
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>
|
<td style="color: blue;">{', '.join(parcours.code for parcours in sem_parcours)}</td>
|
||||||
</tr>
|
</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)
|
evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre)
|
||||||
H.append(
|
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
|
% evals
|
||||||
)
|
)
|
||||||
if evals["last_modif"]:
|
if evals["last_modif"]:
|
||||||
|
@ -1844,6 +1844,15 @@ div.formsemestre_status {
|
|||||||
/* EMO_WARNING, "⚠️" */
|
/* EMO_WARNING, "⚠️" */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.formsemestre_status_head {
|
||||||
|
border-collapse: collapse;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
table.formsemestre_status_head tr td:nth-child(2) {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
table.formsemestre_status {
|
table.formsemestre_status {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""FormSemestreDescription
|
"""FormSemestreDescription et capacité d'accueil
|
||||||
|
|
||||||
Revision ID: 2640b7686de6
|
Revision ID: 2640b7686de6
|
||||||
Revises: f6cb3d4e44ec
|
Revises: f6cb3d4e44ec
|
||||||
@ -32,7 +32,11 @@ def upgrade():
|
|||||||
),
|
),
|
||||||
sa.PrimaryKeyConstraint("id"),
|
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():
|
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")
|
op.drop_table("notes_formsemestre_description")
|
||||||
|
Loading…
Reference in New Issue
Block a user