forked from ScoDoc/ScoDoc
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.comp.res_but import ResultatsSemestreBUT
|
||||||
from app.models import FormSemestre, Identite
|
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, sco_utils as scu
|
||||||
from app.scodoc import sco_bulletins_json
|
from app.scodoc import sco_bulletins_json
|
||||||
from app.scodoc import sco_bulletins_pdf
|
from app.scodoc import sco_bulletins_pdf
|
||||||
@ -62,18 +63,15 @@ class BulletinBUT:
|
|||||||
# }
|
# }
|
||||||
return d
|
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"
|
"dict synthèse résultats UE"
|
||||||
res = self.res
|
res = self.res
|
||||||
|
|
||||||
d = {
|
d = {
|
||||||
"id": ue.id,
|
"id": ue.id,
|
||||||
"titre": ue.titre,
|
"titre": ue.titre,
|
||||||
"numero": ue.numero,
|
"numero": ue.numero,
|
||||||
"type": ue.type,
|
"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,
|
"color": ue.color,
|
||||||
"competence": None, # XXX TODO lien avec référentiel
|
"competence": None, # XXX TODO lien avec référentiel
|
||||||
"moyenne": None,
|
"moyenne": None,
|
||||||
@ -86,6 +84,11 @@ class BulletinBUT:
|
|||||||
"ressources": self.etud_ue_mod_results(etud, ue, res.ressources),
|
"ressources": self.etud_ue_mod_results(etud, ue, res.ressources),
|
||||||
"saes": self.etud_ue_mod_results(etud, ue, res.saes),
|
"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 ue.type != UE_SPORT:
|
||||||
if self.prefs["bul_show_ue_rangs"]:
|
if self.prefs["bul_show_ue_rangs"]:
|
||||||
rangs, effectif = res.ue_rangs[ue.id]
|
rangs, effectif = res.ue_rangs[ue.id]
|
||||||
@ -277,11 +280,17 @@ class BulletinBUT:
|
|||||||
"numero": formsemestre.semestre_id,
|
"numero": formsemestre.semestre_id,
|
||||||
"inscription": "", # inutilisé mais nécessaire pour le js de Seb.
|
"inscription": "", # inutilisé mais nécessaire pour le js de Seb.
|
||||||
"groupes": [], # XXX TODO
|
"groupes": [], # XXX TODO
|
||||||
"absences": {
|
}
|
||||||
|
if self.prefs["bul_show_abs"]:
|
||||||
|
semestre_infos["absences"] = {
|
||||||
"injustifie": nbabs - nbabsjust,
|
"injustifie": nbabs - nbabsjust,
|
||||||
"total": nbabs,
|
"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(
|
semestre_infos.update(
|
||||||
sco_bulletins_json.dict_decision_jury(etud.id, formsemestre.id)
|
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),
|
"saes": self.etud_mods_results(etud, res.saes, version=version),
|
||||||
"ues": {
|
"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
|
for ue in res.ues
|
||||||
# si l'UE comporte des modules auxquels on est inscrit:
|
# si l'UE comporte des modules auxquels on est inscrit:
|
||||||
if (
|
if (
|
||||||
|
@ -65,6 +65,9 @@ class ResultatsSemestre(ResultatsCache):
|
|||||||
self.moyennes_matieres = {}
|
self.moyennes_matieres = {}
|
||||||
"""Moyennes de matières, si calculées. { matiere_id : Series, index etudid }"""
|
"""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):
|
def compute(self):
|
||||||
"Charge les notes et inscriptions et calcule toutes les moyennes"
|
"Charge les notes et inscriptions et calcule toutes les moyennes"
|
||||||
# voir ce qui est chargé / calculé ici et dans les sous-classes
|
# voir ce qui est chargé / calculé ici et dans les sous-classes
|
||||||
@ -177,7 +180,6 @@ class ResultatsSemestre(ResultatsCache):
|
|||||||
if not self.validations:
|
if not self.validations:
|
||||||
self.validations = res_sem.load_formsemestre_validations(self.formsemestre)
|
self.validations = res_sem.load_formsemestre_validations(self.formsemestre)
|
||||||
ue_capitalisees = self.validations.ue_capitalisees
|
ue_capitalisees = self.validations.ue_capitalisees
|
||||||
ue_by_code = {}
|
|
||||||
for etudid in ue_capitalisees.index:
|
for etudid in ue_capitalisees.index:
|
||||||
recompute_mg = False
|
recompute_mg = False
|
||||||
# ue_codes = set(ue_capitalisees.loc[etudid]["ue_code"])
|
# 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...
|
d["periode"] = 1 # typiquement, début en septembre: S1, S3...
|
||||||
else:
|
else:
|
||||||
d["periode"] = 2 # typiquement, début en février: S2, S4...
|
d["periode"] = 2 # typiquement, début en février: S2, S4...
|
||||||
d["titre_num"] = self.titre_num()
|
|
||||||
d["titreannee"] = self.titre_annee()
|
d["titreannee"] = self.titre_annee()
|
||||||
d["mois_debut"] = self.mois_debut()
|
d["mois_debut"] = self.mois_debut()
|
||||||
d["mois_fin"] = self.mois_fin()
|
d["mois_fin"] = self.mois_fin()
|
||||||
@ -174,7 +173,6 @@ class FormSemestre(db.Model):
|
|||||||
d["session_id"] = self.session_id()
|
d["session_id"] = self.session_id()
|
||||||
d["etapes"] = self.etapes_apo_vdi()
|
d["etapes"] = self.etapes_apo_vdi()
|
||||||
d["etapes_apo_str"] = self.etapes_apo_str()
|
d["etapes_apo_str"] = self.etapes_apo_str()
|
||||||
d["responsables"] = [u.id for u in self.responsables] # liste des ids
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def query_ues(self, with_sport=False) -> flask_sqlalchemy.BaseQuery:
|
def query_ues(self, with_sport=False) -> flask_sqlalchemy.BaseQuery:
|
||||||
@ -302,6 +300,10 @@ class FormSemestre(db.Model):
|
|||||||
else:
|
else:
|
||||||
return ", ".join([u.get_nomcomplet() for u in self.responsables])
|
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):
|
def annee_scolaire_str(self):
|
||||||
"2021 - 2022"
|
"2021 - 2022"
|
||||||
return scu.annee_scolaire_repr(self.date_debut.year, self.date_debut.month)
|
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 io
|
||||||
|
import pprint
|
||||||
|
import pydoc
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
import pydoc
|
|
||||||
|
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
|
|
||||||
@ -140,7 +141,11 @@ def process_field(field, cdict, style, suppress_empty_pars=False, format="pdf"):
|
|||||||
cdict
|
cdict
|
||||||
) # note that None values are mapped to empty strings
|
) # note that None values are mapped to empty strings
|
||||||
except:
|
except:
|
||||||
log("process_field: invalid format=%s" % field)
|
log(
|
||||||
|
f"""process_field: invalid format. field={field!r}
|
||||||
|
values={pprint.pformat(cdict)}
|
||||||
|
"""
|
||||||
|
)
|
||||||
text = (
|
text = (
|
||||||
"<para><i>format invalide !</i></para><para>"
|
"<para><i>format invalide !</i></para><para>"
|
||||||
+ traceback.format_exc()
|
+ traceback.format_exc()
|
||||||
|
@ -1172,7 +1172,7 @@ class BasePreferences(object):
|
|||||||
"bul_show_abs", # ex "gestion_absence"
|
"bul_show_abs", # ex "gestion_absence"
|
||||||
{
|
{
|
||||||
"initvalue": 1,
|
"initvalue": 1,
|
||||||
"title": "Indiquer les absences sous les bulletins",
|
"title": "Indiquer les absences dans les bulletins",
|
||||||
"input_type": "boolcheckbox",
|
"input_type": "boolcheckbox",
|
||||||
"category": "bul",
|
"category": "bul",
|
||||||
"labels": ["non", "oui"],
|
"labels": ["non", "oui"],
|
||||||
|
@ -175,11 +175,24 @@ section>div:nth-child(1){
|
|||||||
.ue .rang{
|
.ue .rang{
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
.absencesRecap {
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
.absencesRecap > div:nth-child(2n) {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
.abs {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
.decision{
|
.decision{
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
text-decoration: underline var(--couleurIntense);
|
}
|
||||||
|
#ects_tot {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
.enteteSemestre{
|
.enteteSemestre{
|
||||||
color: black;
|
color: black;
|
||||||
|
@ -2152,6 +2152,18 @@ div.eval_description {
|
|||||||
padding: 3px;
|
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 */
|
/* Saisie des notes */
|
||||||
div.saisienote_etape1 {
|
div.saisienote_etape1 {
|
||||||
|
@ -83,7 +83,7 @@ class releveBUT extends HTMLElement {
|
|||||||
<div>
|
<div>
|
||||||
<div class=infoSemestre></div>
|
<div class=infoSemestre></div>
|
||||||
<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>
|
<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>
|
<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>
|
</div>
|
||||||
@ -204,9 +204,10 @@ class releveBUT extends HTMLElement {
|
|||||||
<div>Min. promo. :</div><div>${data.semestre.notes.min}</div>
|
<div>Min. promo. :</div><div>${data.semestre.notes.min}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class=absencesRecap>
|
<div class=absencesRecap>
|
||||||
<div class=enteteSemestre>Absences</div>
|
<div class=enteteSemestre>Absences</div><div class=enteteSemestre>1/2 jour.</div>
|
||||||
<div class=enteteSemestre>N.J. ${data.semestre.absences?.injustifie ?? "-"}</div>
|
<div class=abs>Non justifiées</div>
|
||||||
<div style="grid-column: 2">Total ${data.semestre.absences?.total ?? "-"}</div>
|
<div>${data.semestre.absences?.injustifie ?? "-"}</div>
|
||||||
|
<div class=abs>Total</div><div>${data.semestre.absences?.total ?? "-"}</div>
|
||||||
</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>
|
<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("")
|
}).join("")
|
||||||
}*/
|
}*/
|
||||||
this.shadow.querySelector(".infoSemestre").innerHTML = output;
|
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} -
|
Bonus : ${dataUE.bonus || 0} -
|
||||||
Malus : ${dataUE.malus || 0}
|
Malus : ${dataUE.malus || 0}
|
||||||
<span class=ects> -
|
<span class=ects> -
|
||||||
ECTS : ${dataUE.ECTS.acquis} / ${dataUE.ECTS.total}
|
ECTS : ${dataUE.ECTS?.acquis || "-"} / ${dataUE.ECTS?.total || "-"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
@ -377,9 +379,9 @@ class releveBUT extends HTMLElement {
|
|||||||
setOptions(options) {
|
setOptions(options) {
|
||||||
Object.entries(options).forEach(([option, value]) => {
|
Object.entries(options).forEach(([option, value]) => {
|
||||||
if (value === false) {
|
if (value === false) {
|
||||||
document.body.classList.add(option.replace("show", "hide"))
|
this.shadow.children[0].classList.add(option.replace("show", "hide"));
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{# -*- mode: jinja-html -*- #}
|
{# -*- mode: jinja-html -*- #}
|
||||||
{# Pied des bulletins HTML #}
|
{# Pied des bulletins HTML #}
|
||||||
|
|
||||||
|
<div class="bul_foot">
|
||||||
|
<div>
|
||||||
<p>Situation actuelle:
|
<p>Situation actuelle:
|
||||||
{% if inscription_courante %}
|
{% if inscription_courante %}
|
||||||
<a class="stdlink" href="{{url_for(
|
<a class="stdlink" href="{{url_for(
|
||||||
@ -13,6 +15,28 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<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" %}
|
{% if formsemestre.modalite == "EXT" %}
|
||||||
<p><a href="{{
|
<p><a href="{{
|
||||||
url_for('notes.formsemestre_ext_edit_ue_validations',
|
url_for('notes.formsemestre_ext_edit_ue_validations',
|
||||||
@ -23,6 +47,8 @@
|
|||||||
Éditer les validations d'UE dans ce semestre extérieur
|
Éditer les validations d'UE dans ce semestre extérieur
|
||||||
</a></p>
|
</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{# Place du diagramme radar #}
|
{# Place du diagramme radar #}
|
||||||
<form id="params">
|
<form id="params">
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<div class="formsemestre_page_title">
|
<div class="formsemestre_page_title">
|
||||||
<div class="infos">
|
<div class="infos">
|
||||||
<span class="semtitle"><a class="stdlink"
|
<span class="semtitle"><a class="stdlink"
|
||||||
title="{{formsemestre.session_id}}"
|
title="{{formsemestre.session_id()}}"
|
||||||
href="{{url_for('notes.formsemestre_status',
|
href="{{url_for('notes.formsemestre_status',
|
||||||
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)}}"
|
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)}}"
|
||||||
>{{formsemestre.titre}}</a>
|
>{{formsemestre.titre}}</a>
|
||||||
|
@ -323,6 +323,9 @@ def formsemestre_bulletinetud(
|
|||||||
elif format == "html":
|
elif format == "html":
|
||||||
return render_template(
|
return render_template(
|
||||||
"but/bulletin.html",
|
"but/bulletin.html",
|
||||||
|
appreciations=models.BulAppreciations.query.filter_by(
|
||||||
|
etudid=etudid, formsemestre_id=formsemestre.id
|
||||||
|
).order_by(models.BulAppreciations.date),
|
||||||
bul_url=url_for(
|
bul_url=url_for(
|
||||||
"notes.formsemestre_bulletinetud",
|
"notes.formsemestre_bulletinetud",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
@ -332,6 +335,8 @@ def formsemestre_bulletinetud(
|
|||||||
force_publishing=1, # pour ScoDoc lui même
|
force_publishing=1, # pour ScoDoc lui même
|
||||||
version=version,
|
version=version,
|
||||||
),
|
),
|
||||||
|
can_edit_appreciations=formsemestre.est_responsable(current_user)
|
||||||
|
or (current_user.has_permission(Permission.ScoEtudInscrit)),
|
||||||
etud=etud,
|
etud=etud,
|
||||||
formsemestre=formsemestre,
|
formsemestre=formsemestre,
|
||||||
inscription_courante=etud.inscription_courante(),
|
inscription_courante=etud.inscription_courante(),
|
||||||
|
Loading…
Reference in New Issue
Block a user