forked from ScoDoc/DocScoDoc
Bul. BUT: ECTS, Absences, Appréciations.
This commit is contained in:
parent
ab87a98eda
commit
7409ccce5a
@ -13,6 +13,7 @@ from flask import url_for, g
|
||||
|
||||
from app.comp.res_but import ResultatsSemestreBUT
|
||||
from app.models import FormSemestre, Identite
|
||||
from app.models.ues import UniteEns
|
||||
from app.scodoc import sco_bulletins, sco_utils as scu
|
||||
from app.scodoc import sco_bulletins_json
|
||||
from app.scodoc import sco_bulletins_pdf
|
||||
@ -62,18 +63,15 @@ class BulletinBUT:
|
||||
# }
|
||||
return d
|
||||
|
||||
def etud_ue_results(self, etud, ue):
|
||||
def etud_ue_results(self, etud: Identite, ue: UniteEns, decision_ue: dict) -> dict:
|
||||
"dict synthèse résultats UE"
|
||||
res = self.res
|
||||
|
||||
d = {
|
||||
"id": ue.id,
|
||||
"titre": ue.titre,
|
||||
"numero": ue.numero,
|
||||
"type": ue.type,
|
||||
"ECTS": {
|
||||
"acquis": 0.0, # XXX TODO voir jury #sco92
|
||||
"total": ue.ects or 0.0, # float même si non renseigné
|
||||
},
|
||||
"color": ue.color,
|
||||
"competence": None, # XXX TODO lien avec référentiel
|
||||
"moyenne": None,
|
||||
@ -86,6 +84,11 @@ class BulletinBUT:
|
||||
"ressources": self.etud_ue_mod_results(etud, ue, res.ressources),
|
||||
"saes": self.etud_ue_mod_results(etud, ue, res.saes),
|
||||
}
|
||||
if self.prefs["bul_show_ects"]:
|
||||
d["ECTS"] = {
|
||||
"acquis": decision_ue.get("ects", 0.0),
|
||||
"total": ue.ects or 0.0, # float même si non renseigné
|
||||
}
|
||||
if ue.type != UE_SPORT:
|
||||
if self.prefs["bul_show_ue_rangs"]:
|
||||
rangs, effectif = res.ue_rangs[ue.id]
|
||||
@ -277,11 +280,17 @@ class BulletinBUT:
|
||||
"numero": formsemestre.semestre_id,
|
||||
"inscription": "", # inutilisé mais nécessaire pour le js de Seb.
|
||||
"groupes": [], # XXX TODO
|
||||
"absences": {
|
||||
}
|
||||
if self.prefs["bul_show_abs"]:
|
||||
semestre_infos["absences"] = {
|
||||
"injustifie": nbabs - nbabsjust,
|
||||
"total": nbabs,
|
||||
},
|
||||
}
|
||||
}
|
||||
decisions_ues = self.res.get_etud_decision_ues(etud.id) or {}
|
||||
if self.prefs["bul_show_ects"]:
|
||||
ects_tot = sum([ue.ects or 0 for ue in res.ues]) if res.ues else 0.0
|
||||
ects_acquis = sum([d.get("ects", 0) for d in decisions_ues.values()])
|
||||
semestre_infos["ECTS"] = {"acquis": ects_acquis, "total": ects_tot}
|
||||
semestre_infos.update(
|
||||
sco_bulletins_json.dict_decision_jury(etud.id, formsemestre.id)
|
||||
)
|
||||
@ -307,7 +316,9 @@ class BulletinBUT:
|
||||
),
|
||||
"saes": self.etud_mods_results(etud, res.saes, version=version),
|
||||
"ues": {
|
||||
ue.acronyme: self.etud_ue_results(etud, ue)
|
||||
ue.acronyme: self.etud_ue_results(
|
||||
etud, ue, decision_ue=decisions_ues.get(ue.id, {})
|
||||
)
|
||||
for ue in res.ues
|
||||
# si l'UE comporte des modules auxquels on est inscrit:
|
||||
if (
|
||||
|
@ -65,6 +65,9 @@ class ResultatsSemestre(ResultatsCache):
|
||||
self.moyennes_matieres = {}
|
||||
"""Moyennes de matières, si calculées. { matiere_id : Series, index etudid }"""
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__}(id={self.id}, formsemestre='{self.formsemestre}')>"
|
||||
|
||||
def compute(self):
|
||||
"Charge les notes et inscriptions et calcule toutes les moyennes"
|
||||
# voir ce qui est chargé / calculé ici et dans les sous-classes
|
||||
@ -177,7 +180,6 @@ class ResultatsSemestre(ResultatsCache):
|
||||
if not self.validations:
|
||||
self.validations = res_sem.load_formsemestre_validations(self.formsemestre)
|
||||
ue_capitalisees = self.validations.ue_capitalisees
|
||||
ue_by_code = {}
|
||||
for etudid in ue_capitalisees.index:
|
||||
recompute_mg = False
|
||||
# ue_codes = set(ue_capitalisees.loc[etudid]["ue_code"])
|
||||
|
@ -161,7 +161,6 @@ class FormSemestre(db.Model):
|
||||
d["periode"] = 1 # typiquement, début en septembre: S1, S3...
|
||||
else:
|
||||
d["periode"] = 2 # typiquement, début en février: S2, S4...
|
||||
d["titre_num"] = self.titre_num()
|
||||
d["titreannee"] = self.titre_annee()
|
||||
d["mois_debut"] = self.mois_debut()
|
||||
d["mois_fin"] = self.mois_fin()
|
||||
@ -174,7 +173,6 @@ class FormSemestre(db.Model):
|
||||
d["session_id"] = self.session_id()
|
||||
d["etapes"] = self.etapes_apo_vdi()
|
||||
d["etapes_apo_str"] = self.etapes_apo_str()
|
||||
d["responsables"] = [u.id for u in self.responsables] # liste des ids
|
||||
return d
|
||||
|
||||
def query_ues(self, with_sport=False) -> flask_sqlalchemy.BaseQuery:
|
||||
@ -302,6 +300,10 @@ class FormSemestre(db.Model):
|
||||
else:
|
||||
return ", ".join([u.get_nomcomplet() for u in self.responsables])
|
||||
|
||||
def est_responsable(self, user):
|
||||
"True si l'user est l'un des responsables du semestre"
|
||||
return user.id in [u.id for u in self.responsables]
|
||||
|
||||
def annee_scolaire_str(self):
|
||||
"2021 - 2022"
|
||||
return scu.annee_scolaire_repr(self.date_debut.year, self.date_debut.month)
|
||||
|
@ -51,10 +51,11 @@ Chaque semestre peut si nécessaire utiliser un type de bulletin différent.
|
||||
|
||||
"""
|
||||
import io
|
||||
import pprint
|
||||
import pydoc
|
||||
import re
|
||||
import time
|
||||
import traceback
|
||||
import pydoc
|
||||
|
||||
from flask import g, request
|
||||
|
||||
@ -140,7 +141,11 @@ def process_field(field, cdict, style, suppress_empty_pars=False, format="pdf"):
|
||||
cdict
|
||||
) # note that None values are mapped to empty strings
|
||||
except:
|
||||
log("process_field: invalid format=%s" % field)
|
||||
log(
|
||||
f"""process_field: invalid format. field={field!r}
|
||||
values={pprint.pformat(cdict)}
|
||||
"""
|
||||
)
|
||||
text = (
|
||||
"<para><i>format invalide !</i></para><para>"
|
||||
+ traceback.format_exc()
|
||||
|
@ -1172,7 +1172,7 @@ class BasePreferences(object):
|
||||
"bul_show_abs", # ex "gestion_absence"
|
||||
{
|
||||
"initvalue": 1,
|
||||
"title": "Indiquer les absences sous les bulletins",
|
||||
"title": "Indiquer les absences dans les bulletins",
|
||||
"input_type": "boolcheckbox",
|
||||
"category": "bul",
|
||||
"labels": ["non", "oui"],
|
||||
|
@ -175,11 +175,24 @@ section>div:nth-child(1){
|
||||
.ue .rang{
|
||||
font-weight: 400;
|
||||
}
|
||||
.absencesRecap {
|
||||
align-items: baseline;
|
||||
}
|
||||
.absencesRecap > div:nth-child(2n) {
|
||||
font-weight: normal;
|
||||
}
|
||||
.abs {
|
||||
font-weight: bold;
|
||||
}
|
||||
.decision{
|
||||
margin: 5px 0;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
text-decoration: underline var(--couleurIntense);
|
||||
}
|
||||
#ects_tot {
|
||||
margin-left: 8px;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
}
|
||||
.enteteSemestre{
|
||||
color: black;
|
||||
|
@ -2152,6 +2152,18 @@ div.eval_description {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
div.bul_foot {
|
||||
max-width: 1000px;
|
||||
background: #FFE7D5;
|
||||
border-radius: 16px;
|
||||
border: 1px solid #AAA;
|
||||
padding: 16px 32px;
|
||||
margin: auto;
|
||||
}
|
||||
div.bull_appreciations {
|
||||
border-left: 1px solid black;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
/* Saisie des notes */
|
||||
div.saisienote_etape1 {
|
||||
|
@ -83,7 +83,7 @@ class releveBUT extends HTMLElement {
|
||||
<div>
|
||||
<div class=infoSemestre></div>
|
||||
<div>
|
||||
<div class=decision></div>
|
||||
<div><span class=decision></span><span class="ects" id="ects_tot"></span></div>
|
||||
<div class=dateInscription>Inscrit le </div>
|
||||
<em>Les moyennes ci-dessus servent à situer l'étudiant dans la promotion et ne correspondent pas à des validations de compétences ou d'UE.</em>
|
||||
</div>
|
||||
@ -204,9 +204,10 @@ class releveBUT extends HTMLElement {
|
||||
<div>Min. promo. :</div><div>${data.semestre.notes.min}</div>
|
||||
</div>
|
||||
<div class=absencesRecap>
|
||||
<div class=enteteSemestre>Absences</div>
|
||||
<div class=enteteSemestre>N.J. ${data.semestre.absences?.injustifie ?? "-"}</div>
|
||||
<div style="grid-column: 2">Total ${data.semestre.absences?.total ?? "-"}</div>
|
||||
<div class=enteteSemestre>Absences</div><div class=enteteSemestre>1/2 jour.</div>
|
||||
<div class=abs>Non justifiées</div>
|
||||
<div>${data.semestre.absences?.injustifie ?? "-"}</div>
|
||||
<div class=abs>Total</div><div>${data.semestre.absences?.total ?? "-"}</div>
|
||||
</div>
|
||||
<a class=photo href="${data.etudiant.fiche_url}"><img src="${data.etudiant.photo_url || "default_Student.svg"}" alt="photo de l'étudiant" title="fiche de l'étudiant" height="120" border="0"></a>
|
||||
`;
|
||||
@ -223,7 +224,8 @@ class releveBUT extends HTMLElement {
|
||||
}).join("")
|
||||
}*/
|
||||
this.shadow.querySelector(".infoSemestre").innerHTML = output;
|
||||
this.shadow.querySelector(".decision").innerHTML = data.semestre.decision?.code || "";
|
||||
this.shadow.querySelector(".decision").innerHTML = "Décision jury: " + (data.semestre.decision?.code || "");
|
||||
this.shadow.querySelector("#ects_tot").innerHTML = "ECTS : " + (data.semestre.ECTS?.acquis || "-") + " / " + (data.semestre.ECTS?.total || "-");
|
||||
}
|
||||
|
||||
/*******************************/
|
||||
@ -256,7 +258,7 @@ class releveBUT extends HTMLElement {
|
||||
Bonus : ${dataUE.bonus || 0} -
|
||||
Malus : ${dataUE.malus || 0}
|
||||
<span class=ects> -
|
||||
ECTS : ${dataUE.ECTS.acquis} / ${dataUE.ECTS.total}
|
||||
ECTS : ${dataUE.ECTS?.acquis || "-"} / ${dataUE.ECTS?.total || "-"}
|
||||
</span>
|
||||
</div>
|
||||
</div>`;
|
||||
@ -377,9 +379,9 @@ class releveBUT extends HTMLElement {
|
||||
setOptions(options) {
|
||||
Object.entries(options).forEach(([option, value]) => {
|
||||
if (value === false) {
|
||||
document.body.classList.add(option.replace("show", "hide"))
|
||||
this.shadow.children[0].classList.add(option.replace("show", "hide"));
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,28 +1,54 @@
|
||||
{# -*- mode: jinja-html -*- #}
|
||||
{# Pied des bulletins HTML #}
|
||||
|
||||
<p>Situation actuelle:
|
||||
{% if inscription_courante %}
|
||||
<a class="stdlink" href="{{url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=inscription_courante.formsemestre_id)
|
||||
}}">{{inscription_str}}</a>
|
||||
{% else %}
|
||||
{{inscription_str}}
|
||||
{% endif %}
|
||||
</p>
|
||||
<div class="bul_foot">
|
||||
<div>
|
||||
<p>Situation actuelle:
|
||||
{% if inscription_courante %}
|
||||
<a class="stdlink" href="{{url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=inscription_courante.formsemestre_id)
|
||||
}}">{{inscription_str}}</a>
|
||||
{% else %}
|
||||
{{inscription_str}}
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
{% if formsemestre.modalite == "EXT" %}
|
||||
<p><a href="{{
|
||||
url_for('notes.formsemestre_ext_edit_ue_validations',
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre.id,
|
||||
etudid=etud.id)}}"
|
||||
class="stdlink">
|
||||
Éditer les validations d'UE dans ce semestre extérieur
|
||||
</a></p>
|
||||
{% endif %}
|
||||
<div class="bull_appreciations">
|
||||
<h3>Appréciations</h3>
|
||||
{% for app in appreciations %}
|
||||
<p><span class="bull_appreciations_date">{{app.date}}</span>{{
|
||||
app.comment}}<span
|
||||
class="bull_appreciations_link">{% if can_edit_appreciations %}<a
|
||||
class="stdlink" href="{{url_for('notes.appreciation_add_form',
|
||||
scodoc_dept=g.scodoc_dept, id=app.id)}}">modifier</a>
|
||||
<a class="stdlink" href="{{url_for('notes.appreciation_add_form',
|
||||
scodoc_dept=g.scodoc_dept, id=app.id, suppress=1)}}">supprimer</a>{% endif %}
|
||||
</span>
|
||||
</p>
|
||||
{% endfor %}
|
||||
{% if can_edit_appreciations %}
|
||||
<p><a class="stdlink" href="{{url_for(
|
||||
'notes.appreciation_add_form', scodoc_dept=g.scodoc_dept,
|
||||
etudid=etud.id, formsemestre_id=formsemestre_id)
|
||||
}}">Ajouter une appréciation</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if formsemestre.modalite == "EXT" %}
|
||||
<p><a href="{{
|
||||
url_for('notes.formsemestre_ext_edit_ue_validations',
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre.id,
|
||||
etudid=etud.id)}}"
|
||||
class="stdlink">
|
||||
Éditer les validations d'UE dans ce semestre extérieur
|
||||
</a></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Place du diagramme radar #}
|
||||
<form id="params">
|
||||
|
@ -5,7 +5,7 @@
|
||||
<div class="formsemestre_page_title">
|
||||
<div class="infos">
|
||||
<span class="semtitle"><a class="stdlink"
|
||||
title="{{formsemestre.session_id}}"
|
||||
title="{{formsemestre.session_id()}}"
|
||||
href="{{url_for('notes.formsemestre_status',
|
||||
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)}}"
|
||||
>{{formsemestre.titre}}</a>
|
||||
|
@ -323,6 +323,9 @@ def formsemestre_bulletinetud(
|
||||
elif format == "html":
|
||||
return render_template(
|
||||
"but/bulletin.html",
|
||||
appreciations=models.BulAppreciations.query.filter_by(
|
||||
etudid=etudid, formsemestre_id=formsemestre.id
|
||||
).order_by(models.BulAppreciations.date),
|
||||
bul_url=url_for(
|
||||
"notes.formsemestre_bulletinetud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
@ -332,6 +335,8 @@ def formsemestre_bulletinetud(
|
||||
force_publishing=1, # pour ScoDoc lui même
|
||||
version=version,
|
||||
),
|
||||
can_edit_appreciations=formsemestre.est_responsable(current_user)
|
||||
or (current_user.has_permission(Permission.ScoEtudInscrit)),
|
||||
etud=etud,
|
||||
formsemestre=formsemestre,
|
||||
inscription_courante=etud.inscription_courante(),
|
||||
|
Loading…
Reference in New Issue
Block a user