Nettoyage code + exception save note

This commit is contained in:
Emmanuel Viennet 2023-05-29 16:04:41 +02:00 committed by iziram
parent e9113d0932
commit af1e46ba82
6 changed files with 135 additions and 140 deletions

View File

@ -306,12 +306,12 @@ class ResultatsSemestreBUT(NotesTableCompat):
return ues_ids return ues_ids
def etud_has_decision(self, etudid): def etud_has_decision(self, etudid) -> bool:
"""True s'il y a une décision de jury pour cet étudiant émanant de ce formsemestre. """True s'il y a une décision de jury pour cet étudiant émanant de ce formsemestre.
prend aussi en compte les autorisations de passage. prend aussi en compte les autorisations de passage.
Sous-classée en BUT pour les RCUEs et années. Sous-classée en BUT pour les RCUEs et années.
""" """
return ( return bool(
super().etud_has_decision(etudid) super().etud_has_decision(etudid)
or ApcValidationAnnee.query.filter_by( or ApcValidationAnnee.query.filter_by(
formsemestre_id=self.formsemestre.id, etudid=etudid formsemestre_id=self.formsemestre.id, etudid=etudid

View File

@ -283,12 +283,12 @@ class NotesTableCompat(ResultatsSemestre):
] ]
return etudids return etudids
def etud_has_decision(self, etudid): def etud_has_decision(self, etudid) -> bool:
"""True s'il y a une décision de jury pour cet étudiant émanant de ce formsemestre. """True s'il y a une décision de jury pour cet étudiant émanant de ce formsemestre.
prend aussi en compte les autorisations de passage. prend aussi en compte les autorisations de passage.
Sous-classée en BUT pour les RCUEs et années. Sous-classée en BUT pour les RCUEs et années.
""" """
return ( return bool(
self.get_etud_decisions_ue(etudid) self.get_etud_decisions_ue(etudid)
or self.get_etud_decision_sem(etudid) or self.get_etud_decision_sem(etudid)
or ScolarAutorisationInscription.query.filter_by( or ScolarAutorisationInscription.query.filter_by(

View File

@ -145,6 +145,18 @@ class Evaluation(db.Model):
db.session.add(copy) db.session.add(copy)
return copy return copy
def is_matin(self) -> bool:
"Evaluation ayant lieu le matin (faux si pas de date)"
heure_debut_dt = self.heure_debut or datetime.time(8, 00)
# 8:00 au cas ou pas d'heure (note externe?)
return bool(self.jour) and heure_debut_dt < datetime.time(12, 00)
def is_apresmidi(self) -> bool:
"Evaluation ayant lieu l'après midi (faux si pas de date)"
heure_debut_dt = self.heure_debut or datetime.time(8, 00)
# 8:00 au cas ou pas d'heure (note externe?)
return bool(self.jour) and heure_debut_dt >= datetime.time(12, 00)
def set_default_poids(self) -> bool: def set_default_poids(self) -> bool:
"""Initialize les poids bvers les UE à leurs valeurs par défaut """Initialize les poids bvers les UE à leurs valeurs par défaut
C'est à dire à 1 si le coef. module/UE est non nul, 0 sinon. C'est à dire à 1 si le coef. module/UE est non nul, 0 sinon.

View File

@ -255,9 +255,8 @@ def do_evaluation_get_all_notes(
"""Toutes les notes pour une evaluation: { etudid : { 'value' : value, 'date' : date ... }} """Toutes les notes pour une evaluation: { etudid : { 'value' : value, 'date' : date ... }}
Attention: inclut aussi les notes des étudiants qui ne sont plus inscrits au module. Attention: inclut aussi les notes des étudiants qui ne sont plus inscrits au module.
""" """
do_cache = ( # pas de cache pour (rares) appels via undo_notes ou specifiant un enseignant
filter_suppressed and table == "notes_notes" and (by_uid is None) do_cache = filter_suppressed and table == "notes_notes" and (by_uid is None)
) # pas de cache pour (rares) appels via undo_notes ou specifiant un enseignant
if do_cache: if do_cache:
r = sco_cache.EvaluationCache.get(evaluation_id) r = sco_cache.EvaluationCache.get(evaluation_id)
if r is not None: if r is not None:

View File

@ -433,7 +433,7 @@ def excel_simple_table(
return ws.generate() return ws.generate()
def excel_feuille_saisie(e, titreannee, description, lines): def excel_feuille_saisie(evaluation: "Evaluation", titreannee, description, lines):
"""Genere feuille excel pour saisie des notes. """Genere feuille excel pour saisie des notes.
E: evaluation (dict) E: evaluation (dict)
lines: liste de tuples lines: liste de tuples
@ -512,18 +512,20 @@ def excel_feuille_saisie(e, titreannee, description, lines):
# description evaluation # description evaluation
ws.append_single_cell_row(scu.unescape_html(description), style_titres) ws.append_single_cell_row(scu.unescape_html(description), style_titres)
ws.append_single_cell_row( ws.append_single_cell_row(
"Evaluation du %s (coef. %g)" % (e["jour"], e["coefficient"]), style "Evaluation du %s (coef. %g)"
% (evaluation.jour or "sans date", evaluation.coefficient or 0.0),
style,
) )
# ligne blanche # ligne blanche
ws.append_blank_row() ws.append_blank_row()
# code et titres colonnes # code et titres colonnes
ws.append_row( ws.append_row(
[ [
ws.make_cell("!%s" % e["evaluation_id"], style_ro), ws.make_cell("!%s" % evaluation.id, style_ro),
ws.make_cell("Nom", style_titres), ws.make_cell("Nom", style_titres),
ws.make_cell("Prénom", style_titres), ws.make_cell("Prénom", style_titres),
ws.make_cell("Groupe", style_titres), ws.make_cell("Groupe", style_titres),
ws.make_cell("Note sur %g" % e["note_max"], style_titres), ws.make_cell("Note sur %g" % (evaluation.note_max or 0.0), style_titres),
ws.make_cell("Remarque", style_titres), ws.make_cell("Remarque", style_titres),
] ]
) )

View File

@ -40,7 +40,7 @@ from app.auth.models import User
from app.comp import res_sem from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat from app.comp.res_compat import NotesTableCompat
from app.models import Evaluation, FormSemestre from app.models import Evaluation, FormSemestre
from app.models import ModuleImpl, ScolarNews from app.models import ModuleImpl, NotesNotes, ScolarNews
from app.models.etudiants import Identite from app.models.etudiants import Identite
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import ModuleType from app.scodoc.sco_utils import ModuleType
@ -50,7 +50,8 @@ from app.scodoc.sco_exceptions import (
AccessDenied, AccessDenied,
InvalidNoteValue, InvalidNoteValue,
NoteProcessError, NoteProcessError,
ScoGenError, ScoBugCatcher,
ScoException,
ScoInvalidParamError, ScoInvalidParamError,
ScoValueError, ScoValueError,
) )
@ -63,7 +64,6 @@ from app.scodoc import sco_edit_module
from app.scodoc import sco_evaluations 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_excel from app.scodoc import sco_excel
from app.scodoc import sco_formsemestre
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_groups_view from app.scodoc import sco_groups_view
@ -526,9 +526,8 @@ def notes_add(
- si la note existe deja avec valeur distincte, ajoute une entree au log (notes_notes_log) - si la note existe deja avec valeur distincte, ajoute une entree au log (notes_notes_log)
Return tuple (nb_changed, nb_suppress, existing_decisions) Return tuple (nb_changed, nb_suppress, existing_decisions)
""" """
now = psycopg2.Timestamp( now = psycopg2.Timestamp(*time.localtime()[:6])
*time.localtime()[:6]
) # datetime.datetime.now().isoformat()
# Verifie inscription et valeur note # Verifie inscription et valeur note
inscrits = { inscrits = {
x[0] x[0]
@ -550,11 +549,11 @@ def notes_add(
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
nb_changed = 0 nb_changed = 0
nb_suppress = 0 nb_suppress = 0
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0] evaluation: Evaluation = Evaluation.query.get_or_404(evaluation_id)
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] formsemestre: FormSemestre = evaluation.moduleimpl.formsemestre
existing_decisions = ( res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
[] # etudids pour lesquels il y a une decision de jury et que la note change:
) # etudids pour lesquels il y a une decision de jury et que la note change etudids_with_existing_decision = []
try: try:
for etudid, value in notes: for etudid, value in notes:
changed = False changed = False
@ -571,20 +570,29 @@ def notes_add(
"date": now, "date": now,
} }
ndb.quote_dict(aa) ndb.quote_dict(aa)
cursor.execute( try:
"""INSERT INTO notes_notes cursor.execute(
(etudid, evaluation_id, value, comment, date, uid) """INSERT INTO notes_notes
VALUES (%(etudid)s,%(evaluation_id)s,%(value)s,%(comment)s,%(date)s,%(uid)s) (etudid, evaluation_id, value, comment, date, uid)
""", VALUES (%(etudid)s,%(evaluation_id)s,%(value)s,%(comment)s,%(date)s,%(uid)s)
aa, """,
) aa,
)
except psycopg2.errors.UniqueViolation as exc:
# XXX ne devrait pas arriver mais bug possible ici (non reproductible)
existing_note = NotesNotes.query.filter_by(
evaluation_id=evaluation_id, etudid=etudid
).first()
raise ScoBugCatcher(
f"dup: existing={existing_note}"
) from exc
changed = True changed = True
else: else:
# il y a deja une note # il y a deja une note
oldval = notes_db[etudid]["value"] oldval = notes_db[etudid]["value"]
if type(value) != type(oldval): if type(value) != type(oldval):
changed = True changed = True
elif type(value) == float and ( elif isinstance(value, float) and (
abs(value - oldval) > scu.NOTES_PRECISION abs(value - oldval) > scu.NOTES_PRECISION
): ):
changed = True changed = True
@ -646,26 +654,21 @@ def notes_add(
nb_suppress += 1 nb_suppress += 1
if changed: if changed:
nb_changed += 1 nb_changed += 1
if has_existing_decision(M, E, etudid): if res.etud_has_decision(etudid):
existing_decisions.append(etudid) etudids_with_existing_decision.append(etudid)
except Exception as exc: except Exception as exc:
log("*** exception in notes_add") log("*** exception in notes_add")
if do_it: if do_it:
cnx.rollback() # abort cnx.rollback() # abort
# inval cache # inval cache
sco_cache.invalidate_formsemestre( sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
formsemestre_id=M["formsemestre_id"]
) # > modif notes (exception)
sco_cache.EvaluationCache.delete(evaluation_id) sco_cache.EvaluationCache.delete(evaluation_id)
raise # XXX raise ScoException from exc
raise ScoGenError("Erreur enregistrement note: merci de ré-essayer") from exc
if do_it: if do_it:
cnx.commit() cnx.commit()
sco_cache.invalidate_formsemestre( sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
formsemestre_id=M["formsemestre_id"]
) # > modif notes
sco_cache.EvaluationCache.delete(evaluation_id) sco_cache.EvaluationCache.delete(evaluation_id)
return nb_changed, nb_suppress, existing_decisions return nb_changed, nb_suppress, etudids_with_existing_decision
def saisie_notes_tableur(evaluation_id, group_ids=()): def saisie_notes_tableur(evaluation_id, group_ids=()):
@ -868,35 +871,35 @@ def saisie_notes_tableur(evaluation_id, group_ids=()):
def feuille_saisie_notes(evaluation_id, group_ids=[]): def feuille_saisie_notes(evaluation_id, group_ids=[]):
"""Document Excel pour saisie notes dans l'évaluation et les groupes indiqués""" """Document Excel pour saisie notes dans l'évaluation et les groupes indiqués"""
evals = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id}) evaluation: Evaluation = Evaluation.query.get(evaluation_id)
if not evals: if not evaluation:
raise ScoValueError("invalid evaluation_id") raise ScoValueError("invalid evaluation_id")
eval_dict = evals[0] modimpl = evaluation.moduleimpl
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=eval_dict["moduleimpl_id"])[0] formsemestre = modimpl.formsemestre
formsemestre_id = M["formsemestre_id"] mod_responsable = sco_users.user_info(modimpl.responsable_id)
Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] if evaluation.jour:
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) indication_date = evaluation.jour.isoformat()
mod_responsable = sco_users.user_info(M["responsable_id"])
if eval_dict["jour"]:
indication_date = ndb.DateDMYtoISO(eval_dict["jour"])
else: else:
indication_date = scu.sanitize_filename(eval_dict["description"])[:12] indication_date = scu.sanitize_filename(evaluation.description or "")[:12]
eval_name = "%s-%s" % (Mod["code"], indication_date) eval_name = "%s-%s" % (evaluation.moduleimpl.module.code, indication_date)
if eval_dict["description"]: if evaluation.description:
evaltitre = "%s du %s" % (eval_dict["description"], eval_dict["jour"]) evaltitre = "%s du %s" % (
evaluation.description,
evaluation.jour.strftime("%d/%m/%Y"),
)
else: else:
evaltitre = "évaluation du %s" % eval_dict["jour"] evaltitre = "évaluation du %s" % evaluation.jour.strftime("%d/%m/%Y")
description = "%s en %s (%s) resp. %s" % ( description = "%s en %s (%s) resp. %s" % (
evaltitre, evaltitre,
Mod["abbrev"] or "", evaluation.moduleimpl.module.abbrev or "",
Mod["code"] or "", evaluation.moduleimpl.module.code,
mod_responsable["prenomnom"], mod_responsable["prenomnom"],
) )
groups_infos = sco_groups_view.DisplayedGroupsInfos( groups_infos = sco_groups_view.DisplayedGroupsInfos(
group_ids=group_ids, group_ids=group_ids,
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre.id,
select_all_when_unspecified=True, select_all_when_unspecified=True,
etat=None, etat=None,
) )
@ -919,15 +922,15 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]):
# une liste de liste de chaines: lignes de la feuille de calcul # une liste de liste de chaines: lignes de la feuille de calcul
L = [] L = []
etuds = _get_sorted_etuds(eval_dict, etudids, formsemestre_id) etuds = _get_sorted_etuds(evaluation, etudids, formsemestre.id)
for e in etuds: for e in etuds:
etudid = e["etudid"] etudid = e["etudid"]
groups = sco_groups.get_etud_groups(etudid, formsemestre_id) groups = sco_groups.get_etud_groups(etudid, formsemestre.id)
grc = sco_groups.listgroups_abbrev(groups) grc = sco_groups.listgroups_abbrev(groups)
L.append( L.append(
[ [
"%s" % etudid, str(etudid),
e["nom"].upper(), e["nom"].upper(),
e["prenom"].lower().capitalize(), e["prenom"].lower().capitalize(),
e["inscr"]["etat"], e["inscr"]["etat"],
@ -939,29 +942,9 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]):
filename = "notes_%s_%s" % (eval_name, gr_title_filename) filename = "notes_%s_%s" % (eval_name, gr_title_filename)
xls = sco_excel.excel_feuille_saisie( xls = sco_excel.excel_feuille_saisie(
eval_dict, sem["titreannee"], description, lines=L evaluation, formsemestre.titre_annee(), description, lines=L
) )
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE) return scu.send_file(xls, filename, scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE)
# return sco_excel.send_excel_file(xls, filename)
def has_existing_decision(M, E, etudid):
"""Verifie s'il y a une validation pour cet etudiant dans ce semestre ou UE
Si oui, return True
"""
formsemestre_id = M["formsemestre_id"]
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
if nt.get_etud_decision_sem(etudid):
return True
dec_ues = nt.get_etud_decisions_ue(etudid)
if dec_ues:
mod = sco_edit_module.module_list({"module_id": M["module_id"]})[0]
ue_id = mod["ue_id"]
if ue_id in dec_ues:
return True # decision pour l'UE a laquelle appartient cette evaluation
return False # pas de decision de jury affectee par cette note
# ----------------------------- # -----------------------------
@ -973,20 +956,18 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
if not isinstance(evaluation_id, int): if not isinstance(evaluation_id, int):
raise ScoInvalidParamError() raise ScoInvalidParamError()
group_ids = [int(group_id) for group_id in (group_ids or [])] group_ids = [int(group_id) for group_id in (group_ids or [])]
evals = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id}) evaluation: Evaluation = Evaluation.query.get(evaluation_id)
if not evals: if evaluation is None:
raise ScoValueError("évaluation inexistante") raise ScoValueError("évaluation inexistante")
E = evals[0] modimpl = evaluation.moduleimpl
M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[0]
formsemestre_id = M["formsemestre_id"]
moduleimpl_status_url = url_for( moduleimpl_status_url = url_for(
"notes.moduleimpl_status", "notes.moduleimpl_status",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
moduleimpl_id=E["moduleimpl_id"], moduleimpl_id=evaluation.moduleimpl_id,
) )
# Check access # Check access
# (admin, respformation, and responsable_id) # (admin, respformation, and responsable_id)
if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]): if not sco_permissions_check.can_edit_notes(current_user, evaluation.moduleimpl_id):
return f""" return f"""
{html_sco_header.sco_header()} {html_sco_header.sco_header()}
<h2>Modification des notes impossible pour {current_user.user_name}</h2> <h2>Modification des notes impossible pour {current_user.user_name}</h2>
@ -1001,16 +982,16 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
# Informations sur les groupes à afficher: # Informations sur les groupes à afficher:
groups_infos = sco_groups_view.DisplayedGroupsInfos( groups_infos = sco_groups_view.DisplayedGroupsInfos(
group_ids=group_ids, group_ids=group_ids,
formsemestre_id=formsemestre_id, formsemestre_id=modimpl.formsemestre_id,
select_all_when_unspecified=True, select_all_when_unspecified=True,
etat=None, etat=None,
) )
if E["description"]: page_title = (
page_title = 'Saisie "%s"' % E["description"] f'Saisie "{evaluation.description}"'
else: if evaluation.description
page_title = "Saisie des notes" else "Saisie des notes"
)
# HTML page: # HTML page:
H = [ H = [
html_sco_header.sco_header( html_sco_header.sco_header(
@ -1036,19 +1017,19 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
"id": "menu_saisie_tableur", "id": "menu_saisie_tableur",
"endpoint": "notes.saisie_notes_tableur", "endpoint": "notes.saisie_notes_tableur",
"args": { "args": {
"evaluation_id": E["evaluation_id"], "evaluation_id": evaluation.id,
"group_ids": groups_infos.group_ids, "group_ids": groups_infos.group_ids,
}, },
}, },
{ {
"title": "Voir toutes les notes du module", "title": "Voir toutes les notes du module",
"endpoint": "notes.evaluation_listenotes", "endpoint": "notes.evaluation_listenotes",
"args": {"moduleimpl_id": E["moduleimpl_id"]}, "args": {"moduleimpl_id": evaluation.moduleimpl_id},
}, },
{ {
"title": "Effacer toutes les notes de cette évaluation", "title": "Effacer toutes les notes de cette évaluation",
"endpoint": "notes.evaluation_suppress_alln", "endpoint": "notes.evaluation_suppress_alln",
"args": {"evaluation_id": E["evaluation_id"]}, "args": {"evaluation_id": evaluation.id},
}, },
], ],
alone=True, alone=True,
@ -1077,7 +1058,9 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
) )
# Le formulaire de saisie des notes: # Le formulaire de saisie des notes:
form = _form_saisie_notes(E, M, groups_infos, destination=moduleimpl_status_url) form = _form_saisie_notes(
evaluation, modimpl, groups_infos, destination=moduleimpl_status_url
)
if form is None: if form is None:
return flask.redirect(moduleimpl_status_url) return flask.redirect(moduleimpl_status_url)
H.append(form) H.append(form)
@ -1101,10 +1084,9 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
return "\n".join(H) return "\n".join(H)
def _get_sorted_etuds(eval_dict: dict, etudids: list, formsemestre_id: int): def _get_sorted_etuds(evaluation: Evaluation, etudids: list, formsemestre_id: int):
notes_db = sco_evaluation_db.do_evaluation_get_all_notes( # Notes existantes
eval_dict["evaluation_id"] notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation.id)
) # Notes existantes
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
etuds = [] etuds = []
for etudid in etudids: for etudid in etudids:
@ -1123,17 +1105,17 @@ def _get_sorted_etuds(eval_dict: dict, etudids: list, formsemestre_id: int):
e["groups"] = sco_groups.get_etud_groups(etudid, formsemestre_id) e["groups"] = sco_groups.get_etud_groups(etudid, formsemestre_id)
# Information sur absence (tenant compte de la demi-journée) # Information sur absence (tenant compte de la demi-journée)
jour_iso = ndb.DateDMYtoISO(eval_dict["jour"]) jour_iso = evaluation.jour.isoformat() if evaluation.jour else ""
warn_abs_lst = [] warn_abs_lst = []
if eval_dict["matin"]: if evaluation.is_matin():
nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=1) nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=True)
nbabsjust = sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=1) nbabsjust = sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=True)
if nbabs: if nbabs:
if nbabsjust: if nbabsjust:
warn_abs_lst.append("absent justifié le matin !") warn_abs_lst.append("absent justifié le matin !")
else: else:
warn_abs_lst.append("absent le matin !") warn_abs_lst.append("absent le matin !")
if eval_dict["apresmidi"]: if evaluation.is_apresmidi():
nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=0) nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=0)
nbabsjust = sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=0) nbabsjust = sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=0)
if nbabs: if nbabs:
@ -1169,35 +1151,38 @@ def _get_sorted_etuds(eval_dict: dict, etudids: list, formsemestre_id: int):
return etuds return etuds
def _form_saisie_notes(E, M, groups_infos, destination=""): def _form_saisie_notes(
evaluation: Evaluation, modimpl: ModuleImpl, groups_infos, destination=""
):
"""Formulaire HTML saisie des notes dans l'évaluation E du moduleimpl M """Formulaire HTML saisie des notes dans l'évaluation E du moduleimpl M
pour les groupes indiqués. pour les groupes indiqués.
On charge tous les étudiants, ne seront montrés que ceux On charge tous les étudiants, ne seront montrés que ceux
des groupes sélectionnés grace a un filtre en javascript. des groupes sélectionnés grace a un filtre en javascript.
""" """
evaluation_id = E["evaluation_id"] formsemestre_id = modimpl.formsemestre_id
formsemestre_id = M["formsemestre_id"] formsemestre: FormSemestre = evaluation.moduleimpl.formsemestre
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
etudids = [ etudids = [
x[0] x[0]
for x in sco_groups.do_evaluation_listeetuds_groups( for x in sco_groups.do_evaluation_listeetuds_groups(
evaluation_id, getallstudents=True, include_demdef=True evaluation.id, getallstudents=True, include_demdef=True
) )
] ]
if not etudids: if not etudids:
return '<div class="ue_warning"><span>Aucun étudiant sélectionné !</span></div>' return '<div class="ue_warning"><span>Aucun étudiant sélectionné !</span></div>'
# Decisions de jury existantes ? # Décisions de jury existantes ?
decisions_jury = {etudid: has_existing_decision(M, E, etudid) for etudid in etudids} decisions_jury = {etudid: res.etud_has_decision(etudid) for etudid in etudids}
# Nb de decisions de jury (pour les inscrits à l'évaluation):
# Nb de décisions de jury (pour les inscrits à l'évaluation):
nb_decisions = sum(decisions_jury.values()) nb_decisions = sum(decisions_jury.values())
etuds = _get_sorted_etuds(E, etudids, formsemestre_id) etuds = _get_sorted_etuds(evaluation, etudids, formsemestre_id)
# Build form: # Build form:
descr = [ descr = [
("evaluation_id", {"default": evaluation_id, "input_type": "hidden"}), ("evaluation_id", {"default": evaluation.id, "input_type": "hidden"}),
("formsemestre_id", {"default": formsemestre_id, "input_type": "hidden"}), ("formsemestre_id", {"default": formsemestre_id, "input_type": "hidden"}),
( (
"group_ids", "group_ids",
@ -1207,7 +1192,7 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
("comment", {"size": 44, "title": "Commentaire", "return_focus_next": True}), ("comment", {"size": 44, "title": "Commentaire", "return_focus_next": True}),
("changed", {"default": "0", "input_type": "hidden"}), # changed in JS ("changed", {"default": "0", "input_type": "hidden"}), # changed in JS
] ]
if M["module"]["module_type"] in ( if modimpl.module.module_type in (
ModuleType.STANDARD, ModuleType.STANDARD,
ModuleType.RESSOURCE, ModuleType.RESSOURCE,
ModuleType.SAE, ModuleType.SAE,
@ -1220,11 +1205,11 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
"title": "Notes ", "title": "Notes ",
"cssclass": "formnote_bareme", "cssclass": "formnote_bareme",
"readonly": True, "readonly": True,
"default": "&nbsp;/ %g" % E["note_max"], "default": "&nbsp;/ %g" % evaluation.note_max,
}, },
) )
) )
elif M["module"]["module_type"] == ModuleType.MALUS: elif modimpl.module.module_type == ModuleType.MALUS:
descr.append( descr.append(
( (
"s3", "s3",
@ -1238,7 +1223,7 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
) )
) )
else: else:
raise ValueError("invalid module type (%s)" % M["module"]["module_type"]) # bug raise ValueError(f"invalid module type ({modimpl.module.module_type})") # bug
initvalues = {} initvalues = {}
for e in etuds: for e in etuds:
@ -1248,7 +1233,7 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
if disabled: if disabled:
classdem = " etud_dem" classdem = " etud_dem"
etud_classes.append("etud_dem") etud_classes.append("etud_dem")
disabled_attr = 'disabled="%d"' % disabled disabled_attr = f'disabled="{disabled}"'
else: else:
classdem = "" classdem = ""
disabled_attr = "" disabled_attr = ""
@ -1265,18 +1250,17 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
) )
# Historique des saisies de notes: # Historique des saisies de notes:
if not disabled: explanation = (
explanation = ( ""
'<span id="hist_%s">' % etudid if disabled
+ get_note_history_menu(evaluation_id, etudid) else f"""<span id="hist_{etudid}">{
+ "</span>" get_note_history_menu(evaluation.id, etudid)
) }</span>"""
else: )
explanation = ""
explanation = e["absinfo"] + explanation explanation = e["absinfo"] + explanation
# Lien modif decision de jury: # Lien modif decision de jury:
explanation += '<span id="jurylink_%s" class="jurylink"></span>' % etudid explanation += f'<span id="jurylink_{etudid}" class="jurylink"></span>'
# Valeur actuelle du champ: # Valeur actuelle du champ:
initvalues["note_" + str(etudid)] = e["val"] initvalues["note_" + str(etudid)] = e["val"]
@ -1330,7 +1314,7 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
H.append(tf.getform()) # check and init H.append(tf.getform()) # check and init
H.append( H.append(
f"""<a href="{url_for("notes.moduleimpl_status", scodoc_dept=g.scodoc_dept, f"""<a href="{url_for("notes.moduleimpl_status", scodoc_dept=g.scodoc_dept,
moduleimpl_id=M["moduleimpl_id"]) moduleimpl_id=modimpl.id)
}" class="btn btn-primary">Terminer</a> }" class="btn btn-primary">Terminer</a>
""" """
) )
@ -1345,7 +1329,7 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
Mettre les notes manquantes à Mettre les notes manquantes à
<input type="text" size="5" name="value"/> <input type="text" size="5" name="value"/>
<input type="submit" value="OK"/> <input type="submit" value="OK"/>
<input type="hidden" name="evaluation_id" value="{evaluation_id}"/> <input type="hidden" name="evaluation_id" value="{evaluation.id}"/>
<input class="group_ids_str" type="hidden" name="group_ids_str" value="{ <input class="group_ids_str" type="hidden" name="group_ids_str" value="{
",".join([str(x) for x in groups_infos.group_ids]) ",".join([str(x) for x in groups_infos.group_ids])
}"/> }"/>
@ -1364,10 +1348,8 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
def save_note(etudid=None, evaluation_id=None, value=None, comment=""): def save_note(etudid=None, evaluation_id=None, value=None, comment=""):
"""Enregistre une note (ajax)""" """Enregistre une note (ajax)"""
authuser = current_user
log( log(
"save_note: evaluation_id=%s etudid=%s uid=%s value=%s" f"save_note: evaluation_id={evaluation_id} etudid={etudid} uid={current_user} value={value}"
% (evaluation_id, etudid, authuser, value)
) )
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0] E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
@ -1380,13 +1362,13 @@ def save_note(etudid=None, evaluation_id=None, value=None, comment=""):
) )
result = {"nbchanged": 0} # JSON result = {"nbchanged": 0} # JSON
# Check access: admin, respformation, or responsable_id # Check access: admin, respformation, or responsable_id
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]): if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]):
result["status"] = "unauthorized" result["status"] = "unauthorized"
else: else:
L, _, _, _, _ = _check_notes([(etudid, value)], E, Mod) L, _, _, _, _ = _check_notes([(etudid, value)], E, Mod)
if L: if L:
nbchanged, _, existing_decisions = notes_add( nbchanged, _, existing_decisions = notes_add(
authuser, evaluation_id, L, comment=comment, do_it=True current_user, evaluation_id, L, comment=comment, do_it=True
) )
ScolarNews.add( ScolarNews.add(
typ=ScolarNews.NEWS_NOTE, typ=ScolarNews.NEWS_NOTE,