Amélioration page bilan ECTS: plus de détails, messages d'avertissement. Closes #992

This commit is contained in:
Emmanuel Viennet 2024-09-12 14:34:34 +02:00
parent e12dca7aef
commit 8c3e7b4ff6
10 changed files with 155 additions and 30 deletions

View File

@ -86,6 +86,12 @@ def _news_delete_jury_etud(etud: Identite, detail: str = ""):
text=f"""Suppression décision jury {detail} pour <a href="{url}">{etud.nomprenom}</a>""",
url=url,
)
Scolog.logdb(
"jury_delete_manual",
etudid=etud.id,
msg=f"Validation {detail} effacée",
commit=True,
)
@bp.route(

View File

@ -501,7 +501,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
scodoc_dept=g.scodoc_dept,
semestre_idx=formsemestre.semestre_id,
formation_id=formsemestre.formation.id)}">
{formsemestre.formation.html()} ({
{formsemestre.formation.html()|safe} ({
formsemestre.formation.id})</a>
</li>
</ul>

View File

@ -64,7 +64,7 @@ class Formation(ScoDocModel):
def html(self) -> str:
"titre complet pour affichage"
return f"""Formation {self.titre} ({self.acronyme}) [version {self.version}] code {self.formation_code}"""
return f"""Formation {self.titre} ({self.acronyme}) version {self.version} code <tt>{self.formation_code}</tt>"""
@classmethod
def get_formation(cls, formation_id: int | str, dept_id: int = None) -> "Formation":

View File

@ -91,7 +91,7 @@ class ScolarFormSemestreValidation(db.Model):
d.pop("_sa_instance_state", None)
return d
def html(self, detail=False) -> str:
def html(self, detail=True) -> str:
"Affichage html"
if self.ue_id is not None:
moyenne = (
@ -114,11 +114,12 @@ class ScolarFormSemestreValidation(db.Model):
+ ", ".join([p.code for p in self.ue.parcours]))
+ "</span>"
if self.ue.parcours else ""}
{("émise par " + link)}
: <b>{self.code}</b>{moyenne}
<b>{(self.ue.ects or 0):g} ECTS</b>
le {self.event_date.strftime(scu.DATEATIME_FMT)}
"""
{("émise par " + link)}
""" + (
f"le {self.event_date.strftime(scu.DATEATIME_FMT)}" if detail else ""
)
else:
return f"""Validation du semestre S{
self.formsemestre.semestre_id if self.formsemestre else "?"}

View File

@ -765,7 +765,7 @@ def ue_table(formation_id=None, semestre_idx=1, msg=""): # was ue_list
"delete_small_dis_img", title="Suppression impossible (module utilisé)"
)
H = [
f"""<h2>{formation.html()} {lockicon}
f"""<h2>{formation.html()|safe} {lockicon}
</h2>
""",
]

View File

@ -8,7 +8,12 @@ span.parcours {
color: blueviolet;
}
div.ue_list_etud_validations ul.liste_validations li {
div.liste_validations {
margin-top: 16px;
margin-bottom: 16px;
}
div.ue_list_etud_validations div.liste_validations details {
margin-bottom: 8px;
}
@ -28,6 +33,7 @@ details {
}
div.validation-details {
margin-left: 32px;
margin-top: 12px;
margin-bottom: 16px;
margin-left: 32px;
}

View File

@ -44,7 +44,7 @@
{%- endmacro %}
{% block app_content %}
<h2>{{formation.html()}}</h2>
<h2>{{formation.html()|safe}}</h2>
{# Liens vers les différents parcours #}
<div class="les_parcours">
@ -133,7 +133,7 @@ Choisissez un parcours...
d'associer à chaque semestre d'un niveau de compétence une UE de la formation
<a class="stdlink"
href="{{url_for('notes.ue_table', scodoc_dept=g.scodoc_dept, formation_id=formation.id )
}}">{{formation.html()}}
}}">{{formation.html()|safe}}
</a>.</p>
<p>Le symbole <span class="parc">TC</span> désigne un niveau du tronc commun

View File

@ -1,4 +1,4 @@
{% extends "sco_page.j2" %}
{% extends "sco_page_dept.j2" %}
{% block styles %}
{{super()}}
@ -10,8 +10,13 @@
<h1>Bilan des ECTS de {{etud.html_link_fiche()|safe}}</h1>
<div class="help">
Cette page donne toutes les UEs acquises par l'étudiant (codes <tt>ADM, ADJ, ADJR, ADSUP, CMP...</tt>)
dans chaque formation qu'il a suivi.
<p>
Cette page donne toutes les UEs acquises par l'étudiant (codes <tt>ADM, ADJ, ADJR, ADSUP, CMP...</tt>).
</p>
<p>
La somme des crédits ECTS en bas de page peut compter des UEs
suivies plusieurs fois (redoublements) n'a pas de signification pour l'octroit des diplômes.
</p>
</div>
{% for diplome in formsemestre_by_diplome %}
@ -21,4 +26,29 @@
{% include "jury/ue_list_etud_validations.j2" %}
{% endfor %}
{% if ue_warnings %}
<div class="scobox">
<div class="scobox-title warning">Attention</div>
<ul>
{% for warning in ue_warnings %}
<li>{{warning|safe}}</li>
{% endfor %}
</ul>
<div class="help fontred">Ces problème peuvent dans certains cas affecter
le comptage des crédits ECTS et la délivrance des diplômes.</div>
</div>
{% endif %}
{% endblock app_content %}
{% block scripts %}
{{super()}}
<script>
function open_all_details() {
var details = document.querySelectorAll('.liste_validations details');
details.forEach(function(detail) {
detail.open = true;
});
}
</script>
{% endblock %}

View File

@ -5,31 +5,52 @@
<div class="help">Liste de toutes les UEs validées par {{etud.html_link_fiche()|safe}},
sur des semestres ou déclarées comme "antérieures" (externes).
</div>
<ul class="liste_validations">
<div class="liste_validations">
{% for validation in validations %}
<li
<div
{% if loop.index0 > 0 and validation.formsemestre and loop.previtem.formsemestre.semestre_id != validation.formsemestre.semestre_id %}
class="new_semestre"
data-ue_id="{{validation.ue.id}}"
{% endif %}
>{{ validation.html() | safe }}
{% if edit_mode %}
{% if validation.formsemestre and validation.formsemestre.can_edit_jury() %}
<form class="inline-form">
<button data-v_id="{{validation.id}}" data-type="validation_ue" data-etudid="{{etud.id}}">
effacer
</button>
</form>
{% else %}
{{ scu.icontag("lock_img", border="0", title="Semestre verrouillé")|safe }}
>
<details>
<summary>
{{ validation.html(detail=False) | safe }}
{% if edit_mode %}
{% if validation.formsemestre and validation.formsemestre.can_edit_jury() %}
<form class="inline-form">
<button data-v_id="{{validation.id}}" data-type="validation_ue" data-etudid="{{etud.id}}">
effacer
</button>
</form>
{% else %}
{{ scu.icontag("lock_img", border="0", title="Semestre verrouillé")|safe }}
{% endif %}
{% endif %}
{% endif %}
</li>
</summary>
<div class="validation-details">
<b>UE {{validation.ue.acronyme}}</b> <tt>[{{validation.ue.ue_code}}]</tt> en
<a class="discretelink" href="{{
url_for('notes.ue_table', scodoc_dept=g.scodoc_dept,
formation_id=validation.ue.formation.id, semestre_idx=validation.ue.semestre_idx)
}}">{{validation.ue.formation.html()|safe}}</a>
{% if validation.ue.formation.is_apc() %}
<div>Compétence: {{validation.ue.niveau_competence}}</div>
<div>Référentiel :
{{ validation.ue.formation.referentiel_competence.get_title()
if validation.ue.formation.referentiel_competence else '<em>pas de référentiel</em>' }}
</div>
{% endif %}
</div>
</details>
</div>
{% endfor %}
</ul>
</div>
<div><a class="stdlink" href="#" onclick="open_all_details()">ouvrir tous les détails</a></div>
{% if total_ects %}
<div class="total_ects">
Total ECTS: {{ "%g" % total_ects }}
Total ECTS (toutes UEs, y compris redoublées): {{ "%g" % total_ects }}
</div>
{% endif %}
</div>

View File

@ -944,6 +944,8 @@ def etud_bilan_ects(etudid: int):
ects_by_diplome = {}
titre_by_diplome = {} # { diplome : titre }
validations_by_diplome = {} # { diplome : query validations UEs }
validations_by_ue_code = defaultdict(list) # { ue_code : [validation] }
validations_by_niveau_sem = defaultdict(list) # { niveau_sem : [validation] }
for diplome, formsemestres in formsemestre_by_diplome.items():
formsemestre = formsemestres[0]
titre_by_diplome[diplome] = formsemestre.formation.get_titre_version()
@ -962,6 +964,62 @@ def etud_bilan_ects(etudid: int):
(validation.ue.ects or 0.0)
for validation in validations_by_diplome[diplome]
)
for validation in validations:
validations_by_ue_code[validation.ue.ue_code].append(validation)
validations_by_niveau_sem[
(
(
validation.ue.niveau_competence.id
if validation.ue.niveau_competence
else None
),
validation.ue.semestre_idx,
)
].append(validation)
ref_comp_ids = {
v.ue.formation.referentiel_competence_id
for validations in validations_by_ue_code.values()
for v in validations
if v.ue.formation.referentiel_competence_id is not None
}
ue_warnings = []
if len(ref_comp_ids) > 1:
ue_warnings.append(
"""plusieurs référentiels de compétences utilisés&nbsp;!
(ok si plusieurs diplôme différents suivis)"""
)
for ue_code, validations in validations_by_ue_code.items():
ectss = {v.ue.ects for v in validations}
if len(ectss) > 1:
ects_str = ", ".join(
f"{v.ue.acronyme}: {v.ue.ects} ects" for v in validations
)
ue_acros = ", ".join({v.ue.acronyme for v in validations})
ue_warnings.append(
f"""Les UEs {ue_acros} ont le même code ({ue_code
}) mais des ECTS différents: {ects_str}"""
)
for (niveau_id, semestre_idx), validations in validations_by_niveau_sem.items():
if not validations:
continue # safeguard
formation = validations[0].ue.formation
ue_acros = ", ".join({v.ue.acronyme for v in validations})
if niveau_id is None and formation.is_apc():
ue_warnings.append(
f"""Les UEs {ue_acros} du S{semestre_idx
} n'ont pas de niveau de compétence associé !"""
)
ectss = {v.ue.ects for v in validations}
if len(ectss) > 1:
ects_str = ", ".join(
f"{v.ue.acronyme}: {v.ue.ects} ects" for v in validations
)
ue_warnings.append(
f"""Les UEs {ue_acros} du même code niveau de compétence
({validations[0].ue.niveau_competence}) ont des ECTS différents: {ects_str}"""
)
return render_template(
"jury/etud_bilan_ects.j2",
@ -969,5 +1027,8 @@ def etud_bilan_ects(etudid: int):
ects_by_diplome=ects_by_diplome,
formsemestre_by_diplome=formsemestre_by_diplome,
titre_by_diplome=titre_by_diplome,
title=f"Bilan ECTS {etud.nomprenom}",
ue_warnings=ue_warnings,
validations_by_diplome=validations_by_diplome,
sco=ScoData(etud=etud),
)