forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -408,7 +408,7 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
de ce module.
|
de ce module.
|
||||||
Évaluation "complete" ssi toutes notes saisies ou en attente.
|
Évaluation "complete" ssi toutes notes saisies ou en attente.
|
||||||
"""
|
"""
|
||||||
modimpl = db.session.get(ModuleImpl, moduleimpl_id)
|
modimpl: ModuleImpl = db.session.get(ModuleImpl, moduleimpl_id)
|
||||||
modimpl_results = self.modimpls_results.get(moduleimpl_id)
|
modimpl_results = self.modimpls_results.get(moduleimpl_id)
|
||||||
if not modimpl_results:
|
if not modimpl_results:
|
||||||
return [] # safeguard
|
return [] # safeguard
|
||||||
|
@ -12,9 +12,7 @@ import sqlalchemy as sa
|
|||||||
from app import db, log
|
from app import db, log
|
||||||
from app.models.etudiants import Identite
|
from app.models.etudiants import Identite
|
||||||
from app.models.events import ScolarNews
|
from app.models.events import ScolarNews
|
||||||
from app.models.moduleimpls import ModuleImpl
|
|
||||||
from app.models.notes import NotesNotes
|
from app.models.notes import NotesNotes
|
||||||
from app.models.ues import UniteEns
|
|
||||||
|
|
||||||
from app.scodoc import sco_cache
|
from app.scodoc import sco_cache
|
||||||
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
||||||
@ -67,7 +65,7 @@ class Evaluation(db.Model):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def create(
|
def create(
|
||||||
cls,
|
cls,
|
||||||
moduleimpl: ModuleImpl = None,
|
moduleimpl: "ModuleImpl" = None,
|
||||||
date_debut: datetime.datetime = None,
|
date_debut: datetime.datetime = None,
|
||||||
date_fin: datetime.datetime = None,
|
date_fin: datetime.datetime = None,
|
||||||
description=None,
|
description=None,
|
||||||
@ -114,7 +112,7 @@ class Evaluation(db.Model):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_new_numero(
|
def get_new_numero(
|
||||||
cls, moduleimpl: ModuleImpl, date_debut: datetime.datetime
|
cls, moduleimpl: "ModuleImpl", date_debut: datetime.datetime
|
||||||
) -> int:
|
) -> int:
|
||||||
"""Get a new numero for an evaluation in this moduleimpl
|
"""Get a new numero for an evaluation in this moduleimpl
|
||||||
If necessary, renumber existing evals to make room for a new one.
|
If necessary, renumber existing evals to make room for a new one.
|
||||||
@ -145,7 +143,7 @@ class Evaluation(db.Model):
|
|||||||
"delete evaluation (commit) (check permission)"
|
"delete evaluation (commit) (check permission)"
|
||||||
from app.scodoc import sco_evaluation_db
|
from app.scodoc import sco_evaluation_db
|
||||||
|
|
||||||
modimpl: ModuleImpl = self.moduleimpl
|
modimpl: "ModuleImpl" = self.moduleimpl
|
||||||
if not modimpl.can_edit_evaluation(current_user):
|
if not modimpl.can_edit_evaluation(current_user):
|
||||||
raise AccessDenied(
|
raise AccessDenied(
|
||||||
f"Modification évaluation impossible pour {current_user.get_nomplogin()}"
|
f"Modification évaluation impossible pour {current_user.get_nomplogin()}"
|
||||||
@ -239,7 +237,7 @@ class Evaluation(db.Model):
|
|||||||
check_convert_evaluation_args(self.moduleimpl, data)
|
check_convert_evaluation_args(self.moduleimpl, data)
|
||||||
if data.get("numero") is None:
|
if data.get("numero") is None:
|
||||||
data["numero"] = Evaluation.get_max_numero(self.moduleimpl.id) + 1
|
data["numero"] = Evaluation.get_max_numero(self.moduleimpl.id) + 1
|
||||||
for k in self.__dict__.keys():
|
for k in self.__dict__:
|
||||||
if k != "_sa_instance_state" and k != "id" and k in data:
|
if k != "_sa_instance_state" and k != "id" and k in data:
|
||||||
setattr(self, k, data[k])
|
setattr(self, k, data[k])
|
||||||
|
|
||||||
@ -257,7 +255,7 @@ class Evaluation(db.Model):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def moduleimpl_evaluation_renumber(
|
def moduleimpl_evaluation_renumber(
|
||||||
cls, moduleimpl: ModuleImpl, only_if_unumbered=False
|
cls, moduleimpl: "ModuleImpl", only_if_unumbered=False
|
||||||
):
|
):
|
||||||
"""Renumber evaluations in this moduleimpl, according to their date. (numero=0: oldest one)
|
"""Renumber evaluations in this moduleimpl, according to their date. (numero=0: oldest one)
|
||||||
Needed because previous versions of ScoDoc did not have eval numeros
|
Needed because previous versions of ScoDoc did not have eval numeros
|
||||||
@ -394,6 +392,8 @@ class Evaluation(db.Model):
|
|||||||
"""set poids vers les UE (remplace existants)
|
"""set poids vers les UE (remplace existants)
|
||||||
ue_poids_dict = { ue_id : poids }
|
ue_poids_dict = { ue_id : poids }
|
||||||
"""
|
"""
|
||||||
|
from app.models.ues import UniteEns
|
||||||
|
|
||||||
L = []
|
L = []
|
||||||
for ue_id, poids in ue_poids_dict.items():
|
for ue_id, poids in ue_poids_dict.items():
|
||||||
ue = db.session.get(UniteEns, ue_id)
|
ue = db.session.get(UniteEns, ue_id)
|
||||||
@ -474,7 +474,7 @@ class EvaluationUEPoids(db.Model):
|
|||||||
backref=db.backref("ue_poids", cascade="all, delete-orphan"),
|
backref=db.backref("ue_poids", cascade="all, delete-orphan"),
|
||||||
)
|
)
|
||||||
ue = db.relationship(
|
ue = db.relationship(
|
||||||
UniteEns,
|
"UniteEns",
|
||||||
backref=db.backref("evaluation_ue_poids", cascade="all, delete-orphan"),
|
backref=db.backref("evaluation_ue_poids", cascade="all, delete-orphan"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -506,7 +506,7 @@ def evaluation_enrich_dict(e: Evaluation, e_dict: dict):
|
|||||||
return e_dict
|
return e_dict
|
||||||
|
|
||||||
|
|
||||||
def check_convert_evaluation_args(moduleimpl: ModuleImpl, data: dict):
|
def check_convert_evaluation_args(moduleimpl: "ModuleImpl", data: dict):
|
||||||
"""Check coefficient, dates and duration, raises exception if invalid.
|
"""Check coefficient, dates and duration, raises exception if invalid.
|
||||||
Convert date and time strings to date and time objects.
|
Convert date and time strings to date and time objects.
|
||||||
|
|
||||||
@ -606,19 +606,6 @@ def heure_to_time(heure: str) -> datetime.time:
|
|||||||
return datetime.time(int(h), int(m))
|
return datetime.time(int(h), int(m))
|
||||||
|
|
||||||
|
|
||||||
def _time_duration_HhM(heure_debut: str, heure_fin: str) -> int:
|
|
||||||
"""duree (nb entier de minutes) entre deux heures a notre format
|
|
||||||
ie 12h23
|
|
||||||
"""
|
|
||||||
if heure_debut and heure_fin:
|
|
||||||
h0, m0 = [int(x) for x in heure_debut.split("h")]
|
|
||||||
h1, m1 = [int(x) for x in heure_fin.split("h")]
|
|
||||||
d = (h1 - h0) * 60 + (m1 - m0)
|
|
||||||
return d
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _moduleimpl_evaluation_insert_before(
|
def _moduleimpl_evaluation_insert_before(
|
||||||
evaluations: list[Evaluation], next_eval: Evaluation
|
evaluations: list[Evaluation], next_eval: Evaluation
|
||||||
) -> int:
|
) -> int:
|
||||||
|
@ -13,7 +13,6 @@ from app import email
|
|||||||
from app import log
|
from app import log
|
||||||
from app.auth.models import User
|
from app.auth.models import User
|
||||||
from app.models import SHORT_STR_LEN
|
from app.models import SHORT_STR_LEN
|
||||||
from app.models.moduleimpls import ModuleImpl
|
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
|
|
||||||
@ -181,6 +180,7 @@ class ScolarNews(db.Model):
|
|||||||
None si inexistant
|
None si inexistant
|
||||||
"""
|
"""
|
||||||
from app.models.formsemestre import FormSemestre
|
from app.models.formsemestre import FormSemestre
|
||||||
|
from app.models.moduleimpls import ModuleImpl
|
||||||
|
|
||||||
formsemestre_id = None
|
formsemestre_id = None
|
||||||
if self.type == self.NEWS_INSCR:
|
if self.type == self.NEWS_INSCR:
|
||||||
|
@ -390,7 +390,7 @@ class FormSemestre(db.Model):
|
|||||||
Module.numero,
|
Module.numero,
|
||||||
Module.code,
|
Module.code,
|
||||||
Evaluation.numero,
|
Evaluation.numero,
|
||||||
Evaluation.date_debut.desc(),
|
Evaluation.date_debut,
|
||||||
)
|
)
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
|
@ -3,12 +3,14 @@
|
|||||||
"""
|
"""
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from flask_sqlalchemy.query import Query
|
from flask_sqlalchemy.query import Query
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.auth.models import User
|
from app.auth.models import User
|
||||||
from app.comp import df_cache
|
from app.comp import df_cache
|
||||||
from app.models import APO_CODE_STR_LEN
|
from app.models import APO_CODE_STR_LEN
|
||||||
from app.models.etudiants import Identite
|
from app.models.etudiants import Identite
|
||||||
|
from app.models.evaluations import Evaluation
|
||||||
from app.models.modules import Module
|
from app.models.modules import Module
|
||||||
from app.scodoc.sco_exceptions import AccessDenied, ScoLockedSemError
|
from app.scodoc.sco_exceptions import AccessDenied, ScoLockedSemError
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
@ -38,7 +40,13 @@ class ModuleImpl(db.Model):
|
|||||||
# formule de calcul moyenne:
|
# formule de calcul moyenne:
|
||||||
computation_expr = db.Column(db.Text())
|
computation_expr = db.Column(db.Text())
|
||||||
|
|
||||||
evaluations = db.relationship("Evaluation", lazy="dynamic", backref="moduleimpl")
|
evaluations = db.relationship(
|
||||||
|
"Evaluation",
|
||||||
|
lazy="dynamic",
|
||||||
|
backref="moduleimpl",
|
||||||
|
order_by=(Evaluation.numero, Evaluation.date_debut),
|
||||||
|
)
|
||||||
|
"évaluations, triées par numéro et dates croissants, donc la plus ancienne d'abord."
|
||||||
enseignants = db.relationship(
|
enseignants = db.relationship(
|
||||||
"User",
|
"User",
|
||||||
secondary="notes_modules_enseignants",
|
secondary="notes_modules_enseignants",
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"""Vérification des absences à une évaluation
|
"""Vérification des absences à une évaluation
|
||||||
"""
|
"""
|
||||||
from flask import url_for, g
|
from flask import url_for, g
|
||||||
|
from flask_sqlalchemy.query import Query
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.models import Evaluation, FormSemestre, Identite, Assiduite
|
from app.models import Evaluation, FormSemestre, Identite, Assiduite
|
||||||
@ -37,9 +38,6 @@ from app.scodoc import sco_evaluations
|
|||||||
from app.scodoc import sco_evaluation_db
|
from app.scodoc import sco_evaluation_db
|
||||||
from app.scodoc import sco_groups
|
from app.scodoc import sco_groups
|
||||||
|
|
||||||
from flask_sqlalchemy.query import Query
|
|
||||||
from sqlalchemy import or_, and_
|
|
||||||
|
|
||||||
|
|
||||||
def evaluation_check_absences(evaluation: Evaluation):
|
def evaluation_check_absences(evaluation: Evaluation):
|
||||||
"""Vérifie les absences au moment de cette évaluation.
|
"""Vérifie les absences au moment de cette évaluation.
|
||||||
@ -78,11 +76,11 @@ def evaluation_check_absences(evaluation: Evaluation):
|
|||||||
|
|
||||||
# Les notes:
|
# Les notes:
|
||||||
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation.id)
|
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation.id)
|
||||||
ValButAbs = [] # une note mais noté absent
|
note_but_abs = [] # une note mais noté absent
|
||||||
AbsNonSignalee = [] # note ABS mais pas noté absent
|
abs_non_signalee = [] # note ABS mais pas noté absent
|
||||||
ExcNonSignalee = [] # note EXC mais pas noté absent
|
exc_non_signalee = [] # note EXC mais pas noté absent
|
||||||
ExcNonJust = [] # note EXC mais absent non justifie
|
exc_non_just = [] # note EXC mais absent non justifie
|
||||||
AbsButExc = [] # note ABS mais justifié
|
abs_but_exc = [] # note ABS mais justifié
|
||||||
for etudid in etudids:
|
for etudid in etudids:
|
||||||
if etudid in notes_db:
|
if etudid in notes_db:
|
||||||
val = notes_db[etudid]["value"]
|
val = notes_db[etudid]["value"]
|
||||||
@ -92,50 +90,43 @@ def evaluation_check_absences(evaluation: Evaluation):
|
|||||||
and val != scu.NOTES_ATTENTE
|
and val != scu.NOTES_ATTENTE
|
||||||
) and etudid in abs_etudids:
|
) and etudid in abs_etudids:
|
||||||
# note valide et absent
|
# note valide et absent
|
||||||
ValButAbs.append(etudid)
|
note_but_abs.append(etudid)
|
||||||
if val is None and not etudid in abs_etudids:
|
if val is None and not etudid in abs_etudids:
|
||||||
# absent mais pas signale comme tel
|
# absent mais pas signale comme tel
|
||||||
AbsNonSignalee.append(etudid)
|
abs_non_signalee.append(etudid)
|
||||||
if val == scu.NOTES_NEUTRALISE and not etudid in abs_etudids:
|
if val == scu.NOTES_NEUTRALISE and not etudid in abs_etudids:
|
||||||
# Neutralisé mais pas signale absent
|
# Neutralisé mais pas signale absent
|
||||||
ExcNonSignalee.append(etudid)
|
exc_non_signalee.append(etudid)
|
||||||
if val == scu.NOTES_NEUTRALISE and etudid in abs_nj_etudids:
|
if val == scu.NOTES_NEUTRALISE and etudid in abs_nj_etudids:
|
||||||
# EXC mais pas justifié
|
# EXC mais pas justifié
|
||||||
ExcNonJust.append(etudid)
|
exc_non_just.append(etudid)
|
||||||
if val is None and etudid in just_etudids:
|
if val is None and etudid in just_etudids:
|
||||||
# ABS mais justificatif
|
# ABS mais justificatif
|
||||||
AbsButExc.append(etudid)
|
abs_but_exc.append(etudid)
|
||||||
|
|
||||||
return ValButAbs, AbsNonSignalee, ExcNonSignalee, ExcNonJust, AbsButExc
|
return note_but_abs, abs_non_signalee, exc_non_signalee, exc_non_just, abs_but_exc
|
||||||
|
|
||||||
|
|
||||||
def evaluation_check_absences_html(
|
def evaluation_check_absences_html(
|
||||||
evaluation: Evaluation, with_header=True, show_ok=True
|
evaluation: Evaluation, with_header=True, show_ok=True
|
||||||
):
|
):
|
||||||
"""Affiche état vérification absences d'une évaluation"""
|
"""Affiche état vérification absences d'une évaluation"""
|
||||||
am, pm = evaluation.is_matin(), evaluation.is_apresmidi()
|
|
||||||
# 1 si matin, 0 si apres midi, 2 si toute la journee:
|
|
||||||
match am, pm:
|
|
||||||
case False, True:
|
|
||||||
demijournee = 0
|
|
||||||
case True, False:
|
|
||||||
demijournee = 1
|
|
||||||
case _:
|
|
||||||
demijournee = 2
|
|
||||||
|
|
||||||
(
|
(
|
||||||
ValButAbs,
|
note_but_abs, # une note alors qu'il était signalé abs
|
||||||
AbsNonSignalee,
|
abs_non_signalee, # note ABS alors que pas signalé abs
|
||||||
ExcNonSignalee,
|
exc_non_signalee, # note EXC alors que pas signalé abs
|
||||||
ExcNonJust,
|
exc_non_just, # note EXC alors que pas de justif
|
||||||
AbsButExc,
|
abs_but_exc, # note ABS alors qu'il y a un justif
|
||||||
) = evaluation_check_absences(evaluation)
|
) = evaluation_check_absences(evaluation)
|
||||||
|
|
||||||
if with_header:
|
if with_header:
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.html_sem_header("Vérification absences à l'évaluation"),
|
html_sco_header.html_sem_header(
|
||||||
|
"Vérification absences à l'évaluation",
|
||||||
|
formsemestre_id=evaluation.moduleimpl.formsemestre_id,
|
||||||
|
),
|
||||||
sco_evaluations.evaluation_describe(evaluation_id=evaluation.id),
|
sco_evaluations.evaluation_describe(evaluation_id=evaluation.id),
|
||||||
"""<p class="help">Vérification de la cohérence entre les notes saisies
|
"""<p class="help">Vérification de la cohérence entre les notes saisies
|
||||||
et les absences signalées.</p>""",
|
et les absences signalées.</p>""",
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
@ -148,10 +139,10 @@ def evaluation_check_absences_html(
|
|||||||
} """
|
} """
|
||||||
]
|
]
|
||||||
if (
|
if (
|
||||||
not ValButAbs
|
not note_but_abs
|
||||||
and not AbsNonSignalee
|
and not abs_non_signalee
|
||||||
and not ExcNonSignalee
|
and not exc_non_signalee
|
||||||
and not ExcNonJust
|
and not exc_non_just
|
||||||
):
|
):
|
||||||
H.append(': <span class="eval_check_absences_ok">ok</span>')
|
H.append(': <span class="eval_check_absences_ok">ok</span>')
|
||||||
H.append("</h2>")
|
H.append("</h2>")
|
||||||
@ -171,46 +162,50 @@ def evaluation_check_absences_html(
|
|||||||
)
|
)
|
||||||
if linkabs:
|
if linkabs:
|
||||||
url = url_for(
|
url = url_for(
|
||||||
"assiduites.signal_evaluation_abs",
|
"assiduites.signale_evaluation_abs",
|
||||||
etudid=etudid,
|
etudid=etudid,
|
||||||
evaluation_id=evaluation.id,
|
evaluation_id=evaluation.id,
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
)
|
)
|
||||||
H.append(
|
H.append(
|
||||||
f"""<a class="stdlink" href="{url}">signaler cette absence</a>"""
|
f"""<a style="margin-left: 16px;" class="stdlink" href="{
|
||||||
|
url}">signaler cette absence</a>"""
|
||||||
)
|
)
|
||||||
H.append("</li>")
|
H.append("</li>")
|
||||||
H.append("</ul>")
|
H.append("</ul>")
|
||||||
|
|
||||||
if ValButAbs or show_ok:
|
if note_but_abs or show_ok:
|
||||||
H.append(
|
H.append(
|
||||||
"<h3>Etudiants ayant une note alors qu'ils sont signalés absents:</h3>"
|
"<h3>Étudiants ayant une note alors qu'ils sont signalés absents:</h3>"
|
||||||
)
|
)
|
||||||
etudlist(ValButAbs)
|
etudlist(note_but_abs)
|
||||||
|
|
||||||
if AbsNonSignalee or show_ok:
|
if abs_non_signalee or show_ok:
|
||||||
H.append(
|
H.append(
|
||||||
"""<h3>Etudiants avec note "ABS" alors qu'ils ne sont <em>pas</em> signalés absents:</h3>"""
|
"""<h3>Étudiants avec note "ABS" alors qu'ils ne sont
|
||||||
|
<em>pas</em> signalés absents:</h3>"""
|
||||||
)
|
)
|
||||||
etudlist(AbsNonSignalee, linkabs=True)
|
etudlist(abs_non_signalee, linkabs=True)
|
||||||
|
|
||||||
if ExcNonSignalee or show_ok:
|
if exc_non_signalee or show_ok:
|
||||||
H.append(
|
H.append(
|
||||||
"""<h3>Etudiants avec note "EXC" alors qu'ils ne sont <em>pas</em> signalés absents:</h3>"""
|
"""<h3>Étudiants avec note "EXC" alors qu'ils ne sont
|
||||||
|
<em>pas</em> signalés absents:</h3>"""
|
||||||
)
|
)
|
||||||
etudlist(ExcNonSignalee)
|
etudlist(exc_non_signalee)
|
||||||
|
|
||||||
if ExcNonJust or show_ok:
|
if exc_non_just or show_ok:
|
||||||
H.append(
|
H.append(
|
||||||
"""<h3>Etudiants avec note "EXC" alors qu'ils sont absents <em>non justifiés</em>:</h3>"""
|
"""<h3>Étudiants avec note "EXC" alors qu'ils sont absents
|
||||||
|
<em>non justifiés</em>:</h3>"""
|
||||||
)
|
)
|
||||||
etudlist(ExcNonJust)
|
etudlist(exc_non_just)
|
||||||
|
|
||||||
if AbsButExc or show_ok:
|
if abs_but_exc or show_ok:
|
||||||
H.append(
|
H.append(
|
||||||
"""<h3>Etudiants avec note "ABS" alors qu'ils ont une <em>justification</em>:</h3>"""
|
"""<h3>Étudiants avec note "ABS" alors qu'ils ont une <em>justification</em>:</h3>"""
|
||||||
)
|
)
|
||||||
etudlist(AbsButExc)
|
etudlist(abs_but_exc)
|
||||||
|
|
||||||
if with_header:
|
if with_header:
|
||||||
H.append(html_sco_header.sco_footer())
|
H.append(html_sco_header.sco_footer())
|
||||||
@ -226,7 +221,8 @@ def formsemestre_check_absences_html(formsemestre_id):
|
|||||||
html_sco_header.html_sem_header(
|
html_sco_header.html_sem_header(
|
||||||
"Vérification absences aux évaluations de ce semestre",
|
"Vérification absences aux évaluations de ce semestre",
|
||||||
),
|
),
|
||||||
"""<p class="help">Vérification de la cohérence entre les notes saisies et les absences signalées.
|
"""<p class="help">Vérification de la cohérence entre les notes saisies
|
||||||
|
et les absences signalées.
|
||||||
Sont listés tous les modules avec des évaluations.<br>Aucune action n'est effectuée:
|
Sont listés tous les modules avec des évaluations.<br>Aucune action n'est effectuée:
|
||||||
il vous appartient de corriger les erreurs détectées si vous le jugez nécessaire.
|
il vous appartient de corriger les erreurs détectées si vous le jugez nécessaire.
|
||||||
</p>""",
|
</p>""",
|
||||||
@ -237,14 +233,12 @@ def formsemestre_check_absences_html(formsemestre_id):
|
|||||||
H.append(
|
H.append(
|
||||||
f"""<div class="module_check_absences">
|
f"""<div class="module_check_absences">
|
||||||
<h2><a href="{
|
<h2><a href="{
|
||||||
url_for("notes.moduleimpl_status",
|
url_for("notes.moduleimpl_status",
|
||||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id)
|
scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id)
|
||||||
}">{modimpl.module.code or ""}: {modimpl.module.abbrev or ""}</a>
|
}">{modimpl.module.code or ""}: {modimpl.module.abbrev or ""}</a>
|
||||||
</h2>"""
|
</h2>"""
|
||||||
)
|
)
|
||||||
for evaluation in modimpl.evaluations.order_by(
|
for evaluation in modimpl.evaluations:
|
||||||
Evaluation.numero, Evaluation.date_debut
|
|
||||||
):
|
|
||||||
H.append(
|
H.append(
|
||||||
evaluation_check_absences_html(
|
evaluation_check_absences_html(
|
||||||
evaluation,
|
evaluation,
|
||||||
|
@ -33,8 +33,8 @@ import operator
|
|||||||
|
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
from flask import g
|
from flask import g
|
||||||
from flask_login import current_user
|
|
||||||
from flask import request
|
from flask import request
|
||||||
|
from flask_login import current_user
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.auth.models import User
|
from app.auth.models import User
|
||||||
@ -50,11 +50,9 @@ from app.scodoc import html_sco_header
|
|||||||
from app.scodoc import sco_cal
|
from app.scodoc import sco_cal
|
||||||
from app.scodoc import sco_evaluation_db
|
from app.scodoc import sco_evaluation_db
|
||||||
from app.scodoc import sco_edit_module
|
from app.scodoc import sco_edit_module
|
||||||
from app.scodoc import sco_edit_ue
|
|
||||||
from app.scodoc import sco_formsemestre_inscriptions
|
from app.scodoc import sco_formsemestre_inscriptions
|
||||||
from app.scodoc import sco_groups
|
from app.scodoc import sco_groups
|
||||||
from app.scodoc import sco_moduleimpl
|
from app.scodoc import sco_moduleimpl
|
||||||
from app.scodoc import sco_permissions_check
|
|
||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
from app.scodoc import sco_users
|
from app.scodoc import sco_users
|
||||||
import sco_version
|
import sco_version
|
||||||
@ -76,22 +74,21 @@ def notes_moyenne_median_mini_maxi(notes):
|
|||||||
if not n:
|
if not n:
|
||||||
return None, None, None, None
|
return None, None, None, None
|
||||||
moy = sum(notes) / n
|
moy = sum(notes) / n
|
||||||
median = ListMedian(notes)
|
median = list_median(notes)
|
||||||
mini = min(notes)
|
mini = min(notes)
|
||||||
maxi = max(notes)
|
maxi = max(notes)
|
||||||
return moy, median, mini, maxi
|
return moy, median, mini, maxi
|
||||||
|
|
||||||
|
|
||||||
def ListMedian(L):
|
def list_median(a_list: list):
|
||||||
"""Median of a list L"""
|
"""Median of a list L"""
|
||||||
n = len(L)
|
n = len(a_list)
|
||||||
if not n:
|
if not n:
|
||||||
raise ValueError("empty list")
|
raise ValueError("empty list")
|
||||||
L.sort()
|
a_list.sort()
|
||||||
if n % 2:
|
if n % 2:
|
||||||
return L[n // 2]
|
return a_list[n // 2]
|
||||||
else:
|
return (a_list[n // 2] + a_list[n // 2 - 1]) / 2
|
||||||
return (L[n // 2] + L[n // 2 - 1]) / 2
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
@ -190,39 +187,39 @@ def do_evaluation_etat(
|
|||||||
|
|
||||||
# On considere une note "manquante" lorsqu'elle n'existe pas
|
# On considere une note "manquante" lorsqu'elle n'existe pas
|
||||||
# ou qu'elle est en attente (ATT)
|
# ou qu'elle est en attente (ATT)
|
||||||
GrNbMissing = collections.defaultdict(int) # group_id : nb notes manquantes
|
group_nb_missing = collections.defaultdict(int) # group_id : nb notes manquantes
|
||||||
GrNotes = collections.defaultdict(list) # group_id: liste notes valides
|
group_notes = collections.defaultdict(list) # group_id: liste notes valides
|
||||||
TotalNbMissing = 0
|
total_nb_missing = 0
|
||||||
TotalNbAtt = 0
|
total_nb_att = 0
|
||||||
groups = {} # group_id : group
|
group_by_id = {} # group_id : group
|
||||||
etud_groups = sco_groups.get_etud_groups_in_partition(partition_id)
|
etud_groups = sco_groups.get_etud_groups_in_partition(partition_id)
|
||||||
|
|
||||||
for i in ins:
|
for i in ins:
|
||||||
group = etud_groups.get(i["etudid"], None)
|
group = etud_groups.get(i["etudid"], None)
|
||||||
if group and not group["group_id"] in groups:
|
if group and not group["group_id"] in group_by_id:
|
||||||
groups[group["group_id"]] = group
|
group_by_id[group["group_id"]] = group
|
||||||
#
|
#
|
||||||
isMissing = False
|
is_missing = False
|
||||||
if i["etudid"] in etuds_notes_dict:
|
if i["etudid"] in etuds_notes_dict:
|
||||||
val = etuds_notes_dict[i["etudid"]]["value"]
|
val = etuds_notes_dict[i["etudid"]]["value"]
|
||||||
if val == scu.NOTES_ATTENTE:
|
if val == scu.NOTES_ATTENTE:
|
||||||
isMissing = True
|
is_missing = True
|
||||||
TotalNbAtt += 1
|
total_nb_att += 1
|
||||||
if group:
|
if group:
|
||||||
GrNotes[group["group_id"]].append(val)
|
group_notes[group["group_id"]].append(val)
|
||||||
else:
|
else:
|
||||||
if group:
|
if group:
|
||||||
_ = GrNotes[group["group_id"]] # create group
|
_ = group_notes[group["group_id"]] # create group
|
||||||
isMissing = True
|
is_missing = True
|
||||||
if isMissing:
|
if is_missing:
|
||||||
TotalNbMissing += 1
|
total_nb_missing += 1
|
||||||
if group:
|
if group:
|
||||||
GrNbMissing[group["group_id"]] += 1
|
group_nb_missing[group["group_id"]] += 1
|
||||||
|
|
||||||
gr_incomplets = [x for x in GrNbMissing.keys()]
|
gr_incomplets = list(group_nb_missing.keys())
|
||||||
gr_incomplets.sort()
|
gr_incomplets.sort()
|
||||||
if (
|
if (
|
||||||
(TotalNbMissing > 0)
|
(total_nb_missing > 0)
|
||||||
and (E["evaluation_type"] != scu.EVALUATION_RATTRAPAGE)
|
and (E["evaluation_type"] != scu.EVALUATION_RATTRAPAGE)
|
||||||
and (E["evaluation_type"] != scu.EVALUATION_SESSION2)
|
and (E["evaluation_type"] != scu.EVALUATION_SESSION2)
|
||||||
):
|
):
|
||||||
@ -231,12 +228,12 @@ def do_evaluation_etat(
|
|||||||
complete = True
|
complete = True
|
||||||
|
|
||||||
complete = (
|
complete = (
|
||||||
(TotalNbMissing == 0)
|
(total_nb_missing == 0)
|
||||||
or (E["evaluation_type"] == scu.EVALUATION_RATTRAPAGE)
|
or (E["evaluation_type"] == scu.EVALUATION_RATTRAPAGE)
|
||||||
or (E["evaluation_type"] == scu.EVALUATION_SESSION2)
|
or (E["evaluation_type"] == scu.EVALUATION_SESSION2)
|
||||||
)
|
)
|
||||||
evalattente = (TotalNbMissing > 0) and (
|
evalattente = (total_nb_missing > 0) and (
|
||||||
(TotalNbMissing == TotalNbAtt) or E["publish_incomplete"]
|
(total_nb_missing == total_nb_att) or E["publish_incomplete"]
|
||||||
)
|
)
|
||||||
# mais ne met pas en attente les evals immediates sans aucune notes:
|
# mais ne met pas en attente les evals immediates sans aucune notes:
|
||||||
if E["publish_incomplete"] and nb_notes == 0:
|
if E["publish_incomplete"] and nb_notes == 0:
|
||||||
@ -244,12 +241,12 @@ def do_evaluation_etat(
|
|||||||
|
|
||||||
# Calcul moyenne dans chaque groupe de TD
|
# Calcul moyenne dans chaque groupe de TD
|
||||||
gr_moyennes = [] # group : {moy,median, nb_notes}
|
gr_moyennes = [] # group : {moy,median, nb_notes}
|
||||||
for group_id, notes in GrNotes.items():
|
for group_id, notes in group_notes.items():
|
||||||
gr_moy, gr_median, gr_mini, gr_maxi = notes_moyenne_median_mini_maxi(notes)
|
gr_moy, gr_median, gr_mini, gr_maxi = notes_moyenne_median_mini_maxi(notes)
|
||||||
gr_moyennes.append(
|
gr_moyennes.append(
|
||||||
{
|
{
|
||||||
"group_id": group_id,
|
"group_id": group_id,
|
||||||
"group_name": groups[group_id]["group_name"],
|
"group_name": group_by_id[group_id]["group_name"],
|
||||||
"gr_moy": scu.fmt_note(gr_moy, E["note_max"]),
|
"gr_moy": scu.fmt_note(gr_moy, E["note_max"]),
|
||||||
"gr_median": scu.fmt_note(gr_median, E["note_max"]),
|
"gr_median": scu.fmt_note(gr_median, E["note_max"]),
|
||||||
"gr_mini": scu.fmt_note(gr_mini, E["note_max"]),
|
"gr_mini": scu.fmt_note(gr_mini, E["note_max"]),
|
||||||
@ -276,7 +273,7 @@ def do_evaluation_etat(
|
|||||||
"last_modif": last_modif,
|
"last_modif": last_modif,
|
||||||
"gr_incomplets": gr_incomplets,
|
"gr_incomplets": gr_incomplets,
|
||||||
"gr_moyennes": gr_moyennes,
|
"gr_moyennes": gr_moyennes,
|
||||||
"groups": groups,
|
"groups": group_by_id,
|
||||||
"evalcomplete": complete,
|
"evalcomplete": complete,
|
||||||
"evalattente": evalattente,
|
"evalattente": evalattente,
|
||||||
"is_malus": is_malus,
|
"is_malus": is_malus,
|
||||||
@ -413,7 +410,7 @@ def do_evaluation_etat_in_sem(formsemestre_id):
|
|||||||
|
|
||||||
|
|
||||||
def do_evaluation_etat_in_mod(nt, moduleimpl_id):
|
def do_evaluation_etat_in_mod(nt, moduleimpl_id):
|
||||||
""""""
|
"""état des évaluations dans ce module"""
|
||||||
evals = nt.get_mod_evaluation_etat_list(moduleimpl_id)
|
evals = nt.get_mod_evaluation_etat_list(moduleimpl_id)
|
||||||
etat = _eval_etat(evals)
|
etat = _eval_etat(evals)
|
||||||
# Il y a-t-il des notes en attente dans ce module ?
|
# Il y a-t-il des notes en attente dans ce module ?
|
||||||
@ -426,7 +423,7 @@ def formsemestre_evaluations_cal(formsemestre_id):
|
|||||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||||
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||||
|
|
||||||
evaluations = formsemestre.get_evaluations() # TODO
|
evaluations = formsemestre.get_evaluations()
|
||||||
nb_evals = len(evaluations)
|
nb_evals = len(evaluations)
|
||||||
|
|
||||||
color_incomplete = "#FF6060"
|
color_incomplete = "#FF6060"
|
||||||
@ -642,7 +639,7 @@ def formsemestre_evaluations_delai_correction(formsemestre_id, fmt="html"):
|
|||||||
|
|
||||||
|
|
||||||
# -------------- VIEWS
|
# -------------- VIEWS
|
||||||
def evaluation_describe(evaluation_id="", edit_in_place=True, link_saisie=True):
|
def evaluation_describe(evaluation_id="", edit_in_place=True, link_saisie=True) -> str:
|
||||||
"""HTML description of evaluation, for page headers
|
"""HTML description of evaluation, for page headers
|
||||||
edit_in_place: allow in-place editing when permitted (not implemented)
|
edit_in_place: allow in-place editing when permitted (not implemented)
|
||||||
"""
|
"""
|
||||||
@ -696,7 +693,15 @@ def evaluation_describe(evaluation_id="", edit_in_place=True, link_saisie=True):
|
|||||||
date_debut=evaluation.date_debut.isoformat(),
|
date_debut=evaluation.date_debut.isoformat(),
|
||||||
date_fin=evaluation.date_fin.isoformat(),
|
date_fin=evaluation.date_fin.isoformat(),
|
||||||
)
|
)
|
||||||
}">absences ce jour</a></span>"""
|
}">absences ce jour</a>
|
||||||
|
</span>
|
||||||
|
<span class="evallink"><a class="stdlink" href="{url_for(
|
||||||
|
'notes.evaluation_check_absences_html',
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
evaluation_id = evaluation.id)
|
||||||
|
}">vérifier notes vs absences</a>
|
||||||
|
</span>
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
H.append("<p><em>sans date</em> ")
|
H.append("<p><em>sans date</em> ")
|
||||||
|
@ -33,18 +33,15 @@ import numpy as np
|
|||||||
import flask
|
import flask
|
||||||
from flask import url_for, g, request
|
from flask import url_for, g, request
|
||||||
|
|
||||||
from app import db, log
|
|
||||||
from app import models
|
|
||||||
from app.comp import res_sem
|
from app.comp import res_sem
|
||||||
from app.comp import moy_mod
|
from app.comp import moy_mod
|
||||||
from app.comp.moy_mod import ModuleImplResults
|
from app.comp.moy_mod import ModuleImplResults
|
||||||
from app.comp.res_compat import NotesTableCompat
|
|
||||||
from app.comp.res_but import ResultatsSemestreBUT
|
from app.comp.res_but import ResultatsSemestreBUT
|
||||||
|
from app.comp.res_compat import NotesTableCompat
|
||||||
from app.models import FormSemestre, Module
|
from app.models import FormSemestre, Module
|
||||||
from app.models.etudiants import Identite
|
from app.models.etudiants import Identite
|
||||||
from app.models.evaluations import Evaluation
|
from app.models.evaluations import Evaluation
|
||||||
from app.models.moduleimpls import ModuleImpl
|
from app.models.moduleimpls import ModuleImpl
|
||||||
import app.scodoc.notesdb as ndb
|
|
||||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||||
|
|
||||||
from app.scodoc.sco_etud import etud_sort_key
|
from app.scodoc.sco_etud import etud_sort_key
|
||||||
@ -54,58 +51,58 @@ from app.scodoc import sco_groups
|
|||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
from app.scodoc import sco_users
|
from app.scodoc import sco_users
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
import sco_version
|
|
||||||
from app.scodoc.gen_tables import GenTable
|
from app.scodoc.gen_tables import GenTable
|
||||||
from app.scodoc.htmlutils import histogram_notes
|
from app.scodoc.htmlutils import histogram_notes
|
||||||
|
import sco_version
|
||||||
|
|
||||||
|
|
||||||
def do_evaluation_listenotes(
|
def do_evaluation_listenotes(
|
||||||
evaluation_id=None, moduleimpl_id=None, fmt="html"
|
evaluation_id=None, moduleimpl_id=None, fmt="html"
|
||||||
) -> tuple[str, str]:
|
) -> tuple[str | flask.Response, str]:
|
||||||
"""
|
"""
|
||||||
Affichage des notes d'une évaluation (si evaluation_id)
|
Affichage des notes d'une évaluation (si evaluation_id)
|
||||||
ou de toutes les évaluations d'un module (si moduleimpl_id)
|
ou de toutes les évaluations d'un module (si moduleimpl_id)
|
||||||
"""
|
"""
|
||||||
mode = None
|
mode = None
|
||||||
if moduleimpl_id:
|
evaluations: list[Evaluation] = []
|
||||||
|
if moduleimpl_id is not None:
|
||||||
mode = "module"
|
mode = "module"
|
||||||
evals = sco_evaluation_db.get_evaluations_dict({"moduleimpl_id": moduleimpl_id})
|
modimpl = ModuleImpl.query.get_or_404(moduleimpl_id)
|
||||||
elif evaluation_id:
|
evaluations = modimpl.evaluations.all()
|
||||||
|
elif evaluation_id is not None:
|
||||||
mode = "eval"
|
mode = "eval"
|
||||||
evals = sco_evaluation_db.get_evaluations_dict({"evaluation_id": evaluation_id})
|
evaluations = Evaluation.query.filter_by(id=evaluation_id).all()
|
||||||
else:
|
else:
|
||||||
raise ValueError("missing argument: evaluation or module")
|
raise ValueError("missing argument: evaluation or module")
|
||||||
if not evals:
|
if not evaluations:
|
||||||
return "<p>Aucune évaluation !</p>", "ScoDoc"
|
return "<p>Aucune évaluation !</p>", "ScoDoc"
|
||||||
|
evaluation = evaluations[0]
|
||||||
|
modimpl = evaluation.moduleimpl # il y a au moins une evaluation
|
||||||
|
|
||||||
eval_dict = evals[0] # il y a au moins une evaluation
|
|
||||||
modimpl = db.session.get(ModuleImpl, eval_dict["moduleimpl_id"])
|
|
||||||
# description de l'evaluation
|
# description de l'evaluation
|
||||||
if mode == "eval":
|
if evaluation_id is not None:
|
||||||
H = [sco_evaluations.evaluation_describe(evaluation_id=evaluation_id)]
|
H = [sco_evaluations.evaluation_describe(evaluation_id=evaluation_id)]
|
||||||
page_title = f"Notes {eval_dict['description'] or modimpl.module.code}"
|
page_title = f"Notes {evaluation.description or modimpl.module.code}"
|
||||||
else:
|
else:
|
||||||
H = []
|
H = []
|
||||||
page_title = f"Notes {modimpl.module.code}"
|
page_title = f"Notes {modimpl.module.code}"
|
||||||
# groupes
|
# groupes
|
||||||
groups = sco_groups.do_evaluation_listegroupes(
|
groups = sco_groups.do_evaluation_listegroupes(evaluation.id, include_default=True)
|
||||||
eval_dict["evaluation_id"], include_default=True
|
|
||||||
)
|
|
||||||
grlabs = [g["group_name"] or "tous" for g in groups] # legendes des boutons
|
grlabs = [g["group_name"] or "tous" for g in groups] # legendes des boutons
|
||||||
grnams = [str(g["group_id"]) for g in groups] # noms des checkbox
|
grnams = [str(g["group_id"]) for g in groups] # noms des checkbox
|
||||||
|
|
||||||
if len(evals) > 1:
|
if len(evaluations) > 1:
|
||||||
descr = [
|
descr = [
|
||||||
(
|
(
|
||||||
"moduleimpl_id",
|
"moduleimpl_id",
|
||||||
{"default": eval_dict["moduleimpl_id"], "input_type": "hidden"},
|
{"default": modimpl.id, "input_type": "hidden"},
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
descr = [
|
descr = [
|
||||||
(
|
(
|
||||||
"evaluation_id",
|
"evaluation_id",
|
||||||
{"default": eval_dict["evaluation_id"], "input_type": "hidden"},
|
{"default": evaluation.id, "input_type": "hidden"},
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
if len(grnams) > 1:
|
if len(grnams) > 1:
|
||||||
@ -148,7 +145,8 @@ def do_evaluation_listenotes(
|
|||||||
"allowed_values": ("yes",),
|
"allowed_values": ("yes",),
|
||||||
"labels": ('listing "anonyme"',),
|
"labels": ('listing "anonyme"',),
|
||||||
"attributes": ('onclick="document.tf.submit();"',),
|
"attributes": ('onclick="document.tf.submit();"',),
|
||||||
"template": '<tr><td class="tf-fieldlabel">%(label)s</td><td class="tf-field">%(elem)s ',
|
"template": """<tr><td class="tf-fieldlabel">%(label)s</td>
|
||||||
|
<td class="tf-field">%(elem)s """,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@ -205,7 +203,7 @@ def do_evaluation_listenotes(
|
|||||||
url_for(
|
url_for(
|
||||||
"notes.moduleimpl_status",
|
"notes.moduleimpl_status",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
moduleimpl_id=eval_dict["moduleimpl_id"],
|
moduleimpl_id=modimpl.id,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"",
|
"",
|
||||||
@ -219,7 +217,7 @@ def do_evaluation_listenotes(
|
|||||||
return (
|
return (
|
||||||
_make_table_notes(
|
_make_table_notes(
|
||||||
tf[1],
|
tf[1],
|
||||||
evals,
|
evaluations,
|
||||||
fmt=fmt,
|
fmt=fmt,
|
||||||
note_sur_20=note_sur_20,
|
note_sur_20=note_sur_20,
|
||||||
anonymous_listing=anonymous_listing,
|
anonymous_listing=anonymous_listing,
|
||||||
@ -234,37 +232,36 @@ def do_evaluation_listenotes(
|
|||||||
|
|
||||||
def _make_table_notes(
|
def _make_table_notes(
|
||||||
html_form,
|
html_form,
|
||||||
evals,
|
evaluations: list[Evaluation],
|
||||||
fmt: str = "",
|
fmt: str = "",
|
||||||
note_sur_20=False,
|
note_sur_20=False,
|
||||||
anonymous_listing=False,
|
anonymous_listing=False,
|
||||||
hide_groups=False,
|
hide_groups=False,
|
||||||
with_emails=False,
|
with_emails=False,
|
||||||
group_ids: list[int] = None,
|
group_ids: list[int] | None = None,
|
||||||
mode="module", # "eval" or "module"
|
mode="module", # "eval" or "module"
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Table liste notes (une seule évaluation ou toutes celles d'un module)"""
|
"""Table liste notes (une seule évaluation ou toutes celles d'un module)"""
|
||||||
group_ids = group_ids or []
|
group_ids = group_ids or []
|
||||||
if not evals:
|
if not evaluations:
|
||||||
return "<p>Aucune évaluation !</p>"
|
return "<p>Aucune évaluation !</p>"
|
||||||
E = evals[0]
|
evaluation = evaluations[0]
|
||||||
moduleimpl_id = E["moduleimpl_id"]
|
modimpl = evaluation.moduleimpl
|
||||||
modimpl = ModuleImpl.query.get_or_404(moduleimpl_id)
|
|
||||||
module: Module = modimpl.module
|
module: Module = modimpl.module
|
||||||
formsemestre: FormSemestre = modimpl.formsemestre
|
formsemestre: FormSemestre = modimpl.formsemestre
|
||||||
is_apc = module.formation.get_cursus().APC_SAE
|
is_apc = module.formation.is_apc()
|
||||||
if is_apc:
|
if is_apc:
|
||||||
res: ResultatsSemestreBUT = res_sem.load_formsemestre_results(formsemestre)
|
res: ResultatsSemestreBUT = res_sem.load_formsemestre_results(formsemestre)
|
||||||
is_conforme = modimpl.check_apc_conformity(res)
|
is_conforme = modimpl.check_apc_conformity(res)
|
||||||
evals_poids, ues = moy_mod.load_evaluations_poids(moduleimpl_id)
|
evals_poids, ues = moy_mod.load_evaluations_poids(modimpl.id)
|
||||||
if not ues:
|
if not ues:
|
||||||
is_apc = False
|
is_apc = False
|
||||||
else:
|
else:
|
||||||
evals_poids, ues = None, None
|
evals_poids, ues = None, None
|
||||||
is_conforme = True
|
is_conforme = True
|
||||||
# (debug) check that all evals are in same module:
|
# (debug) check that all evals are in same module:
|
||||||
for e in evals:
|
for e in evaluations:
|
||||||
if e["moduleimpl_id"] != moduleimpl_id:
|
if e.moduleimpl_id != modimpl.id:
|
||||||
raise ValueError("invalid evaluations list")
|
raise ValueError("invalid evaluations list")
|
||||||
|
|
||||||
if fmt == "xls":
|
if fmt == "xls":
|
||||||
@ -302,11 +299,14 @@ def _make_table_notes(
|
|||||||
}
|
}
|
||||||
rows = []
|
rows = []
|
||||||
|
|
||||||
class KeyManager(dict): # comment : key (pour regrouper les comments a la fin)
|
class KeyManager(dict):
|
||||||
|
"comment : key (pour regrouper les comments a la fin)"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.lastkey = 1
|
self.lastkey = 1
|
||||||
|
|
||||||
def nextkey(self):
|
def nextkey(self) -> str:
|
||||||
|
"get new key (int)"
|
||||||
r = self.lastkey
|
r = self.lastkey
|
||||||
self.lastkey += 1
|
self.lastkey += 1
|
||||||
# self.lastkey = chr(ord(self.lastkey)+1)
|
# self.lastkey = chr(ord(self.lastkey)+1)
|
||||||
@ -323,7 +323,7 @@ def _make_table_notes(
|
|||||||
anonymous_lst_key = "etudid"
|
anonymous_lst_key = "etudid"
|
||||||
|
|
||||||
etudid_etats = sco_groups.do_evaluation_listeetuds_groups(
|
etudid_etats = sco_groups.do_evaluation_listeetuds_groups(
|
||||||
E["evaluation_id"], groups, include_demdef=True
|
evaluation.id, groups, include_demdef=True
|
||||||
)
|
)
|
||||||
for etudid, etat in etudid_etats:
|
for etudid, etat in etudid_etats:
|
||||||
css_row_class = None
|
css_row_class = None
|
||||||
@ -360,7 +360,8 @@ def _make_table_notes(
|
|||||||
formsemestre_id=formsemestre.id,
|
formsemestre_id=formsemestre.id,
|
||||||
etudid=etudid,
|
etudid=etudid,
|
||||||
),
|
),
|
||||||
"_nomprenom_td_attrs": f"""id="{etudid}" class="etudinfo" data-sort="{etud.sort_key}" """,
|
"_nomprenom_td_attrs": f"""id="{etudid}" class="etudinfo" data-sort="{
|
||||||
|
etud.sort_key}" """,
|
||||||
"prenom": etud.prenom.lower().capitalize(),
|
"prenom": etud.prenom.lower().capitalize(),
|
||||||
"nom_usuel": etud.nom_usuel,
|
"nom_usuel": etud.nom_usuel,
|
||||||
"nomprenom": etud.nomprenom,
|
"nomprenom": etud.nomprenom,
|
||||||
@ -408,10 +409,12 @@ def _make_table_notes(
|
|||||||
"comment": "",
|
"comment": "",
|
||||||
}
|
}
|
||||||
# Ajoute les notes de chaque évaluation:
|
# Ajoute les notes de chaque évaluation:
|
||||||
for e in evals:
|
evals_state: dict[int, dict] = {}
|
||||||
e["eval_state"] = sco_evaluations.do_evaluation_etat(e["evaluation_id"])
|
for e in evaluations:
|
||||||
|
evals_state[e.id] = sco_evaluations.do_evaluation_etat(e.id)
|
||||||
notes, nb_abs, nb_att = _add_eval_columns(
|
notes, nb_abs, nb_att = _add_eval_columns(
|
||||||
e,
|
e,
|
||||||
|
evals_state[e.id],
|
||||||
evals_poids,
|
evals_poids,
|
||||||
ues,
|
ues,
|
||||||
rows,
|
rows,
|
||||||
@ -426,7 +429,7 @@ def _make_table_notes(
|
|||||||
keep_numeric,
|
keep_numeric,
|
||||||
fmt=fmt,
|
fmt=fmt,
|
||||||
)
|
)
|
||||||
columns_ids.append(e["evaluation_id"])
|
columns_ids.append(e.id)
|
||||||
#
|
#
|
||||||
if anonymous_listing:
|
if anonymous_listing:
|
||||||
rows.sort(key=lambda x: x["code"] or "")
|
rows.sort(key=lambda x: x["code"] or "")
|
||||||
@ -436,12 +439,12 @@ def _make_table_notes(
|
|||||||
|
|
||||||
# Si module, ajoute la (les) "moyenne(s) du module:
|
# Si module, ajoute la (les) "moyenne(s) du module:
|
||||||
if mode == "module":
|
if mode == "module":
|
||||||
if len(evals) > 1:
|
if len(evaluations) > 1:
|
||||||
# Moyenne de l'étudiant dans le module
|
# Moyenne de l'étudiant dans le module
|
||||||
# Affichée même en APC à titre indicatif
|
# Affichée même en APC à titre indicatif
|
||||||
_add_moymod_column(
|
_add_moymod_column(
|
||||||
formsemestre.id,
|
formsemestre.id,
|
||||||
moduleimpl_id,
|
modimpl.id,
|
||||||
rows,
|
rows,
|
||||||
columns_ids,
|
columns_ids,
|
||||||
titles,
|
titles,
|
||||||
@ -473,7 +476,7 @@ def _make_table_notes(
|
|||||||
if with_emails:
|
if with_emails:
|
||||||
columns_ids += ["email", "emailperso"]
|
columns_ids += ["email", "emailperso"]
|
||||||
# Ajoute lignes en tête et moyennes
|
# Ajoute lignes en tête et moyennes
|
||||||
if len(evals) > 0 and fmt != "bordereau":
|
if len(evaluations) > 0 and fmt != "bordereau":
|
||||||
rows_head = [row_coefs]
|
rows_head = [row_coefs]
|
||||||
if is_apc:
|
if is_apc:
|
||||||
rows_head.append(row_poids)
|
rows_head.append(row_poids)
|
||||||
@ -481,22 +484,22 @@ def _make_table_notes(
|
|||||||
rows = rows_head + rows
|
rows = rows_head + rows
|
||||||
rows.append(row_moys)
|
rows.append(row_moys)
|
||||||
# ajout liens HTMl vers affichage une evaluation:
|
# ajout liens HTMl vers affichage une evaluation:
|
||||||
if fmt == "html" and len(evals) > 1:
|
if fmt == "html" and len(evaluations) > 1:
|
||||||
rlinks = {"_table_part": "head"}
|
rlinks = {"_table_part": "head"}
|
||||||
for e in evals:
|
for e in evaluations:
|
||||||
rlinks[e["evaluation_id"]] = "afficher"
|
rlinks[e.id] = "afficher"
|
||||||
rlinks[
|
rlinks[
|
||||||
"_" + str(e["evaluation_id"]) + "_help"
|
"_" + str(e.id) + "_help"
|
||||||
] = "afficher seulement les notes de cette évaluation"
|
] = "afficher seulement les notes de cette évaluation"
|
||||||
rlinks["_" + str(e["evaluation_id"]) + "_target"] = url_for(
|
rlinks["_" + str(e.id) + "_target"] = url_for(
|
||||||
"notes.evaluation_listenotes",
|
"notes.evaluation_listenotes",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
evaluation_id=e["evaluation_id"],
|
evaluation_id=e.id,
|
||||||
)
|
)
|
||||||
rlinks["_" + str(e["evaluation_id"]) + "_td_attrs"] = ' class="tdlink" '
|
rlinks["_" + str(e.id) + "_td_attrs"] = ' class="tdlink" '
|
||||||
rows.append(rlinks)
|
rows.append(rlinks)
|
||||||
|
|
||||||
if len(evals) == 1: # colonne "Rem." seulement si une eval
|
if len(evaluations) == 1: # colonne "Rem." seulement si une eval
|
||||||
if fmt == "html": # pas d'indication d'origine en pdf (pour affichage)
|
if fmt == "html": # pas d'indication d'origine en pdf (pour affichage)
|
||||||
columns_ids.append("expl_key")
|
columns_ids.append("expl_key")
|
||||||
elif fmt == "xls" or fmt == "xml":
|
elif fmt == "xls" or fmt == "xml":
|
||||||
@ -514,68 +517,84 @@ def _make_table_notes(
|
|||||||
gl = "&hide_groups%3Alist=yes" + gl
|
gl = "&hide_groups%3Alist=yes" + gl
|
||||||
if with_emails:
|
if with_emails:
|
||||||
gl = "&with_emails%3Alist=yes" + gl
|
gl = "&with_emails%3Alist=yes" + gl
|
||||||
if len(evals) == 1:
|
if len(evaluations) == 1:
|
||||||
evalname = "%s-%s" % (module.code, ndb.DateDMYtoISO(E["jour"]))
|
evalname = f"""{module.code}-{
|
||||||
hh = "%s, %s (%d étudiants)" % (E["description"], gr_title, len(etudid_etats))
|
evaluation.date_debut.replace(tzinfo=None).isoformat()
|
||||||
filename = scu.make_filename("notes_%s_%s" % (evalname, gr_title_filename))
|
if evaluation.date_debut else ""}"""
|
||||||
|
hh = "%s, %s (%d étudiants)" % (
|
||||||
|
evaluation.description,
|
||||||
|
gr_title,
|
||||||
|
len(etudid_etats),
|
||||||
|
)
|
||||||
|
filename = scu.make_filename(f"notes_{evalname}_{gr_title_filename}")
|
||||||
|
|
||||||
if fmt == "bordereau":
|
if fmt == "bordereau":
|
||||||
hh = " %d étudiants" % (len(etudid_etats))
|
hh = f""" {len(etudid_etats)} étudiants {
|
||||||
hh += " %d absent" % (nb_abs)
|
nb_abs} absent{'s' if nb_abs > 1 else ''}, {nb_att} en attente."""
|
||||||
if nb_abs > 1:
|
|
||||||
hh += "s"
|
|
||||||
hh += ", %d en attente." % (nb_att)
|
|
||||||
|
|
||||||
# Attention: ReportLab supporte seulement '<br/>', pas '<br>' !
|
# Attention: ReportLab supporte seulement '<br/>', pas '<br>' !
|
||||||
pdf_title = f"""<br/> BORDEREAU DE SIGNATURES
|
pdf_title = f"""<br/> BORDEREAU DE SIGNATURES
|
||||||
<br/><br/>{formsemestre.titre or ''}
|
<br/><br/>{formsemestre.titre or ''}
|
||||||
<br/>({formsemestre.mois_debut()} - {formsemestre.mois_fin()})
|
<br/>({formsemestre.mois_debut()} - {formsemestre.mois_fin()})
|
||||||
semestre {formsemestre.semestre_id} {formsemestre.modalite or ""}
|
semestre {formsemestre.semestre_id} {formsemestre.modalite or ""}
|
||||||
<br/>Notes du module {module.code} - {module.titre}
|
<br/>Notes du module {module.code} - {module.titre}
|
||||||
<br/>Évaluation : {e["description"]}
|
<br/>Évaluation : {evaluation.description}
|
||||||
"""
|
"""
|
||||||
if len(e["jour"]) > 0:
|
if evaluation.date_debut:
|
||||||
pdf_title += " (%(jour)s)" % e
|
pdf_title += f" ({evaluation.date_debut.strftime('%d/%m/%Y')})"
|
||||||
pdf_title += "(noté sur %(note_max)s )<br/><br/>" % e
|
pdf_title += "(noté sur {evaluation.note_max} )<br/><br/>"
|
||||||
else:
|
else:
|
||||||
hh = " %s, %s (%d étudiants)" % (
|
hh = " %s, %s (%d étudiants)" % (
|
||||||
E["description"],
|
evaluation.description,
|
||||||
gr_title,
|
gr_title,
|
||||||
len(etudid_etats),
|
len(etudid_etats),
|
||||||
)
|
)
|
||||||
if len(e["jour"]) > 0:
|
if evaluation.date_debut:
|
||||||
pdf_title = "%(description)s (%(jour)s)" % e
|
pdf_title = f"{evaluation.description} ({evaluation.date_debut.strftime('%d/%m/%Y')})"
|
||||||
else:
|
else:
|
||||||
pdf_title = "%(description)s " % e
|
pdf_title = evaluation.description or f"évaluation dans {module.code}"
|
||||||
|
|
||||||
caption = hh
|
caption = hh
|
||||||
html_title = ""
|
html_title = ""
|
||||||
base_url = "evaluation_listenotes?evaluation_id=%s" % E["evaluation_id"] + gl
|
base_url = (
|
||||||
html_next_section = (
|
url_for(
|
||||||
'<div class="notes_evaluation_stats">%d absents, %d en attente.</div>'
|
"notes.evaluation_listenotes",
|
||||||
% (nb_abs, nb_att)
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
evaluation_id=evaluation.id,
|
||||||
|
)
|
||||||
|
+ gl
|
||||||
)
|
)
|
||||||
|
html_next_section = f"""<div class="notes_evaluation_stats">{nb_abs} absents,
|
||||||
|
{nb_att} en attente.</div>"""
|
||||||
else:
|
else:
|
||||||
|
# Plusieurs évaluations (module)
|
||||||
filename = scu.make_filename("notes_%s_%s" % (module.code, gr_title_filename))
|
filename = scu.make_filename("notes_%s_%s" % (module.code, gr_title_filename))
|
||||||
title = f"Notes {module.type_name()} {module.code} {module.titre}"
|
title = f"Notes {module.type_name()} {module.code} {module.titre}"
|
||||||
title += f""" semestre {formsemestre.titre_mois()}"""
|
title += f""" semestre {formsemestre.titre_mois()}"""
|
||||||
if gr_title and gr_title != "tous":
|
if gr_title and gr_title != "tous":
|
||||||
title += " %s" % gr_title
|
title += " {gr_title}"
|
||||||
caption = title
|
caption = title
|
||||||
html_next_section = ""
|
html_next_section = ""
|
||||||
if fmt == "pdf" or fmt == "bordereau":
|
if fmt == "pdf" or fmt == "bordereau":
|
||||||
caption = "" # same as pdf_title
|
caption = "" # same as pdf_title
|
||||||
pdf_title = title
|
pdf_title = title
|
||||||
html_title = f"""<h2 class="formsemestre">Notes {module.type_name()} <a href="{
|
html_title = f"""<h2 class="formsemestre">Notes {module.type_name()}
|
||||||
url_for("notes.moduleimpl_status",
|
<a class="stdlink" href="{
|
||||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id)
|
url_for("notes.moduleimpl_status",
|
||||||
|
scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id)
|
||||||
}">{module.code} {module.titre}</a></h2>
|
}">{module.code} {module.titre}</a></h2>
|
||||||
"""
|
"""
|
||||||
if not is_conforme:
|
if not is_conforme:
|
||||||
html_title += (
|
html_title += (
|
||||||
"""<div class="warning">Poids des évaluations non conformes !</div>"""
|
"""<div class="warning">Poids des évaluations non conformes !</div>"""
|
||||||
)
|
)
|
||||||
base_url = "evaluation_listenotes?moduleimpl_id=%s" % moduleimpl_id + gl
|
base_url = (
|
||||||
|
url_for(
|
||||||
|
"notes.evaluation_listenotes",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
moduleimpl_id=modimpl.id,
|
||||||
|
)
|
||||||
|
+ gl
|
||||||
|
)
|
||||||
# display
|
# display
|
||||||
tab = GenTable(
|
tab = GenTable(
|
||||||
titles=titles,
|
titles=titles,
|
||||||
@ -600,64 +619,70 @@ def _make_table_notes(
|
|||||||
if fmt != "html":
|
if fmt != "html":
|
||||||
return t
|
return t
|
||||||
|
|
||||||
if len(evals) > 1:
|
if len(evaluations) > 1:
|
||||||
all_complete = True
|
all_complete = True
|
||||||
for e in evals:
|
for e in evaluations:
|
||||||
if not e["eval_state"]["evalcomplete"]:
|
if not evals_state[e.id]["evalcomplete"]:
|
||||||
all_complete = False
|
all_complete = False
|
||||||
if all_complete:
|
if all_complete:
|
||||||
eval_info = """<span class="eval_info"><span class="eval_complete">Évaluations
|
eval_info = """<span class="eval_info"><span class="eval_complete">Évaluations
|
||||||
prises en compte dans les moyennes.</span>"""
|
prises en compte dans les moyennes.</span>"""
|
||||||
else:
|
else:
|
||||||
eval_info = """<span class="eval_info help">
|
eval_info = """<span class="eval_info help">
|
||||||
Les évaluations en vert et orange sont prises en compte dans les moyennes.
|
Les évaluations en vert et orange sont prises en compte dans les moyennes.
|
||||||
Celles en rouge n'ont pas toutes leurs notes."""
|
Celles en rouge n'ont pas toutes leurs notes."""
|
||||||
if is_apc:
|
if is_apc:
|
||||||
eval_info += """ <span>La moyenne indicative est la moyenne des moyennes d'UE, et n'est pas utilisée en BUT.
|
eval_info += """ <span>La moyenne indicative est la moyenne des moyennes d'UE,
|
||||||
Les moyennes sur le groupe sont estimées sans les absents (sauf pour les moyennes des moyennes d'UE) ni les démissionnaires.</span>"""
|
et n'est pas utilisée en BUT.
|
||||||
|
Les moyennes sur le groupe sont estimées sans les absents
|
||||||
|
(sauf pour les moyennes des moyennes d'UE) ni les démissionnaires.</span>"""
|
||||||
eval_info += """</span>"""
|
eval_info += """</span>"""
|
||||||
return html_form + eval_info + t + "<p></p>"
|
return html_form + eval_info + t + "<p></p>"
|
||||||
else:
|
# Une seule evaluation: ajoute histogramme
|
||||||
# Une seule evaluation: ajoute histogramme
|
histo = histogram_notes(notes)
|
||||||
histo = histogram_notes(notes)
|
# 2 colonnes: histo, comments
|
||||||
# 2 colonnes: histo, comments
|
C = [
|
||||||
C = [
|
f"""<br><a class="stdlink" href="{base_url}&fmt=bordereau">Bordereau de Signatures (version PDF)</a>
|
||||||
f'<br><a class="stdlink" href="{base_url}&fmt=bordereau">Bordereau de Signatures (version PDF)</a>',
|
<table>
|
||||||
"<table><tr><td><div><h4>Répartition des notes:</h4>"
|
<tr><td>
|
||||||
+ histo
|
<div><h4>Répartition des notes:</h4>
|
||||||
+ "</div></td>\n",
|
{histo}
|
||||||
'<td style="padding-left: 50px; vertical-align: top;"><p>',
|
</div>
|
||||||
]
|
</td>
|
||||||
commentkeys = list(key_mgr.items()) # [ (comment, key), ... ]
|
<td style="padding-left: 50px; vertical-align: top;"><p>
|
||||||
commentkeys.sort(key=lambda x: int(x[1]))
|
"""
|
||||||
for comment, key in commentkeys:
|
]
|
||||||
C.append(
|
commentkeys = list(key_mgr.items()) # [ (comment, key), ... ]
|
||||||
'<span class="colcomment">(%s)</span> <em>%s</em><br>' % (key, comment)
|
commentkeys.sort(key=lambda x: int(x[1]))
|
||||||
)
|
for comment, key in commentkeys:
|
||||||
if commentkeys:
|
C.append(f"""<span class="colcomment">({key})</span> <em>{comment}</em><br>""")
|
||||||
C.append(
|
if commentkeys:
|
||||||
'<span><a class=stdlink" href="evaluation_list_operations?evaluation_id=%s">Gérer les opérations</a></span><br>'
|
C.append(
|
||||||
% E["evaluation_id"]
|
f"""<span><a class=stdlink" href="{ url_for(
|
||||||
)
|
'notes.evaluation_list_operations', scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id )
|
||||||
eval_info = "xxx"
|
}">Gérer les opérations</a></span><br>
|
||||||
if E["eval_state"]["evalcomplete"]:
|
"""
|
||||||
eval_info = '<span class="eval_info eval_complete">Evaluation prise en compte dans les moyennes</span>'
|
|
||||||
elif E["eval_state"]["evalattente"]:
|
|
||||||
eval_info = '<span class="eval_info eval_attente">Il y a des notes en attente (les autres sont prises en compte)</span>'
|
|
||||||
else:
|
|
||||||
eval_info = '<span class="eval_info eval_incomplete">Notes incomplètes, évaluation non prise en compte dans les moyennes</span>'
|
|
||||||
|
|
||||||
return (
|
|
||||||
sco_evaluations.evaluation_describe(evaluation_id=E["evaluation_id"])
|
|
||||||
+ eval_info
|
|
||||||
+ html_form
|
|
||||||
+ t
|
|
||||||
+ "\n".join(C)
|
|
||||||
)
|
)
|
||||||
|
eval_info = "xxx"
|
||||||
|
if evals_state[evaluation.id]["evalcomplete"]:
|
||||||
|
eval_info = '<span class="eval_info eval_complete">Evaluation prise en compte dans les moyennes</span>'
|
||||||
|
elif evals_state[evaluation.id]["evalattente"]:
|
||||||
|
eval_info = '<span class="eval_info eval_attente">Il y a des notes en attente (les autres sont prises en compte)</span>'
|
||||||
|
else:
|
||||||
|
eval_info = '<span class="eval_info eval_incomplete">Notes incomplètes, évaluation non prise en compte dans les moyennes</span>'
|
||||||
|
|
||||||
|
return (
|
||||||
|
sco_evaluations.evaluation_describe(evaluation_id=evaluation.id)
|
||||||
|
+ eval_info
|
||||||
|
+ html_form
|
||||||
|
+ t
|
||||||
|
+ "\n".join(C)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _add_eval_columns(
|
def _add_eval_columns(
|
||||||
e,
|
evaluation: Evaluation,
|
||||||
|
eval_state,
|
||||||
evals_poids,
|
evals_poids,
|
||||||
ues,
|
ues,
|
||||||
rows,
|
rows,
|
||||||
@ -678,24 +703,24 @@ def _add_eval_columns(
|
|||||||
nb_att = 0
|
nb_att = 0
|
||||||
sum_notes = 0
|
sum_notes = 0
|
||||||
notes = [] # liste des notes numeriques, pour calcul histogramme uniquement
|
notes = [] # liste des notes numeriques, pour calcul histogramme uniquement
|
||||||
evaluation_id = e["evaluation_id"]
|
inscrits = evaluation.moduleimpl.formsemestre.etudids_actifs # set d'etudids
|
||||||
e_o = db.session.get(Evaluation, evaluation_id) # XXX en attendant ré-écriture
|
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation.id)
|
||||||
inscrits = e_o.moduleimpl.formsemestre.etudids_actifs # set d'etudids
|
|
||||||
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
|
|
||||||
|
|
||||||
if len(e["jour"]) > 0:
|
if evaluation.date_debut:
|
||||||
titles[evaluation_id] = "%(description)s (%(jour)s)" % e
|
titles[
|
||||||
|
evaluation.id
|
||||||
|
] = f"{evaluation.description} ({evaluation.date_debut.strftime('%d/%m/%Y')})"
|
||||||
else:
|
else:
|
||||||
titles[evaluation_id] = "%(description)s " % e
|
titles[evaluation.id] = f"{evaluation.description} "
|
||||||
|
|
||||||
if e["eval_state"]["evalcomplete"]:
|
if eval_state["evalcomplete"]:
|
||||||
klass = "eval_complete"
|
klass = "eval_complete"
|
||||||
elif e["eval_state"]["evalattente"]:
|
elif eval_state["evalattente"]:
|
||||||
klass = "eval_attente"
|
klass = "eval_attente"
|
||||||
else:
|
else:
|
||||||
klass = "eval_incomplete"
|
klass = "eval_incomplete"
|
||||||
titles[evaluation_id] += " (non prise en compte)"
|
titles[evaluation.id] += " (non prise en compte)"
|
||||||
titles[f"_{evaluation_id}_td_attrs"] = f'class="{klass}"'
|
titles[f"_{evaluation.id}_td_attrs"] = f'class="{klass}"'
|
||||||
|
|
||||||
for row in rows:
|
for row in rows:
|
||||||
etudid = row["etudid"]
|
etudid = row["etudid"]
|
||||||
@ -712,8 +737,8 @@ def _add_eval_columns(
|
|||||||
and val != scu.NOTES_NEUTRALISE
|
and val != scu.NOTES_NEUTRALISE
|
||||||
and val != scu.NOTES_ATTENTE
|
and val != scu.NOTES_ATTENTE
|
||||||
):
|
):
|
||||||
if e["note_max"] > 0:
|
if evaluation.note_max > 0:
|
||||||
valsur20 = val * 20.0 / e["note_max"] # remet sur 20
|
valsur20 = val * 20.0 / evaluation.note_max # remet sur 20
|
||||||
else:
|
else:
|
||||||
valsur20 = 0
|
valsur20 = 0
|
||||||
notes.append(valsur20) # toujours sur 20 pour l'histogramme
|
notes.append(valsur20) # toujours sur 20 pour l'histogramme
|
||||||
@ -731,7 +756,7 @@ def _add_eval_columns(
|
|||||||
comment,
|
comment,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if (etudid in inscrits) and e["publish_incomplete"]:
|
if (etudid in inscrits) and evaluation.publish_incomplete:
|
||||||
# Note manquante mais prise en compte immédiate: affiche ATT
|
# Note manquante mais prise en compte immédiate: affiche ATT
|
||||||
val = scu.NOTES_ATTENTE
|
val = scu.NOTES_ATTENTE
|
||||||
val_fmt = "ATT"
|
val_fmt = "ATT"
|
||||||
@ -746,11 +771,11 @@ def _add_eval_columns(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if val is None:
|
if val is None:
|
||||||
row[f"_{evaluation_id}_td_attrs"] = f'class="etudabs {cell_class}" '
|
row[f"_{evaluation.id}_td_attrs"] = f'class="etudabs {cell_class}" '
|
||||||
if not row.get("_css_row_class", ""):
|
if not row.get("_css_row_class", ""):
|
||||||
row["_css_row_class"] = "etudabs"
|
row["_css_row_class"] = "etudabs"
|
||||||
else:
|
else:
|
||||||
row[f"_{evaluation_id}_td_attrs"] = f'class="{cell_class}" '
|
row[f"_{evaluation.id}_td_attrs"] = f'class="{cell_class}" '
|
||||||
# regroupe les commentaires
|
# regroupe les commentaires
|
||||||
if explanation:
|
if explanation:
|
||||||
if explanation in K:
|
if explanation in K:
|
||||||
@ -763,8 +788,8 @@ def _add_eval_columns(
|
|||||||
|
|
||||||
row.update(
|
row.update(
|
||||||
{
|
{
|
||||||
evaluation_id: val_fmt,
|
evaluation.id: val_fmt,
|
||||||
"_" + str(evaluation_id) + "_help": explanation,
|
"_" + str(evaluation.id) + "_help": explanation,
|
||||||
# si plusieurs evals seront ecrasés et non affichés:
|
# si plusieurs evals seront ecrasés et non affichés:
|
||||||
"comment": explanation,
|
"comment": explanation,
|
||||||
"expl_key": expl_key,
|
"expl_key": expl_key,
|
||||||
@ -772,36 +797,38 @@ def _add_eval_columns(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
row_coefs[evaluation_id] = "coef. %s" % e["coefficient"]
|
row_coefs[evaluation.id] = f"coef. {evaluation.coefficient:g}"
|
||||||
if is_apc:
|
if is_apc:
|
||||||
if fmt == "html":
|
if fmt == "html":
|
||||||
row_poids[evaluation_id] = _mini_table_eval_ue_poids(
|
row_poids[evaluation.id] = _mini_table_eval_ue_poids(
|
||||||
evaluation_id, evals_poids, ues
|
evaluation.id, evals_poids, ues
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
row_poids[evaluation_id] = e_o.get_ue_poids_str()
|
row_poids[evaluation.id] = evaluation.get_ue_poids_str()
|
||||||
if note_sur_20:
|
if note_sur_20:
|
||||||
nmax = 20.0
|
nmax = 20.0
|
||||||
else:
|
else:
|
||||||
nmax = e["note_max"]
|
nmax = evaluation.note_max
|
||||||
if keep_numeric:
|
if keep_numeric:
|
||||||
row_note_max[evaluation_id] = nmax
|
row_note_max[evaluation.id] = nmax
|
||||||
else:
|
else:
|
||||||
row_note_max[evaluation_id] = "/ %s" % nmax
|
row_note_max[evaluation.id] = f"/ {nmax}"
|
||||||
|
|
||||||
if nb_notes > 0:
|
if nb_notes > 0:
|
||||||
row_moys[evaluation_id] = scu.fmt_note(
|
row_moys[evaluation.id] = scu.fmt_note(
|
||||||
sum_notes / nb_notes, keep_numeric=keep_numeric
|
sum_notes / nb_notes, keep_numeric=keep_numeric
|
||||||
)
|
)
|
||||||
row_moys[
|
row_moys[
|
||||||
"_" + str(evaluation_id) + "_help"
|
"_" + str(evaluation.id) + "_help"
|
||||||
] = "moyenne sur %d notes (%s le %s)" % (
|
] = "moyenne sur %d notes (%s le %s)" % (
|
||||||
nb_notes,
|
nb_notes,
|
||||||
e["description"],
|
evaluation.description,
|
||||||
e["jour"],
|
evaluation.date_debut.strftime("%d/%m/%Y")
|
||||||
|
if evaluation.date_debut
|
||||||
|
else "",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
row_moys[evaluation_id] = ""
|
row_moys[evaluation.id] = ""
|
||||||
|
|
||||||
return notes, nb_abs, nb_att # pour histogramme
|
return notes, nb_abs, nb_att # pour histogramme
|
||||||
|
|
||||||
|
@ -50,7 +50,6 @@ table.dataTable thead .sorting_desc,
|
|||||||
table.dataTable thead .sorting_asc_disabled,
|
table.dataTable thead .sorting_asc_disabled,
|
||||||
table.dataTable thead .sorting_desc_disabled {
|
table.dataTable thead .sorting_desc_disabled {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
*cursor: hand;
|
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center right;
|
background-position: center right;
|
||||||
}
|
}
|
||||||
@ -83,9 +82,9 @@ table.dataTable tbody tr.selected {
|
|||||||
background-color: #b0bed9;
|
background-color: #b0bed9;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.dataTable tbody th,
|
table.dataTable.gt_table tbody th,
|
||||||
table.dataTable tbody td {
|
table.dataTable.gt_table tbody td {
|
||||||
padding: 8px 10px;
|
padding: 2px 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.dataTable.row-border tbody th,
|
table.dataTable.row-border tbody th,
|
||||||
@ -138,6 +137,10 @@ table.dataTable.display tbody tr:hover.selected {
|
|||||||
background-color: #a9b7d1;
|
background-color: #a9b7d1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.dataTable.with-highlight tr:hover td {
|
||||||
|
background-color: rgba(255, 255, 0, 0.415);
|
||||||
|
}
|
||||||
|
|
||||||
table.dataTable.order-column tbody tr > .sorting_1,
|
table.dataTable.order-column tbody tr > .sorting_1,
|
||||||
table.dataTable.order-column tbody tr > .sorting_2,
|
table.dataTable.order-column tbody tr > .sorting_2,
|
||||||
table.dataTable.order-column tbody tr > .sorting_3,
|
table.dataTable.order-column tbody tr > .sorting_3,
|
||||||
@ -368,7 +371,6 @@ table.dataTable td {
|
|||||||
.dataTables_wrapper {
|
.dataTables_wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
clear: both;
|
clear: both;
|
||||||
*zoom: 1;
|
|
||||||
zoom: 1;
|
zoom: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +410,6 @@ table.dataTable td {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
text-decoration: none !important;
|
text-decoration: none !important;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
*cursor: hand;
|
|
||||||
color: #333333 !important;
|
color: #333333 !important;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
@ -760,4 +761,3 @@ table.dataTable.gt_table.gt_left td,
|
|||||||
table.dataTable.gt_table.gt_left th {
|
table.dataTable.gt_table.gt_left th {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
scodoc;css
|
|
||||||
|
@ -1139,8 +1139,13 @@ a.redlink:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a.discretelink,
|
a.discretelink,
|
||||||
a:discretelink:visited {
|
a.discretelink:visited {
|
||||||
color: black;
|
color: black;
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-style: dotted;
|
||||||
|
}
|
||||||
|
table.gt_table a.discretelink,
|
||||||
|
table.gt_table a.discretelink:visited {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ class TableEtud(tb.Table):
|
|||||||
):
|
):
|
||||||
etuds = etuds or []
|
etuds = etuds or []
|
||||||
self.rows: list["RowEtud"] = [] # juste pour que VSCode nous aide sur .rows
|
self.rows: list["RowEtud"] = [] # juste pour que VSCode nous aide sur .rows
|
||||||
classes = classes or ["gt_table", "gt_left"]
|
classes = classes or ["gt_table", "gt_left", "with-highlight"]
|
||||||
super().__init__(
|
super().__init__(
|
||||||
row_class=row_class or RowEtud,
|
row_class=row_class or RowEtud,
|
||||||
classes=classes,
|
classes=classes,
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div class="pageContent">
|
<div class="pageContent">
|
||||||
{{minitimeline | safe }}
|
{{minitimeline | safe }}
|
||||||
<h2>Assiduité de {{sco.etud.nomprenom}}</h2>
|
<h2>Assiduité de {{sco.etud.html_link_fiche()|safe}}</h2>
|
||||||
|
|
||||||
<div class="options">
|
<div class="options">
|
||||||
<input type="checkbox" id="show_pres" name="show_pres" class="memo"><label for="show_pres">afficher les présences</label>
|
<input type="checkbox" id="show_pres" name="show_pres" class="memo"><label for="show_pres">afficher les présences</label>
|
||||||
|
@ -1641,19 +1641,17 @@ def signal_assiduites_diff():
|
|||||||
).build()
|
).build()
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/SignalEvaluationAbs/<int:evaluation_id>/<int:etudid>")
|
@bp.route("/signale_evaluation_abs/<int:evaluation_id>/<int:etudid>")
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.AbsChange)
|
||||||
def signal_evaluation_abs(etudid: int = None, evaluation_id: int = None):
|
def signale_evaluation_abs(etudid: int = None, evaluation_id: int = None):
|
||||||
"""
|
"""
|
||||||
Signale l'absence d'un étudiant à une évaluation
|
Signale l'absence d'un étudiant à une évaluation
|
||||||
Si la durée de l'évaluation est inférieur à 1 jour
|
Si la durée de l'évaluation est inférieure à 1 jour
|
||||||
Alors l'absence sera sur la période de l'évaluation
|
l'absence sera sur la période de l'évaluation
|
||||||
Sinon L'utilisateur sera redirigé vers la page de saisie des absences de l'étudiant
|
sinon l'utilisateur sera redirigé vers la page de saisie des absences de l'étudiant
|
||||||
"""
|
"""
|
||||||
etud = Identite.get_etud(etudid)
|
etud = Identite.get_etud(etudid)
|
||||||
|
|
||||||
# Récupération de l'évaluation concernée
|
|
||||||
evaluation: Evaluation = Evaluation.query.get_or_404(evaluation_id)
|
evaluation: Evaluation = Evaluation.query.get_or_404(evaluation_id)
|
||||||
|
|
||||||
delta: datetime.timedelta = evaluation.date_fin - evaluation.date_debut
|
delta: datetime.timedelta = evaluation.date_fin - evaluation.date_debut
|
||||||
@ -1683,9 +1681,9 @@ def signal_evaluation_abs(etudid: int = None, evaluation_id: int = None):
|
|||||||
etat=scu.EtatAssiduite.ABSENT,
|
etat=scu.EtatAssiduite.ABSENT,
|
||||||
moduleimpl=evaluation.moduleimpl,
|
moduleimpl=evaluation.moduleimpl,
|
||||||
)
|
)
|
||||||
except ScoValueError as see:
|
except ScoValueError as exc:
|
||||||
# En cas d'erreur
|
# En cas d'erreur
|
||||||
msg: str = see.args[0]
|
msg: str = exc.args[0]
|
||||||
if "Duplication" in msg:
|
if "Duplication" in msg:
|
||||||
msg = """Une autre saisie concerne déjà cette période.
|
msg = """Une autre saisie concerne déjà cette période.
|
||||||
En cliquant sur continuer vous serez redirigé vers la page de
|
En cliquant sur continuer vous serez redirigé vers la page de
|
||||||
@ -1703,12 +1701,12 @@ def signal_evaluation_abs(etudid: int = None, evaluation_id: int = None):
|
|||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
duplication="oui",
|
duplication="oui",
|
||||||
)
|
)
|
||||||
raise ScoValueError(msg, dest) from see
|
raise ScoValueError(msg, dest) from exc
|
||||||
|
|
||||||
db.session.add(assiduite_unique)
|
db.session.add(assiduite_unique)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
# on flash pour indiquer que l'absence a bien été créée puis on revient sur la page de l'évaluation
|
# on flash puis on revient sur la page de l'évaluation
|
||||||
flash("L'absence a bien été créée")
|
flash("L'absence a bien été créée")
|
||||||
# rediriger vers la page d'évaluation
|
# rediriger vers la page d'évaluation
|
||||||
return redirect(
|
return redirect(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""Tests unitaires : bulletins de notes
|
"""Tests unitaires : bulletins de notes
|
||||||
|
|
||||||
Utiliser comme:
|
Utiliser comme:
|
||||||
pytest tests/unit/test_sco_basic.py
|
pytest tests/unit/test_sco_basic.py
|
||||||
|
|
||||||
Au besoin, créer un base de test neuve:
|
Au besoin, créer un base de test neuve:
|
||||||
@ -69,8 +69,8 @@ def test_bulletin_data_classic(test_client):
|
|||||||
min_eval_1 = float(note_eval_1["min"])
|
min_eval_1 = float(note_eval_1["min"])
|
||||||
max_eval_1 = float(note_eval_1["max"])
|
max_eval_1 = float(note_eval_1["max"])
|
||||||
# la valeur actuelle est 12.34, on s'assure qu'elle n'est pas extrême:
|
# la valeur actuelle est 12.34, on s'assure qu'elle n'est pas extrême:
|
||||||
assert min_eval_1 > 0
|
assert min_eval_1 > 0 # 12.34
|
||||||
assert max_eval_1 < 20
|
assert max_eval_1 < 20 # 12.34
|
||||||
|
|
||||||
# Saisie note pour changer min/max:
|
# Saisie note pour changer min/max:
|
||||||
# Met le max à 20:
|
# Met le max à 20:
|
||||||
|
Loading…
Reference in New Issue
Block a user