forked from ScoDoc/ScoDoc
Jury BUT: amélioration front et back. Voir #547. Tests YAML: refonte circuit jury. Cas lyon43. Tests ok.
This commit is contained in:
parent
5eed392400
commit
c833974569
@ -584,9 +584,10 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
|
||||
def compute_decisions_niveaux(self) -> dict[int, "DecisionsProposeesRCUE"]:
|
||||
"""Pour chaque niveau de compétence de cette année, construit
|
||||
le DecisionsProposeesRCUE,
|
||||
ou None s'il n'y en a pas
|
||||
le DecisionsProposeesRCUE, ou None s'il n'y en a pas
|
||||
(ne devrait pas arriver car compute_rcues_annee vérifie déjà cela).
|
||||
|
||||
Appelé à la construction du deca, donc avant décisions manuelles.
|
||||
Return: { niveau_id : DecisionsProposeesRCUE }
|
||||
"""
|
||||
# Retrouve le RCUE associé à chaque niveau
|
||||
@ -633,6 +634,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
|
||||
def record_form(self, form: dict):
|
||||
"""Enregistre les codes de jury en base
|
||||
à partir d'un dict représentant le formulaire jury BUT:
|
||||
form dict:
|
||||
- 'code_ue_1896' : 'AJ' code pour l'UE id 1896
|
||||
- 'code_rcue_6" : 'ADM' code pour le RCUE du niveau 6
|
||||
@ -642,7 +644,9 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
et qu'il n'y en a pas déjà, enregistre ceux par défaut.
|
||||
"""
|
||||
log("jury_but.DecisionsProposeesAnnee.record_form")
|
||||
with sco_cache.DeferredSemCacheManager():
|
||||
code_annee = None
|
||||
codes_rcues = [] # [ (dec_rcue, code), ... ]
|
||||
codes_ues = [] # [ (dec_ue, code), ... ]
|
||||
for key in form:
|
||||
code = form[key]
|
||||
# Codes d'UE
|
||||
@ -652,7 +656,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
dec_ue = self.decisions_ues.get(ue_id)
|
||||
if not dec_ue:
|
||||
raise ScoValueError(f"UE invalide ue_id={ue_id}")
|
||||
dec_ue.record(code)
|
||||
codes_ues.append((dec_ue, code))
|
||||
else:
|
||||
# Codes de RCUE
|
||||
m = re.match(r"^code_rcue_(\d+)$", key)
|
||||
@ -661,12 +665,20 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
dec_rcue = self.decisions_rcue_by_niveau.get(niveau_id)
|
||||
if not dec_rcue:
|
||||
raise ScoValueError(f"RCUE invalide niveau_id={niveau_id}")
|
||||
dec_rcue.record(code)
|
||||
codes_rcues.append((dec_rcue, code))
|
||||
elif key == "code_annee":
|
||||
# Code annuel
|
||||
self.record(code)
|
||||
code_annee = code
|
||||
|
||||
with sco_cache.DeferredSemCacheManager():
|
||||
# Enregistre les codes, dans l'ordre UE, RCUE, Année
|
||||
for dec_ue, code in codes_ues:
|
||||
dec_ue.record(code)
|
||||
for dec_rcue, code in codes_rcues:
|
||||
dec_rcue.record(code)
|
||||
self.record(code_annee)
|
||||
self.record_all()
|
||||
|
||||
db.session.commit()
|
||||
|
||||
def record(self, code: str, no_overwrite=False):
|
||||
@ -790,6 +802,15 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
msg=f"Validation année BUT{self.annee_but}: effacée",
|
||||
)
|
||||
db.session.delete(validation)
|
||||
# Efface éventuelle validation de semestre
|
||||
# (en principe inutilisées en BUT)
|
||||
# et autres UEs (en cas de changement d'architecture de formation depuis le jury ?)
|
||||
#
|
||||
for validation in ScolarFormSemestreValidation.query.filter_by(
|
||||
etudid=self.etud.id, formsemestre_id=self.formsemestre_id
|
||||
):
|
||||
db.session.delete(validation)
|
||||
|
||||
db.session.flush()
|
||||
self.invalidate_formsemestre_cache()
|
||||
|
||||
@ -878,6 +899,7 @@ class DecisionsProposeesRCUE(DecisionsProposees):
|
||||
inscription_etat: str = scu.INSCRIT,
|
||||
):
|
||||
super().__init__(etud=dec_prop_annee.etud)
|
||||
self.deca = dec_prop_annee
|
||||
self.rcue = rcue
|
||||
if rcue is None: # RCUE non dispo, eg un seul semestre
|
||||
self.codes = []
|
||||
@ -928,7 +950,11 @@ class DecisionsProposeesRCUE(DecisionsProposees):
|
||||
} codes={self.codes} explanation={self.explanation}"""
|
||||
|
||||
def record(self, code: str, no_overwrite=False):
|
||||
"""Enregistre le code"""
|
||||
"""Enregistre le code RCUE.
|
||||
Note:
|
||||
- si le RCUE est ADJ, les UE non validées sont passées à ADJ
|
||||
XXX on pourra imposer ici d'autres règles de cohérence
|
||||
"""
|
||||
if self.rcue is None:
|
||||
return # pas de RCUE a enregistrer
|
||||
if self.inscription_etat != scu.INSCRIT:
|
||||
@ -964,6 +990,15 @@ class DecisionsProposeesRCUE(DecisionsProposees):
|
||||
msg=f"Validation {self.rcue}: {code}",
|
||||
)
|
||||
db.session.add(self.validation)
|
||||
# Modifie au besoin les codes d'UE
|
||||
if code == "ADJ":
|
||||
deca = self.deca
|
||||
for ue_id in (self.rcue.ue_1.id, self.rcue.ue_2.id):
|
||||
dec_ue = deca.decisions_ues.get(ue_id)
|
||||
if dec_ue and dec_ue.code_valide not in CODES_UE_VALIDES:
|
||||
log(f"rcue.record: force ADJ sur {dec_ue}")
|
||||
dec_ue.record("ADJ")
|
||||
|
||||
if self.rcue.formsemestre_1 is not None:
|
||||
sco_cache.invalidate_formsemestre(
|
||||
formsemestre_id=self.rcue.formsemestre_1.id
|
||||
@ -972,6 +1007,7 @@ class DecisionsProposeesRCUE(DecisionsProposees):
|
||||
sco_cache.invalidate_formsemestre(
|
||||
formsemestre_id=self.rcue.formsemestre_2.id
|
||||
)
|
||||
self.code_valide = code # mise à jour état
|
||||
self.recorded = True
|
||||
|
||||
def erase(self):
|
||||
@ -1032,14 +1068,14 @@ class DecisionsProposeesUE(DecisionsProposees):
|
||||
):
|
||||
# Une UE peut être validée plusieurs fois en cas de redoublement (qu'elle soit capitalisée ou non)
|
||||
# mais ici on a restreint au formsemestre donc une seule (prend la première)
|
||||
self.validation = ScolarFormSemestreValidation.query.filter_by(
|
||||
validation = ScolarFormSemestreValidation.query.filter_by(
|
||||
etudid=etud.id, formsemestre_id=formsemestre.id, ue_id=ue.id
|
||||
).first()
|
||||
super().__init__(
|
||||
etud=etud,
|
||||
code_valide=self.validation.code if self.validation is not None else None,
|
||||
code_valide=validation.code if validation is not None else None,
|
||||
)
|
||||
# log(f"built {self}")
|
||||
self.validation = validation
|
||||
self.formsemestre = formsemestre
|
||||
self.ue: UniteEns = ue
|
||||
self.rcue: RegroupementCoherentUE = None
|
||||
@ -1082,7 +1118,7 @@ class DecisionsProposeesUE(DecisionsProposees):
|
||||
|
||||
def set_rcue(self, rcue: RegroupementCoherentUE):
|
||||
"""Rattache cette UE à un RCUE. Cela peut modifier les codes
|
||||
proposés (si compensation)"""
|
||||
proposés par compute_codes() (si compensation)"""
|
||||
self.rcue = rcue
|
||||
|
||||
def compute_codes(self):
|
||||
@ -1138,6 +1174,7 @@ class DecisionsProposeesUE(DecisionsProposees):
|
||||
log(f"DecisionsProposeesUE: recording {self.validation}")
|
||||
|
||||
sco_cache.invalidate_formsemestre(formsemestre_id=self.formsemestre.id)
|
||||
self.code_valide = code # mise à jour
|
||||
self.recorded = True
|
||||
|
||||
def erase(self):
|
||||
|
@ -30,6 +30,7 @@ from app.models import (
|
||||
Identite,
|
||||
UniteEns,
|
||||
ScolarAutorisationInscription,
|
||||
ScolarFormSemestreValidation,
|
||||
)
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
@ -50,7 +51,7 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str:
|
||||
_gen_but_select("code_annee", deca.codes, deca.code_valide,
|
||||
disabled=True, klass="manual")
|
||||
}
|
||||
<span>({'non ' if deca.code_valide is None else ''}enregistrée)</span>
|
||||
<span>({deca.code_valide or 'non'} enregistrée)</span>
|
||||
</div>
|
||||
"""
|
||||
)
|
||||
@ -108,8 +109,8 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str:
|
||||
if ue.niveau_competence and ue.niveau_competence.id == niveau.id
|
||||
]
|
||||
ue_pair = ues[0] if ues else None
|
||||
# Les UEs à afficher, toujours en readonly
|
||||
# sur le formsemestre de l'année précédente du redoublant
|
||||
# Les UEs à afficher,
|
||||
# qui seront toujours en readonly sur le formsemestre de l'année précédente du redoublant
|
||||
ues_ro = [
|
||||
(
|
||||
ue_impair,
|
||||
@ -132,6 +133,7 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str:
|
||||
deca.decisions_ues[ue.id],
|
||||
disabled=read_only or ue_read_only,
|
||||
annee_prec=ue_read_only,
|
||||
niveau_id=ue.niveau_competence.id,
|
||||
)
|
||||
)
|
||||
else:
|
||||
@ -150,6 +152,7 @@ def _gen_but_select(
|
||||
code_valide: str,
|
||||
disabled: bool = False,
|
||||
klass: str = "",
|
||||
data: dict = {},
|
||||
) -> str:
|
||||
"Le menu html select avec les codes"
|
||||
# if disabled: # mauvaise idée car le disabled est traité en JS
|
||||
@ -165,8 +168,11 @@ def _gen_but_select(
|
||||
)
|
||||
return f"""<select required name="{name}"
|
||||
class="but_code {klass}"
|
||||
data-orig_code="{code_valide or (codes[0] if codes else '')}"
|
||||
data-orig_recorded="{code_valide or ''}"
|
||||
onchange="change_menu_code(this);"
|
||||
{"disabled" if disabled else ""}
|
||||
{" ".join( f'data-{k}="{v}"' for (k,v) in data.items() )}
|
||||
>{options_htm}</select>
|
||||
"""
|
||||
|
||||
@ -176,6 +182,7 @@ def _gen_but_niveau_ue(
|
||||
dec_ue: DecisionsProposeesUE,
|
||||
disabled: bool = False,
|
||||
annee_prec: bool = False,
|
||||
niveau_id: int = None,
|
||||
) -> str:
|
||||
if dec_ue.ue_status and dec_ue.ue_status["is_capitalized"]:
|
||||
moy_ue_str = f"""<span class="ue_cap">{
|
||||
@ -196,6 +203,13 @@ def _gen_but_niveau_ue(
|
||||
"""
|
||||
else:
|
||||
moy_ue_str = f"""<span>{scu.fmt_note(dec_ue.moy_ue)}</span>"""
|
||||
if dec_ue.code_valide:
|
||||
scoplement = f"""<div class="scoplement">
|
||||
Code {dec_ue.code_valide} enregistré le {dec_ue.validation.event_date.strftime("%d/%m/%Y")}
|
||||
à {dec_ue.validation.event_date.strftime("%Hh%M")}
|
||||
</div>
|
||||
"""
|
||||
else:
|
||||
scoplement = ""
|
||||
|
||||
return f"""<div class="but_niveau_ue {
|
||||
@ -210,7 +224,9 @@ def _gen_but_niveau_ue(
|
||||
<div class="but_code">{
|
||||
_gen_but_select("code_ue_"+str(ue.id),
|
||||
dec_ue.codes,
|
||||
dec_ue.code_valide, disabled=disabled
|
||||
dec_ue.code_valide,
|
||||
disabled=disabled,
|
||||
klass=f"code_ue ue_rcue_{niveau_id}" if not disabled else ""
|
||||
)
|
||||
}</div>
|
||||
|
||||
@ -250,14 +266,15 @@ def _gen_but_rcue(dec_rcue: DecisionsProposeesRCUE, niveau: ApcNiveau) -> str:
|
||||
{scoplement}
|
||||
</div>
|
||||
<div class="but_code">
|
||||
<div>{_gen_but_select("code_rcue_"+str(niveau.id),
|
||||
{_gen_but_select("code_rcue_"+str(niveau.id),
|
||||
dec_rcue.codes,
|
||||
dec_rcue.code_valide,
|
||||
disabled=True, klass="manual"
|
||||
disabled=True,
|
||||
klass="manual code_rcue",
|
||||
data = { "niveau_id" : str(niveau.id)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
|
||||
@ -274,17 +291,15 @@ def jury_but_semestriel(
|
||||
semestre_terminal = (
|
||||
formsemestre.semestre_id >= formsemestre.formation.get_parcours().NB_SEM
|
||||
)
|
||||
autorisations_passage = ScolarAutorisationInscription.query.filter_by(
|
||||
etudid=etud.id,
|
||||
origin_formsemestre_id=formsemestre.id,
|
||||
).all()
|
||||
# Par défaut: autorisé à passer dans le semestre suivant si sem. impair,
|
||||
# ou si décision déjà enregistrée:
|
||||
est_autorise_a_passer = (formsemestre.semestre_id % 2) or (
|
||||
formsemestre.semestre_id + 1
|
||||
) in (
|
||||
a.semestre_id
|
||||
for a in ScolarAutorisationInscription.query.filter_by(
|
||||
etudid=etud.id,
|
||||
origin_formsemestre_id=formsemestre.id,
|
||||
)
|
||||
)
|
||||
) in (a.semestre_id for a in autorisations_passage)
|
||||
decisions_ues = {
|
||||
ue.id: DecisionsProposeesUE(etud, formsemestre, ue, inscription_etat)
|
||||
for ue in ues
|
||||
@ -308,7 +323,9 @@ def jury_but_semestriel(
|
||||
flash("codes enregistrés")
|
||||
if not semestre_terminal:
|
||||
if request.form.get("autorisation_passage"):
|
||||
if not est_autorise_a_passer:
|
||||
if not formsemestre.semestre_id + 1 in (
|
||||
a.semestre_id for a in autorisations_passage
|
||||
):
|
||||
ScolarAutorisationInscription.autorise_etud(
|
||||
etud.id,
|
||||
formsemestre.formation.formation_code,
|
||||
@ -368,21 +385,31 @@ def jury_but_semestriel(
|
||||
}">{etud.photo_html(title="fiche de " + etud.nomprenom)}</a>
|
||||
</div>
|
||||
</div>
|
||||
<h3>Jury sur un semestre BUT isolé</h3>
|
||||
<h3>Jury sur un semestre BUT isolé (ne concerne que les UEs)</h3>
|
||||
{warning}
|
||||
</div>
|
||||
|
||||
<form method="post" id="jury_but">
|
||||
""",
|
||||
]
|
||||
if (not read_only) and any([dec.code_valide for dec in decisions_ues.values()]):
|
||||
|
||||
erase_span = ""
|
||||
if not read_only:
|
||||
# Requête toutes les validations (pas seulement celles du deca courant),
|
||||
# au cas où: changement d'architecture, saisie en mode classique, ...
|
||||
validations = ScolarFormSemestreValidation.query.filter_by(
|
||||
etudid=etud.id, formsemestre_id=formsemestre.id
|
||||
).all()
|
||||
if validations:
|
||||
erase_span = f"""<a href="{
|
||||
url_for("notes.formsemestre_jury_but_erase",
|
||||
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id,
|
||||
etudid=etud.id, only_one_sem=1)
|
||||
}" class="stdlink">effacer les décisions enregistrées</a>"""
|
||||
else:
|
||||
erase_span = "Cet étudiant n'a aucune décision enregistrée pour ce semestre."
|
||||
erase_span = (
|
||||
"Cet étudiant n'a aucune décision enregistrée pour ce semestre."
|
||||
)
|
||||
|
||||
H.append(
|
||||
f"""
|
||||
@ -436,6 +463,9 @@ def jury_but_semestriel(
|
||||
<input type="checkbox" name="autorisation_passage" value="1" {
|
||||
"checked" if est_autorise_a_passer else ""}>
|
||||
<em>autoriser à passer dans le semestre S{formsemestre.semestre_id+1}</em>
|
||||
{("(autorisations enregistrées: " + ' '.join(
|
||||
'S' + str(a.semestre_id or '') for a in autorisations_passage) + ")"
|
||||
) if autorisations_passage else ""}
|
||||
</input>
|
||||
</div>
|
||||
"""
|
||||
|
@ -68,7 +68,8 @@ class ApcValidationRCUE(db.Model):
|
||||
"description en HTML"
|
||||
return f"""Décision sur RCUE {self.ue1.acronyme}/{self.ue2.acronyme}:
|
||||
<b>{self.code}</b>
|
||||
<em>enregistrée le {self.date.strftime("%d/%m/%Y")}</em>"""
|
||||
<em>enregistrée le {self.date.strftime("%d/%m/%Y")}
|
||||
à {self.date.strftime("%Hh%M")}</em>"""
|
||||
|
||||
def niveau(self) -> ApcNiveau:
|
||||
"""Le niveau de compétence associé à cet RCUE."""
|
||||
|
@ -93,6 +93,10 @@ class ScolarAutorisationInscription(db.Model):
|
||||
db.ForeignKey("notes_formsemestre.id"),
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"""{self.__class__.__name__}(id={self.id}, etudid={
|
||||
self.etudid}, semestre_id={self.semestre_id})"""
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"as a dict"
|
||||
d = dict(self.__dict__)
|
||||
|
@ -87,6 +87,8 @@ _formsemestreEditor = ndb.EditableTable(
|
||||
"resp_can_edit": bool,
|
||||
"resp_can_change_ens": bool,
|
||||
"ens_can_edit_eval": bool,
|
||||
"bul_bgcolor": lambda color: color or "white",
|
||||
"titre": lambda titre: titre or "sans titre",
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -312,6 +312,7 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
||||
le titre: ils seront automatiquement ajoutés <input type="button"
|
||||
value="remettre titre par défaut" onClick="document.tf.titre.value='{
|
||||
_default_sem_title(formation)}';"/>""",
|
||||
"allow_null": False,
|
||||
},
|
||||
),
|
||||
(
|
||||
|
@ -157,6 +157,8 @@ div.but_niveau_ue.annee_prec {
|
||||
background-color: rgb(167, 167, 0);
|
||||
}
|
||||
|
||||
div.but_section_annee,
|
||||
div.but_niveau_rcue.modified,
|
||||
div.but_niveau_ue.modified {
|
||||
background-color: rgb(255, 214, 254);
|
||||
}
|
||||
|
@ -5,17 +5,36 @@ function enable_manual_codes(elt) {
|
||||
$(".jury_but select.manual").prop("disabled", !elt.checked);
|
||||
}
|
||||
|
||||
// changement menu code:
|
||||
// changement d'un menu code:
|
||||
function change_menu_code(elt) {
|
||||
elt.parentElement.parentElement.classList.remove("recorded");
|
||||
// TODO: comparer avec valeur enregistrée (à mettre en data-orig ?)
|
||||
// et colorer en fonction
|
||||
// Ajuste styles pour visualiser codes enregistrés/modifiés
|
||||
if (elt.value != elt.dataset.orig_code) {
|
||||
elt.parentElement.parentElement.classList.add("modified");
|
||||
} else {
|
||||
elt.parentElement.parentElement.classList.remove("modified");
|
||||
}
|
||||
if (elt.value == elt.dataset.orig_recorded) {
|
||||
elt.parentElement.parentElement.classList.add("recorded");
|
||||
} else {
|
||||
elt.parentElement.parentElement.classList.remove("recorded");
|
||||
}
|
||||
// Si RCUE passant en ADJ, change les menus des UEs associées
|
||||
if (elt.classList.contains("code_rcue")
|
||||
&& elt.dataset.niveau_id
|
||||
&& elt.value == "ADJ"
|
||||
&& elt.value != elt.dataset.orig_recorded) {
|
||||
let ue_selects = elt.parentElement.parentElement.parentElement.querySelectorAll(
|
||||
"select.ue_rcue_" + elt.dataset.niveau_id);
|
||||
ue_selects.forEach(select => {
|
||||
select.value = "ADJ";
|
||||
change_menu_code(select); // pour changer les styles
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$(function () {
|
||||
// Recupère la liste ordonnées des etudids
|
||||
// pour avoir le "suivant" etr le "précédent"
|
||||
// pour avoir le "suivant" et le "précédent"
|
||||
// (liens de navigation)
|
||||
const url = new URL(document.URL);
|
||||
const frags = url.pathname.split("/"); // .../formsemestre_validation_but/formsemestre_id/etudid
|
||||
|
@ -201,4 +201,79 @@ Etudiants:
|
||||
moy_rcue: 9.50 # la moyenne courante (et non enregistrée), donc pas 10.5
|
||||
est_compensable: False
|
||||
decision_annee: ADM
|
||||
|
||||
geii43:
|
||||
prenom: etugeii43
|
||||
civilite: M
|
||||
formsemestres:
|
||||
S1:
|
||||
notes_modules: # on joue avec les SAE seulement car elles sont "diagonales"
|
||||
"S1.1": 9.00
|
||||
"S1.2": 9.00
|
||||
attendu: # les codes jury que l'on doit vérifier
|
||||
deca:
|
||||
passage_de_droit: False
|
||||
nb_competences: 2
|
||||
nb_rcue_annee: 0
|
||||
decisions_ues:
|
||||
"UE11":
|
||||
codes: [ "AJ", "..." ]
|
||||
code_valide: AJ
|
||||
decision_jury: AJ
|
||||
moy_ue: 9.00
|
||||
"UE12":
|
||||
codes: [ "AJ", "..." ]
|
||||
code_valide: AJ
|
||||
decision_jury: AJ
|
||||
moy_ue: 9.00
|
||||
S2:
|
||||
notes_modules: # on joue avec les SAE seulement car elles sont "diagonales"
|
||||
"S2.1": 9.00
|
||||
"S2.2": 9.00
|
||||
attendu: # les codes jury que l'on doit vérifier
|
||||
deca:
|
||||
passage_de_droit: False
|
||||
nb_competences: 2
|
||||
nb_rcue_annee: 2
|
||||
valide_moitie_rcue: False
|
||||
codes: [ "RED", "..." ]
|
||||
decisions_ues:
|
||||
"UE21":
|
||||
codes: [ "AJ", "..." ]
|
||||
code_valide: AJ
|
||||
moy_ue: 9.00
|
||||
"UE22":
|
||||
codes: [ "AJ", "..." ]
|
||||
code_valide: AJ # va basculer en ADJ car RCUE en ADJ (mais le test est AVANT !)
|
||||
moy_ue: 9.00
|
||||
decisions_rcues: # on repère ici les RCUE par l'acronyme de leur 1ere UE (donc du S1)
|
||||
"UE11":
|
||||
code_valide: AJ
|
||||
decision_jury: AJ
|
||||
rcue:
|
||||
moy_rcue: 9.00
|
||||
est_compensable: False
|
||||
"UE12":
|
||||
code_valide: AJ # code par défaut proposé
|
||||
decision_jury: ADJ # code donné par le jury de S2
|
||||
rcue:
|
||||
moy_rcue: 9.00
|
||||
est_compensable: False
|
||||
decision_annee: RED
|
||||
S1-red:
|
||||
notes_modules: # on joue avec les SAE seulement car elles sont "diagonales"
|
||||
"S1.1": 11.00
|
||||
"S1.2": 7.00
|
||||
attendu: # les codes jury que l'on doit vérifier
|
||||
deca:
|
||||
passage_de_droit: false
|
||||
nb_competences: 2
|
||||
nb_rcue_annee: 0
|
||||
decisions_ues:
|
||||
"UE11":
|
||||
codes: [ "ADM", "..." ]
|
||||
code_valide: ADM
|
||||
moy_ue: 11.00
|
||||
"UE12":
|
||||
code_valide: AJ
|
||||
moy_ue: 7.00
|
||||
decision_annee: AJ
|
||||
|
@ -232,7 +232,7 @@ class ScoFake(object):
|
||||
self,
|
||||
formation_id=None,
|
||||
semestre_id=None,
|
||||
titre=None,
|
||||
titre="",
|
||||
date_debut=None,
|
||||
date_fin=None,
|
||||
etat=None,
|
||||
@ -253,6 +253,7 @@ class ScoFake(object):
|
||||
) -> int:
|
||||
if responsables is None:
|
||||
responsables = (self.default_user.id,)
|
||||
titre = titre or "sans titre"
|
||||
oid = sco_formsemestre.do_formsemestre_create(locals())
|
||||
oids = sco_formsemestre.do_formsemestre_list(
|
||||
args={"formsemestre_id": oid}
|
||||
|
@ -1,19 +1,29 @@
|
||||
##############################################################################
|
||||
# ScoDoc
|
||||
# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved.
|
||||
# See LICENSE
|
||||
##############################################################################
|
||||
|
||||
""" Test jury BUT avec parcours
|
||||
|
||||
Ces tests sont généralement lents (construction de la base),
|
||||
et donc marqués par `@pytest.mark.slow`.
|
||||
|
||||
Certains sont aussi marqués par @pytest.mark.lemans ou @pytest.mark.lyon
|
||||
pour lancer certains tests spécifiques seulement.
|
||||
|
||||
Exemple utilisation spécifique:
|
||||
# test sur "Lyon" seulement:
|
||||
pytest --pdb -m lyon tests/unit/test_but_jury.py
|
||||
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from tests.unit import yaml_setup
|
||||
|
||||
import app
|
||||
from app.but.jury_but import DecisionsProposeesAnnee
|
||||
from app.but.jury_but_validation_auto import formsemestre_validation_auto_but
|
||||
from app.models import (
|
||||
Formation,
|
||||
FormSemestre,
|
||||
Identite,
|
||||
UniteEns,
|
||||
)
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.models import FormSemestre
|
||||
from config import TestConfig
|
||||
|
||||
DEPT = TestConfig.DEPT_TEST
|
||||
|
@ -34,7 +34,7 @@ formsemestre_validation_auto_but(only_adm=False)
|
||||
|
||||
test_but_jury()
|
||||
- compare décisions attendues indiquées dans le YAML avec celles de ScoDoc
|
||||
et enregistre immédiatement après la décision manuelle indiquée par `decision_jury`
|
||||
et enregistre immédiatement APRES la décision manuelle indiquée par `decision_jury`
|
||||
dans le YAML.
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user