Améliore visu jury BUT. + minor code cleaning.

This commit is contained in:
Emmanuel Viennet 2022-12-24 18:06:22 -03:00
parent 29869e543f
commit 1e73021c42
4 changed files with 152 additions and 96 deletions

View File

@ -8,6 +8,7 @@
"""
import re
import numpy as np
import flask
from flask import flash, url_for
@ -15,10 +16,15 @@ from flask import g, request
from app import db
from app.but import jury_but
from app.but.jury_but import DecisionsProposeesAnnee, DecisionsProposeesUE
from app.but.jury_but import (
DecisionsProposeesAnnee,
DecisionsProposeesRCUE,
DecisionsProposeesUE,
)
from app.comp import res_sem
from app.comp.res_but import ResultatsSemestreBUT
from app.models import (
ApcNiveau,
FormSemestre,
FormSemestreInscription,
Identite,
@ -59,19 +65,9 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str:
"""
)
else:
H.append("""<div><em>Pas de décision annuelle (sem. impair)</em></div>""")
H.append("""<div><em>Pas de décision annuelle (sem. impair).</em></div>""")
H.append("""</div>""")
if deca.formsemestre_pair is not None:
annee_sco_pair = deca.formsemestre_pair.annee_scolaire()
avertissement_redoublement = (
f"année {annee_sco_pair}-{annee_sco_pair+1}"
if annee_sco_pair != deca.annee_scolaire()
else ""
)
else:
avertissement_redoublement = ""
formsemestre_1 = deca.formsemestre_impair
formsemestre_2 = deca.formsemestre_pair
# Ordonne selon les dates des 2 semestres considérés (pour les redoublants à cheval):
@ -84,16 +80,19 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str:
formsemestre_1, formsemestre_2 = formsemestre_2, formsemestre_1
H.append(
f"""
<div class="titre_niveaux"><b>Niveaux de compétences et unités d'enseignement du BUT{deca.annee_but}</b></div>
<div class="titre_niveaux"><b>Niveaux de compétences et unités d'enseignement du BUT{
deca.annee_but}</b></div>
<div class="but_annee">
<div class="titre"></div>
<div class="titre">S{formsemestre_1.semestre_id
<div class="titre">{"S" +str(formsemestre_1.semestre_id)
if formsemestre_1 else "-"}
<span class="avertissement_redoublement">{formsemestre_1.annee_scolaire_str() if formsemestre_1 else ""}</span>
<span class="avertissement_redoublement">{formsemestre_1.annee_scolaire_str()
if formsemestre_1 else ""}</span>
</div>
<div class="titre">S{formsemestre_2.semestre_id
<div class="titre">{"S"+str(formsemestre_2.semestre_id)
if formsemestre_2 else "-"}
<span class="avertissement_redoublement">{formsemestre_2.annee_scolaire_str() if formsemestre_2 else ""}</span>
<span class="avertissement_redoublement">{formsemestre_2.annee_scolaire_str()
if formsemestre_2 else ""}</span>
</div>
<div class="titre">RCUE</div>
"""
@ -109,7 +108,8 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str:
ue_impair = ues[0] if ues else None
ues = [ue for ue in deca.ues_pair if 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, toujours en readonly
# sur le formsemestre de l'année précédente du redoublant
ues_ro = [
(
ue_impair,
@ -137,24 +137,9 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str:
else:
H.append("""<div class="niveau_vide"></div>""")
# RCUE
if dec_rcue is None:
H.append("""<div class="niveau_vide"></div>""")
else:
H.append(
f"""<div class="but_niveau_rcue
{'recorded' if dec_rcue.code_valide is not None else ''}
">
<div class="but_note">{scu.fmt_note(dec_rcue.rcue.moy_rcue)}</div>
<div class="but_code">{
_gen_but_select("code_rcue_"+str(niveau.id),
dec_rcue.codes,
dec_rcue.code_valide,
disabled=True, klass="manual"
)
}</div>
</div>"""
)
# Colonne RCUE
H.append(_gen_but_rcue(dec_rcue, niveau))
H.append("</div>") # but_annee
return "\n".join(H)
@ -169,9 +154,9 @@ def _gen_but_select(
"Le menu html select avec les codes"
# if disabled: # mauvaise idée car le disabled est traité en JS
# return f"""<div class="but_code {klass}">{code_valide}</div>"""
h = "\n".join(
options_htm = "\n".join(
[
f"""<option value="{code}"
f"""<option value="{code}"
{'selected' if code == code_valide else ''}
class="{'recorded' if code == code_valide else ''}"
>{code}</option>"""
@ -182,7 +167,7 @@ def _gen_but_select(
class="but_code {klass}"
onchange="change_menu_code(this);"
{"disabled" if disabled else ""}
>{h}</select>
>{options_htm}</select>
"""
@ -191,18 +176,21 @@ def _gen_but_niveau_ue(
dec_ue: DecisionsProposeesUE,
disabled: bool = False,
annee_prec: bool = False,
):
) -> str:
if dec_ue.ue_status and dec_ue.ue_status["is_capitalized"]:
moy_ue_str = f"""<span class="ue_cap">{
scu.fmt_note(dec_ue.moy_ue_with_cap)}</span>"""
scoplement = f"""<div class="scoplement">
<div>
<b>UE {ue.acronyme} capitalisée le
{dec_ue.ue_status["event_date"].strftime("%d/%m/%Y")}
</b>
<b>UE {ue.acronyme} capitalisée </b>
<span>le {dec_ue.ue_status["event_date"].strftime("%d/%m/%Y")}
</span>
</div>
<div>UE en cours avec moyenne
{scu.fmt_note(dec_ue.moy_ue)}
<div>UE en cours
{ "sans notes" if np.isnan(dec_ue.moy_ue)
else
("avec moyenne" + scu.fmt_note(dec_ue.moy_ue))
}
</div>
</div>
"""
@ -229,6 +217,43 @@ def _gen_but_niveau_ue(
</div>"""
def _gen_but_rcue(dec_rcue: DecisionsProposeesRCUE, niveau: ApcNiveau) -> str:
if dec_rcue is None:
return """
<div class="but_niveau_rcue niveau_vide with_scoplement">
<div></div>
<div class="scoplement">Pas de RCUE (UE non capitalisée ?)</div>
</div>
"""
scoplement = (
f"""<div class="scoplement">{
dec_rcue.validation.to_html()
}</div>"""
if dec_rcue.validation
else ""
)
return f"""
<div class="but_niveau_rcue
{'recorded' if dec_rcue.code_valide is not None else ''}
">
<div class="but_note with_scoplement">
<div>{scu.fmt_note(dec_rcue.rcue.moy_rcue)}</div>
{scoplement}
</div>
<div class="but_code">
<div>{_gen_but_select("code_rcue_"+str(niveau.id),
dec_rcue.codes,
dec_rcue.code_valide,
disabled=True, klass="manual"
)}
</div>
</div>
</div>
"""
def jury_but_semestriel(
formsemestre: FormSemestre,
etud: Identite,
@ -265,9 +290,9 @@ def jury_but_semestriel(
for key in request.form:
code = request.form[key]
# Codes d'UE
m = re.match(r"^code_ue_(\d+)$", key)
if m:
ue_id = int(m.group(1))
code_match = re.match(r"^code_ue_(\d+)$", key)
if code_match:
ue_id = int(code_match.group(1))
dec_ue = decisions_ues.get(ue_id)
if not dec_ue:
raise ScoValueError(f"UE invalide ue_id={ue_id}")
@ -285,7 +310,8 @@ def jury_but_semestriel(
)
db.session.commit()
flash(
f"autorisation de passage en S{formsemestre.semestre_id + 1} enregistrée"
f"""autorisation de passage en S{formsemestre.semestre_id + 1
} enregistrée"""
)
else:
if est_autorise_a_passer:
@ -442,11 +468,10 @@ def infos_fiche_etud_html(etudid: int) -> str:
# temporaire quick & dirty: affiche le dernier
try:
deca = DecisionsProposeesAnnee(etud, formsemestres_but[-1])
if True: # len(deca.rcues_annee) > 0:
return f"""<div class="infos_but">
return f"""<div class="infos_but">
{show_etud(deca, read_only=True)}
</div>
"""
"""
except ScoValueError:
pass

View File

@ -2,19 +2,17 @@
"""Décisions de jury (validations) des RCUE et années du BUT
"""
import flask_sqlalchemy
from sqlalchemy.sql import text
from typing import Union
from app import db
import flask_sqlalchemy
from app import db
from app.models import CODE_STR_LEN
from app.models.but_refcomp import ApcNiveau
from app.models.etudiants import Identite
from app.models.ues import UniteEns
from app.models.formations import Formation
from app.models.formsemestre import FormSemestre
from app.models.ues import UniteEns
from app.scodoc import sco_codes_parcours as sco_codes
from app.scodoc import sco_utils as scu
@ -63,7 +61,14 @@ class ApcValidationRCUE(db.Model):
self.ue1}/{self.ue2}:{self.code!r}>"""
def __str__(self):
return f"""décision sur RCUE {self.ue1.acronyme}/{self.ue2.acronyme}: {self.code}"""
return f"""Décision sur RCUE {self.ue1.acronyme}/{self.ue2.acronyme}: {
self.code} enregistrée le {self.date.strftime("%d/%m/%Y")}"""
def to_html(self) -> str:
"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>"""
def niveau(self) -> ApcNiveau:
"""Le niveau de compétence associé à cet RCUE."""
@ -96,10 +101,6 @@ class RegroupementCoherentUE:
dec_ue_2: "DecisionsProposeesUE",
inscription_etat: str,
):
from app.comp import res_sem
from app.comp.res_but import ResultatsSemestreBUT
# from app.but.jury_but import DecisionsProposeesUE
ue_1 = dec_ue_1.ue
ue_2 = dec_ue_2.ue
# Ordonne les UE dans le sens croissant (S1,S2) ou (S3,S4)...
@ -296,7 +297,8 @@ class ApcValidationAnnee(db.Model):
formsemestre = db.relationship("FormSemestre", backref="apc_validations_annees")
def __repr__(self):
return f"<{self.__class__.__name__} {self.id} {self.etud} BUT{self.ordre}/{self.annee_scolaire}:{self.code!r}>"
return f"""<{self.__class__.__name__} {self.id} {self.etud
} BUT{self.ordre}/{self.annee_scolaire}:{self.code!r}>"""
def __str__(self):
return f"""décision sur année BUT{self.ordre} {self.annee_scolaire} : {self.code}"""
@ -333,7 +335,8 @@ def dict_decision_jury(etud: Identite, formsemestre: FormSemestre) -> dict:
titres_rcues.append(f"""pas de compétence: code {dec_rcue["code"]}""")
else:
titres_rcues.append(
f"""{niveau["competence"]["titre"]}&nbsp;{niveau["ordre"]}:&nbsp;{dec_rcue["code"]}"""
f"""{niveau["competence"]["titre"]}&nbsp;{niveau["ordre"]}:&nbsp;{
dec_rcue["code"]}"""
)
decisions["descr_decisions_rcue"] = ", ".join(titres_rcues)
decisions["descr_decisions_niveaux"] = (

View File

@ -35,6 +35,7 @@
.niveau_vide {
background-color: rgb(195, 195, 195) !important;
position: relative;
}
.but_annee>* {
@ -208,4 +209,8 @@ div.but_doc table tbody tr:nth-child(odd) {
div.but_doc table tr td.amue {
color: rgb(127, 127, 206);
font-size: 90%;
}
.but_niveau_rcue .scoplement {
font-weight: normal;
}

View File

@ -624,13 +624,14 @@ def index_html():
if editable:
H.append(
f"""
<p class="help">Une "formation" est un programme pédagogique structuré
<p class="help">Une "formation" est un programme pédagogique structuré
en UE, matières et modules. Chaque semestre se réfère à une formation.
La modification d'une formation affecte tous les semestres qui s'y
La modification d'une formation affecte tous les semestres qui s'y
réfèrent.</p>
<ul>
<li><a class="stdlink" href="formation_create" id="link-create-formation">Créer une formation</a>
<li><a class="stdlink" href="formation_create" id="link-create-formation">Créer une
formation</a>
</li>
<li><a class="stdlink" href="formation_import_xml_form">Importer une formation (xml)</a>
</li>
@ -644,7 +645,7 @@ def index_html():
<li><a class="stdlink" href="{
url_for("notes.export_recap_formations_annee_scolaire",
scodoc_dept=g.scodoc_dept, annee_scolaire=scu.annee_scolaire())
}">exporter les formations de l'année scolaire
}">exporter les formations de l'année scolaire
{scu.annee_scolaire()} - {scu.annee_scolaire()+1}
</a>
</li>
@ -656,8 +657,7 @@ def index_html():
}">Liste des référentiels chargés</a>
</li>
</ul>
"""
"""
)
H.append(html_sco_header.sco_footer())
@ -855,7 +855,7 @@ def formsemestre_change_lock(formsemestre_id, dialog_confirmed=False):
else:
msg = "verrouillage"
return scu.confirm_dialog(
"<h2>Confirmer le %s du semestre ?</h2>" % msg,
f"<h2>Confirmer le {msg} du semestre ?</h2>",
helpmsg="""Les notes d'un semestre verrouillé ne peuvent plus être modifiées.
Un semestre verrouillé peut cependant être déverrouillé facilement à tout moment
(par son responsable ou un administrateur).
@ -863,7 +863,11 @@ def formsemestre_change_lock(formsemestre_id, dialog_confirmed=False):
Le programme d'une formation qui a un semestre verrouillé ne peut plus être modifié.
""",
dest_url="",
cancel_url="formsemestre_status?formsemestre_id=%s" % formsemestre_id,
cancel_url=url_for(
"notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
),
parameters={"formsemestre_id": formsemestre_id},
)
@ -940,21 +944,24 @@ def edit_enseignants_form(moduleimpl_id):
H.append(
f"""
<li>{nom} (<a class="stdlink" href="{
url_for('notes.edit_enseignants_form_delete', scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id, ens_id=ens["ens_id"])
url_for('notes.edit_enseignants_form_delete',
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id,
ens_id=ens["ens_id"])
}">supprimer</a>)
</li>"""
)
H.append("</ul>")
F = """<p class="help">Les enseignants d'un module ont le droit de
F = f"""<p class="help">Les enseignants d'un module ont le droit de
saisir et modifier toutes les notes des évaluations de ce module.
</p>
<p class="help">Pour changer le responsable du module, passez par la
page "<a class="stdlink" href="formsemestre_editwithmodules?formation_id=%s&formsemestre_id=%s">Modification du semestre</a>", accessible uniquement au responsable de la formation (chef de département)
page "<a class="stdlink" href="{
url_for("notes.formsemestre_editwithmodules", scodoc_dept=g.scodoc_dept,
formation_id=sem["formation_id"], formsemestre_id=M["formsemestre_id"])
}">Modification du semestre</a>",
accessible uniquement au responsable de la formation (chef de département)
</p>
""" % (
sem["formation_id"],
M["formsemestre_id"],
)
"""
modform = [
("moduleimpl_id", {"input_type": "hidden"}),
@ -1009,14 +1016,18 @@ def edit_enseignants_form(moduleimpl_id):
or ens_id == M["responsable_id"]
):
H.append(
'<p class="help">Enseignant %s déjà dans la liste !</p>' % ens_id
f"""<p class="help">Enseignant {ens_id} déjà dans la liste !</p>"""
)
else:
sco_moduleimpl.do_ens_create(
{"moduleimpl_id": moduleimpl_id, "ens_id": ens_id}
)
return flask.redirect(
"edit_enseignants_form?moduleimpl_id=%s" % moduleimpl_id
url_for(
"notes.edit_enseignants_form",
scodoc_dept=g.scodoc_dept,
moduleimpl_id=moduleimpl_id,
)
)
return header + "\n".join(H) + tf[1] + F + footer
@ -1372,12 +1383,12 @@ def formsemestre_enseignants_list(formsemestre_id, format="html"):
if not u:
continue
cursor.execute(
"""SELECT * FROM scolog L, notes_formsemestre_inscription I
WHERE method = 'AddAbsence'
and authenticated_user = %(authenticated_user)s
and L.etudid = I.etudid
and I.formsemestre_id = %(formsemestre_id)s
and date > %(date_debut)s
"""SELECT * FROM scolog L, notes_formsemestre_inscription I
WHERE method = 'AddAbsence'
and authenticated_user = %(authenticated_user)s
and L.etudid = I.etudid
and I.formsemestre_id = %(formsemestre_id)s
and date > %(date_debut)s
and date < %(date_fin)s
""",
{
@ -1448,15 +1459,21 @@ def edit_enseignants_form_delete(moduleimpl_id, ens_id: int):
ok = True
break
if not ok:
raise ScoValueError("invalid ens_id (%s)" % ens_id)
raise ScoValueError(f"invalid ens_id ({ens_id})")
ndb.SimpleQuery(
"""DELETE FROM notes_modules_enseignants
WHERE moduleimpl_id = %(moduleimpl_id)s
WHERE moduleimpl_id = %(moduleimpl_id)s
AND ens_id = %(ens_id)s
""",
{"moduleimpl_id": moduleimpl_id, "ens_id": ens_id},
)
return flask.redirect("edit_enseignants_form?moduleimpl_id=%s" % moduleimpl_id)
return flask.redirect(
url_for(
"notes.edit_enseignants_form",
scodoc_dept=g.scodoc_dept,
moduleimpl_id=moduleimpl_id,
)
)
# --- Gestion des inscriptions aux semestres
@ -2403,10 +2420,16 @@ def formsemestre_validation_but(
warning = ""
if len(deca.niveaux_competences) != len(deca.decisions_rcue_by_niveau):
warning += f"""<div class="warning">Attention: {len(deca.niveaux_competences)}
niveaux mais {len(deca.decisions_rcue_by_niveau)} regroupements RCUE.</div>"""
if deca.parcour is None:
warning += """<div class="warning">L'étudiant n'est pas inscrit à un parcours.</div>"""
if deca.a_cheval:
warning += f"""<div class="warning">Attention: regroupements RCUE
entre années (redoublement).</div>"""
else:
warning += f"""<div class="warning">Attention: {len(deca.niveaux_competences)}
niveaux mais {len(deca.decisions_rcue_by_niveau)} regroupements RCUE.</div>"""
if (deca.parcour is None) and len(formsemestre.parcours) > 0:
warning += (
"""<div class="warning">L'étudiant n'est pas inscrit à un parcours.</div>"""
)
if deca.formsemestre_impair and deca.inscription_etat_impair != scu.INSCRIT:
etat_ins = scu.ETATS_INSCRIPTION.get(deca.inscription_etat_impair, "inconnu?")
warning += f"""<div class="warning">{etat_ins} en S{deca.formsemestre_impair.semestre_id}"""
@ -2764,7 +2787,7 @@ def formsemestre_jury_but_erase(
return render_template(
"confirm_dialog.html",
title=f"Effacer les validations de jury de {etud.nomprenom} ?",
explanation=f"""Les validations d'UE et autorisations de passage
explanation=f"""Les validations d'UE et autorisations de passage
du semestre S{formsemestre.semestre_id} seront effacées."""
if only_one_sem
else """Les validations de toutes les UE, RCUE (compétences) et année seront effacées.""",
@ -3148,7 +3171,7 @@ def check_sem_integrity(formsemestre_id, fix=False):
else:
H.append(
"""
<p class="alert">Problème détecté réparable:
<p class="alert">Problème détecté réparable:
<a href="check_sem_integrity?formsemestre_id=%s&fix=1">réparer maintenant</a></p>
"""
% (formsemestre_id,)