diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 39c657aa9..738a47442 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -973,7 +973,7 @@ class FormSemestre(models.ScoDocModel): def etudids_actifs(self) -> tuple[list[int], set[int]]: """Liste les etudids inscrits (incluant DEM et DEF), - qui ser al'index des dataframes de notes + qui sera l'index des dataframes de notes et donne l'ensemble des inscrits non DEM ni DEF. """ return [inscr.etudid for inscr in self.inscriptions], { diff --git a/app/scodoc/sco_import_users.py b/app/scodoc/sco_import_users.py index 8f0ac7700..314822a13 100644 --- a/app/scodoc/sco_import_users.py +++ b/app/scodoc/sco_import_users.py @@ -140,7 +140,7 @@ def read_users_excel_file(datafile, titles=TITLES) -> list[dict]: for line in data[1:]: d = {} for i, field in enumerate(xls_titles): - d[field] = line[i] + d[field] = (line[i] or "").strip() users.append(d) return users diff --git a/app/scodoc/sco_moduleimpl_status.py b/app/scodoc/sco_moduleimpl_status.py index abe854576..5897ea466 100644 --- a/app/scodoc/sco_moduleimpl_status.py +++ b/app/scodoc/sco_moduleimpl_status.py @@ -70,13 +70,21 @@ def moduleimpl_evaluation_menu(evaluation: Evaluation, nbnotes: int = 0) -> str: menu_eval = [ { - "title": "Saisir notes", + "title": "Saisir les notes", "endpoint": "notes.saisie_notes", "args": { "evaluation_id": evaluation_id, }, "enabled": can_edit_notes_ens, }, + { + "title": "Saisir par fichier tableur", + "id": "menu_saisie_tableur", + "endpoint": "notes.saisie_notes_tableur", + "args": { + "evaluation_id": evaluation.id, + }, + }, { "title": "Modifier évaluation", "endpoint": "notes.evaluation_edit", diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py index d8006e496..7df74fa36 100644 --- a/app/scodoc/sco_saisie_notes.py +++ b/app/scodoc/sco_saisie_notes.py @@ -29,12 +29,15 @@ Formulaire revu en juillet 2016 """ +import html import time -import psycopg2 + import flask from flask import g, url_for, request from flask_login import current_user +from flask_sqlalchemy.query import Query +import psycopg2 from app import db, log from app.auth.models import User @@ -75,8 +78,6 @@ import app.scodoc.sco_utils as scu from app.scodoc.sco_utils import json_error from app.scodoc.sco_utils import ModuleType -from flask_sqlalchemy.query import Query - def convert_note_from_string( note: str, @@ -115,7 +116,7 @@ def convert_note_from_string( return note_value, invalid -def _displayNote(val): +def _display_note(val): """Convert note from DB to viewable string. Utilisé seulement pour I/O vers formulaires (sans perte de precision) (Utiliser fmt_note pour les affichages) @@ -272,7 +273,7 @@ def do_evaluation_upload_xls(): diag.append("Notes invalides pour: " + ", ".join(etudsnames)) raise InvalidNoteValue() else: - etudids_changed, nb_suppress, etudids_with_decisions = notes_add( + etudids_changed, nb_suppress, etudids_with_decisions, messages = notes_add( current_user, evaluation_id, valid_notes, comment ) # news @@ -292,9 +293,19 @@ def do_evaluation_upload_xls(): max_frequency=30 * 60, # 30 minutes ) - msg = f"""

{len(etudids_changed)} notes changées ({len(withoutnotes)} sans notes, { - len(absents)} absents, {nb_suppress} note supprimées) + msg = f"""

+ {len(etudids_changed)} notes changées ({len(withoutnotes)} sans notes, + {len(absents)} absents, {nb_suppress} note supprimées)

""" + if messages: + msg += f"""
Attention : + +
""" if etudids_with_decisions: msg += """

Important: il y avait déjà des décisions de jury enregistrées, qui sont peut-être à revoir suite à cette modification !

@@ -322,7 +333,7 @@ def do_evaluation_set_etud_note(evaluation: Evaluation, etud: Identite, value) - # Convert and check value L, invalids, _, _, _ = _check_notes([(etud.id, value)], evaluation) if len(invalids) == 0: - etudids_changed, _, _ = notes_add( + etudids_changed, _, _, _ = notes_add( current_user, evaluation.id, L, "Initialisation notes" ) if len(etudids_changed) == 1: @@ -398,7 +409,9 @@ def do_evaluation_set_missing( ) # ok comment = "Initialisation notes manquantes" - etudids_changed, _, _ = notes_add(current_user, evaluation_id, valid_notes, comment) + etudids_changed, _, _, _ = notes_add( + current_user, evaluation_id, valid_notes, comment + ) # news url = url_for( "notes.moduleimpl_status", @@ -456,7 +469,7 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False): ) if not dialog_confirmed: - etudids_changed, nb_suppress, existing_decisions = notes_add( + etudids_changed, nb_suppress, existing_decisions, _ = notes_add( current_user, evaluation_id, notes, do_it=False, check_inscription=False ) msg = f"""

Confirmer la suppression des {nb_suppress} notes ? @@ -477,7 +490,7 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False): ) # modif - etudids_changed, nb_suppress, existing_decisions = notes_add( + etudids_changed, nb_suppress, existing_decisions, _ = notes_add( current_user, evaluation_id, notes, @@ -519,7 +532,7 @@ def notes_add( comment=None, do_it=True, check_inscription=True, -) -> tuple[list[int], int, list[int]]: +) -> tuple[list[int], int, list[int], list[str]]: """ Insert or update notes notes is a list of tuples (etudid,value) @@ -528,30 +541,48 @@ def notes_add( Nota: - si la note existe deja avec valeur distincte, ajoute une entree au log (notes_notes_log) - Return: tuple (etudids_changed, nb_suppress, etudids_with_decision) + Return: tuple (etudids_changed, nb_suppress, etudids_with_decision, messages) + + messages = list de messages d'avertissement/information pour l'utilisateur """ evaluation = Evaluation.get_evaluation(evaluation_id) now = psycopg2.Timestamp(*time.localtime()[:6]) - - # Vérifie inscription et valeur note - inscrits = { + messages = [] + # Vérifie inscription au module (même DEM/DEF) + etudids_inscrits_mod = { x[0] for x in sco_groups.do_evaluation_listeetuds_groups( evaluation_id, getallstudents=True, include_demdef=True ) } - # Les étudiants inscrits au semestre ni DEM ni DEF - _, etudids_actifs = evaluation.moduleimpl.formsemestre.etudids_actifs() + # Les étudiants inscrits au semestre et ceux "actifs" (ni DEM ni DEF) + etudids_inscrits_sem, etudids_actifs = ( + evaluation.moduleimpl.formsemestre.etudids_actifs() + ) for etudid, value in notes: - if check_inscription and ( - (etudid not in inscrits) or (etudid not in etudids_actifs) - ): - log(f"notes_add: {etudid} non inscrit ou DEM/DEF: aborting") - raise NoteProcessError(f"étudiant {etudid} non inscrit dans ce module") + + if check_inscription: + msg_err, msg_warn = "", "" + if etudid not in etudids_inscrits_sem: + msg_err = "non inscrit au semestre" + elif etudid not in etudids_inscrits_mod: + msg_err = "non inscrit au module" + elif etudid not in etudids_actifs: + # DEM ou DEF + msg_warn = "démissionnaire ou défaillant (note enregistrée)" + if msg_err or msg_warn: + etud = Identite.query.get(etudid) if isinstance(etudid, int) else None + msg = f"étudiant {etud.nomprenom if etud else etudid} {msg_err or msg_warn}" + if msg_err: + log(f"notes_add: {etudid} non inscrit ou DEM/DEF: aborting") + raise NoteProcessError(msg) + if msg_warn: + messages.append(msg) if (value is not None) and not isinstance(value, float): log(f"notes_add: {etudid} valeur de note invalide ({value}): aborting") + etud = Identite.query.get(etudid) if isinstance(etudid, int) else None raise NoteProcessError( - f"etudiant {etudid}: valeur de note invalide ({value})" + f"etudiant {etud.nomprenom if etud else etudid}: valeur de note invalide ({value})" ) # Recherche notes existantes notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id) @@ -566,102 +597,20 @@ def notes_add( etudids_with_decision = [] try: for etudid, value in notes: - changed = False - if etudid not in notes_db: - # nouvelle note - if value != scu.NOTES_SUPPRESS: - if do_it: - args = { - "etudid": etudid, - "evaluation_id": evaluation_id, - "value": value, - "comment": comment, - "uid": user.id, - "date": now, - } - ndb.quote_dict(args) - # Note: le conflit ci-dessous peut arriver si un autre thread - # a modifié la base après qu'on ait lu notes_db - cursor.execute( - """INSERT INTO notes_notes - (etudid, evaluation_id, value, comment, date, uid) - VALUES - (%(etudid)s,%(evaluation_id)s,%(value)s, - %(comment)s,%(date)s,%(uid)s) - ON CONFLICT ON CONSTRAINT notes_notes_etudid_evaluation_id_key - DO UPDATE SET etudid=%(etudid)s, evaluation_id=%(evaluation_id)s, - value=%(value)s, comment=%(comment)s, date=%(date)s, uid=%(uid)s - """, - args, - ) - changed = True - else: - # il y a deja une note - oldval = notes_db[etudid]["value"] - if type(value) != type(oldval): - changed = True - elif isinstance(value, float) and ( - abs(value - oldval) > scu.NOTES_PRECISION - ): - changed = True - elif value != oldval: - changed = True - if changed: - # recopie l'ancienne note dans notes_notes_log, puis update - if do_it: - cursor.execute( - """INSERT INTO notes_notes_log - (etudid,evaluation_id,value,comment,date,uid) - SELECT etudid, evaluation_id, value, comment, date, uid - FROM notes_notes - WHERE etudid=%(etudid)s - and evaluation_id=%(evaluation_id)s - """, - {"etudid": etudid, "evaluation_id": evaluation_id}, - ) - args = { - "etudid": etudid, - "evaluation_id": evaluation_id, - "value": value, - "date": now, - "comment": comment, - "uid": user.id, - } - ndb.quote_dict(args) - if value != scu.NOTES_SUPPRESS: - if do_it: - cursor.execute( - """UPDATE notes_notes - SET value=%(value)s, comment=%(comment)s, date=%(date)s, uid=%(uid)s - WHERE etudid = %(etudid)s - and evaluation_id = %(evaluation_id)s - """, - args, - ) - else: # suppression ancienne note - if do_it: - log( - f"""notes_add, suppress, evaluation_id={evaluation_id}, etudid={ - etudid}, oldval={oldval}""" - ) - cursor.execute( - """DELETE FROM notes_notes - WHERE etudid = %(etudid)s - AND evaluation_id = %(evaluation_id)s - """, - args, - ) - # garde trace de la suppression dans l'historique: - args["value"] = scu.NOTES_SUPPRESS - cursor.execute( - """INSERT INTO notes_notes_log - (etudid,evaluation_id,value,comment,date,uid) - VALUES - (%(etudid)s, %(evaluation_id)s, %(value)s, %(comment)s, %(date)s, %(uid)s) - """, - args, - ) - nb_suppress += 1 + changed, suppressed = _record_note( + cursor, + notes_db, + etudid, + evaluation_id, + value, + comment=comment, + user=user, + date=now, + do_it=do_it, + ) + if suppressed: + nb_suppress += 1 + if changed: etudids_changed.append(etudid) if res.etud_has_decision(etudid, include_rcues=False): @@ -678,7 +627,108 @@ def notes_add( cnx.commit() sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id) sco_cache.EvaluationCache.delete(evaluation_id) - return etudids_changed, nb_suppress, etudids_with_decision + return etudids_changed, nb_suppress, etudids_with_decision, messages + + +def _record_note( + cursor, + notes_db, + etudid: int, + evaluation_id: int, + value: float, + comment: str = "", + user: User = None, + date=None, + do_it=False, +): + "Enregistrement de la note en base" + changed = False + suppressed = False + args = { + "etudid": etudid, + "evaluation_id": evaluation_id, + "value": value, + # convention scodoc7 quote comment: + "comment": (html.escape(comment) if isinstance(comment, str) else comment), + "uid": user.id, + "date": date, + } + if etudid not in notes_db: + # nouvelle note + if value != scu.NOTES_SUPPRESS: + if do_it: + # Note: le conflit ci-dessous peut arriver si un autre thread + # a modifié la base après qu'on ait lu notes_db + cursor.execute( + """INSERT INTO notes_notes + (etudid, evaluation_id, value, comment, date, uid) + VALUES + (%(etudid)s,%(evaluation_id)s,%(value)s, + %(comment)s,%(date)s,%(uid)s) + ON CONFLICT ON CONSTRAINT notes_notes_etudid_evaluation_id_key + DO UPDATE SET etudid=%(etudid)s, evaluation_id=%(evaluation_id)s, + value=%(value)s, comment=%(comment)s, date=%(date)s, uid=%(uid)s + """, + args, + ) + changed = True + else: + # il y a deja une note + oldval = notes_db[etudid]["value"] + if type(value) != type(oldval): + changed = True + elif isinstance(value, float) and (abs(value - oldval) > scu.NOTES_PRECISION): + changed = True + elif value != oldval: + changed = True + if changed: + # recopie l'ancienne note dans notes_notes_log, puis update + if do_it: + cursor.execute( + """INSERT INTO notes_notes_log + (etudid,evaluation_id,value,comment,date,uid) + SELECT etudid, evaluation_id, value, comment, date, uid + FROM notes_notes + WHERE etudid=%(etudid)s + and evaluation_id=%(evaluation_id)s + """, + args, + ) + if value != scu.NOTES_SUPPRESS: + if do_it: + cursor.execute( + """UPDATE notes_notes + SET value=%(value)s, comment=%(comment)s, date=%(date)s, uid=%(uid)s + WHERE etudid = %(etudid)s + and evaluation_id = %(evaluation_id)s + """, + args, + ) + else: # suppression ancienne note + if do_it: + log( + f"""notes_add, suppress, evaluation_id={evaluation_id}, etudid={ + etudid}, oldval={oldval}""" + ) + cursor.execute( + """DELETE FROM notes_notes + WHERE etudid = %(etudid)s + AND evaluation_id = %(evaluation_id)s + """, + args, + ) + # garde trace de la suppression dans l'historique: + args["value"] = scu.NOTES_SUPPRESS + cursor.execute( + """INSERT INTO notes_notes_log + (etudid,evaluation_id,value,comment,date,uid) + VALUES + (%(etudid)s, %(evaluation_id)s, %(value)s, %(comment)s, %(date)s, %(uid)s) + """, + args, + ) + suppressed = True + return changed, suppressed def saisie_notes_tableur(evaluation_id, group_ids=()): @@ -703,7 +753,7 @@ def saisie_notes_tableur(evaluation_id, group_ids=()): ) page_title = "Saisie des notes" + ( - f"""de {evaluation.description}""" if evaluation.description else "" + f""" de {evaluation.description}""" if evaluation.description else "" ) # Informations sur les groupes à afficher: @@ -797,9 +847,13 @@ def saisie_notes_tableur(evaluation_id, group_ids=()): }"> Revenir au tableau de bord du module     + Charger un autre fichier de notes +     Charger d'autres notes dans cette évaluation + }">Formulaire de saisie des notes

""" ) else: @@ -1015,7 +1069,7 @@ def saisie_notes(evaluation_id: int, group_ids: list = None): "Autres opérations", [ { - "title": "Saisie par fichier tableur", + "title": "Saisir par fichier tableur", "id": "menu_saisie_tableur", "endpoint": "notes.saisie_notes_tableur", "args": { @@ -1126,7 +1180,7 @@ def _get_sorted_etuds(evaluation: Evaluation, etudids: list, formsemestre_id: in # Note actuelle de l'étudiant: if etudid in notes_db: - e["val"] = _displayNote(notes_db[etudid]["value"]) + e["val"] = _display_note(notes_db[etudid]["value"]) comment = notes_db[etudid]["comment"] if comment is None: comment = "" @@ -1368,7 +1422,7 @@ def save_notes( # valid_notes, _, _, _, _ = _check_notes(notes, evaluation) if valid_notes: - etudids_changed, _, etudids_with_decision = notes_add( + etudids_changed, _, etudids_with_decision, messages = notes_add( current_user, evaluation.id, valid_notes, comment=comment, do_it=True ) ScolarNews.add( @@ -1386,12 +1440,14 @@ def save_notes( etudid: get_note_history_menu(evaluation.id, etudid) for etudid in etudids_changed }, + "messages": messages, } else: result = { "etudids_changed": [], "etudids_with_decision": [], "history_menu": [], + "messages": [], } return result @@ -1420,7 +1476,7 @@ def get_note_history_menu(evaluation_id: int, etudid: int) -> str: first = True for i in history: jt = i["date"].strftime("le %d/%m/%Y à %H:%M") + " (%s)" % i["user_name"] - dispnote = _displayNote(i["value"]) + dispnote = _display_note(i["value"]) if first: nv = "" # ne repete pas la valeur de la note courante else: diff --git a/app/scodoc/sco_ue_external.py b/app/scodoc/sco_ue_external.py index f1840386b..ff5bca9bc 100644 --- a/app/scodoc/sco_ue_external.py +++ b/app/scodoc/sco_ue_external.py @@ -180,7 +180,7 @@ def external_ue_inscrit_et_note( description="note externe", ) # Saisie des notes - _, _, _ = sco_saisie_notes.notes_add( + _, _, _, _ = sco_saisie_notes.notes_add( current_user, evaluation.id, list(notes_etuds.items()), diff --git a/app/scodoc/sco_users.py b/app/scodoc/sco_users.py index eb19901b3..f1ab31917 100644 --- a/app/scodoc/sco_users.py +++ b/app/scodoc/sco_users.py @@ -35,7 +35,7 @@ from flask import url_for, g, request from flask_login import current_user -from app import db, Departement +from app import Departement from app.auth.models import Permission, Role, User, UserRole from app.models import ScoDocSiteConfig, USERNAME_STR_LEN diff --git a/sco_version.py b/sco_version.py index 0a1eeda1f..2d37b22ba 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.6.975" +SCOVERSION = "9.6.976" SCONAME = "ScoDoc" diff --git a/tests/unit/test_notes_modules.py b/tests/unit/test_notes_modules.py index 1542eaecd..7a5a9ea56 100644 --- a/tests/unit/test_notes_modules.py +++ b/tests/unit/test_notes_modules.py @@ -2,6 +2,7 @@ Vérif moyennes de modules des bulletins et aussi moyennes modules et UE internes (via nt) """ + import datetime import numpy as np from flask import g @@ -107,16 +108,16 @@ def test_notes_modules(test_client): # --- Notes ordinaires note_1 = 12.0 note_2 = 13.0 - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e1["evaluation_id"], etudid=etuds[0]["etudid"], note=note_1 ) - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e2["evaluation_id"], etudid=etuds[0]["etudid"], note=note_2 ) - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e1["evaluation_id"], etudid=etuds[1]["etudid"], note=note_1 / 2 ) - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e2["evaluation_id"], etudid=etuds[1]["etudid"], note=note_2 / 3 ) b = sco_bulletins.formsemestre_bulletinetud_dict( @@ -139,22 +140,20 @@ def test_notes_modules(test_client): ) # Absence à une évaluation - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e1["evaluation_id"], etudid=etudid, note=None ) # abs - _, _, _ = G.create_note( - evaluation_id=e2["evaluation_id"], etudid=etudid, note=note_2 - ) + _ = G.create_note(evaluation_id=e2["evaluation_id"], etudid=etudid, note=note_2) b = sco_bulletins.formsemestre_bulletinetud_dict( sem["formsemestre_id"], etud["etudid"] ) note_th = (coef_1 * 0.0 + coef_2 * note_2) / (coef_1 + coef_2) assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(note_th) # Absences aux deux évaluations - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e1["evaluation_id"], etudid=etudid, note=None ) # abs - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e2["evaluation_id"], etudid=etudid, note=None ) # abs b = sco_bulletins.formsemestre_bulletinetud_dict( @@ -171,10 +170,8 @@ def test_notes_modules(test_client): ) # Note excusée EXC <-> scu.NOTES_NEUTRALISE - _, _, _ = G.create_note( - evaluation_id=e1["evaluation_id"], etudid=etudid, note=note_1 - ) - _, _, _ = G.create_note( + _ = G.create_note(evaluation_id=e1["evaluation_id"], etudid=etudid, note=note_1) + _ = G.create_note( evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE ) # EXC b = sco_bulletins.formsemestre_bulletinetud_dict( @@ -190,10 +187,8 @@ def test_notes_modules(test_client): expected_moy_ue=note_1, ) # Note en attente ATT <-> scu.NOTES_ATTENTE - _, _, _ = G.create_note( - evaluation_id=e1["evaluation_id"], etudid=etudid, note=note_1 - ) - _, _, _ = G.create_note( + _ = G.create_note(evaluation_id=e1["evaluation_id"], etudid=etudid, note=note_1) + _ = G.create_note( evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_ATTENTE ) # ATT b = sco_bulletins.formsemestre_bulletinetud_dict( @@ -209,10 +204,10 @@ def test_notes_modules(test_client): expected_moy_ue=note_1, ) # Neutralisation (EXC) des 2 évals - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e1["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE ) # EXC - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE ) # EXC b = sco_bulletins.formsemestre_bulletinetud_dict( @@ -228,10 +223,10 @@ def test_notes_modules(test_client): expected_moy_ue=np.nan, ) # Attente (ATT) sur les 2 evals - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e1["evaluation_id"], etudid=etudid, note=scu.NOTES_ATTENTE ) # ATT - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_ATTENTE ) # ATT b = sco_bulletins.formsemestre_bulletinetud_dict( @@ -290,7 +285,7 @@ def test_notes_modules(test_client): {"etudid": etudid, "moduleimpl_id": moduleimpl_id}, formsemestre_id=formsemestre_id, ) - _, _, _ = G.create_note(evaluation_id=e1["evaluation_id"], etudid=etudid, note=12.5) + _ = G.create_note(evaluation_id=e1["evaluation_id"], etudid=etudid, note=12.5) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) mod_stats = nt.get_mod_stats(moduleimpl_id) @@ -318,9 +313,7 @@ def test_notes_modules(test_client): description="evaluation mod 2", coefficient=1.0, ) - _, _, _ = G.create_note( - evaluation_id=e_m2["evaluation_id"], etudid=etudid, note=19.5 - ) + _ = G.create_note(evaluation_id=e_m2["evaluation_id"], etudid=etudid, note=19.5) formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) ue_status = nt.get_etud_ue_status(etudid, ue_id) @@ -328,22 +321,20 @@ def test_notes_modules(test_client): # Moyenne d'UE si l'un des modules est EXC ("NA") # 2 modules, notes EXC dans le premier, note valide n dans le second # la moyenne de l'UE doit être n - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e1["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE ) # EXC - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE ) # EXC - _, _, _ = G.create_note( - evaluation_id=e_m2["evaluation_id"], etudid=etudid, note=12.5 - ) - _, _, _ = G.create_note( + _ = G.create_note(evaluation_id=e_m2["evaluation_id"], etudid=etudid, note=12.5) + _ = G.create_note( evaluation_id=e1["evaluation_id"], etudid=etuds[1]["etudid"], note=11.0 ) - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e2["evaluation_id"], etudid=etuds[1]["etudid"], note=11.0 ) - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e_m2["evaluation_id"], etudid=etuds[1]["etudid"], note=11.0 ) @@ -407,12 +398,12 @@ def test_notes_modules_att_dem(test_client): coefficient=coef_1, ) # Attente (ATT) sur les 2 evals - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e1["evaluation_id"], etudid=etuds[0]["etudid"], note=scu.NOTES_ATTENTE, ) # ATT - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e1["evaluation_id"], etudid=etuds[1]["etudid"], note=scu.NOTES_ATTENTE, @@ -455,7 +446,7 @@ def test_notes_modules_att_dem(test_client): assert note_e1 == scu.NOTES_ATTENTE # XXXX un peu contestable # Saisie note ABS pour le deuxième etud - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e1["evaluation_id"], etudid=etuds[1]["etudid"], note=None ) nt = check_nt( diff --git a/tests/unit/test_notes_rattrapage.py b/tests/unit/test_notes_rattrapage.py index 22cd1e475..4ee29c328 100644 --- a/tests/unit/test_notes_rattrapage.py +++ b/tests/unit/test_notes_rattrapage.py @@ -72,8 +72,8 @@ def test_notes_rattrapage(test_client): evaluation_type=Evaluation.EVALUATION_RATTRAPAGE, ) etud = etuds[0] - _, _, _ = G.create_note(evaluation_id=e["id"], etudid=etud["etudid"], note=12.0) - _, _, _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["etudid"], note=11.0) + _ = G.create_note(evaluation_id=e["id"], etudid=etud["etudid"], note=12.0) + _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["etudid"], note=11.0) # --- Vérifications internes structures ScoDoc formsemestre = db.session.get(FormSemestre, formsemestre_id) @@ -98,23 +98,21 @@ def test_notes_rattrapage(test_client): # Note moyenne: ici le ratrapage est inférieur à la note: assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(12.0) # rattrapage > moyenne: - _, _, _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["etudid"], note=18.0) + _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["etudid"], note=18.0) b = sco_bulletins.formsemestre_bulletinetud_dict( sem["formsemestre_id"], etud["etudid"] ) assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(18.0) # rattrapage vs absences - _, _, _ = G.create_note( - evaluation_id=e["id"], etudid=etud["etudid"], note=None - ) # abs - _, _, _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["etudid"], note=17.0) + _ = G.create_note(evaluation_id=e["id"], etudid=etud["etudid"], note=None) # abs + _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["etudid"], note=17.0) b = sco_bulletins.formsemestre_bulletinetud_dict( sem["formsemestre_id"], etud["etudid"] ) assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(17.0) # et sans note de rattrapage - _, _, _ = G.create_note(evaluation_id=e["id"], etudid=etud["etudid"], note=10.0) - _, _, _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["etudid"], note=None) + _ = G.create_note(evaluation_id=e["id"], etudid=etud["etudid"], note=10.0) + _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["etudid"], note=None) b = sco_bulletins.formsemestre_bulletinetud_dict( sem["formsemestre_id"], etud["etudid"] ) @@ -159,18 +157,14 @@ def test_notes_rattrapage(test_client): assert len(mod_res.get_evaluations_completes(moduleimpl)) == 2 # Saisie note session 2: - _, _, _ = G.create_note( - evaluation_id=e_session2["id"], etudid=etud["etudid"], note=5.0 - ) + _ = G.create_note(evaluation_id=e_session2["id"], etudid=etud["etudid"], note=5.0) b = sco_bulletins.formsemestre_bulletinetud_dict( sem["formsemestre_id"], etud["etudid"] ) # Note moyenne: utilise session 2 même si inférieure assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(5.0) - _, _, _ = G.create_note( - evaluation_id=e_session2["id"], etudid=etud["etudid"], note=20.0 - ) + _ = G.create_note(evaluation_id=e_session2["id"], etudid=etud["etudid"], note=20.0) b = sco_bulletins.formsemestre_bulletinetud_dict( sem["formsemestre_id"], etud["etudid"] ) @@ -178,16 +172,14 @@ def test_notes_rattrapage(test_client): assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(20.0) # Met la note session2 à ABS (None) - _, _, _ = G.create_note( - evaluation_id=e_session2["id"], etudid=etud["etudid"], note=None - ) + _ = G.create_note(evaluation_id=e_session2["id"], etudid=etud["etudid"], note=None) b = sco_bulletins.formsemestre_bulletinetud_dict( sem["formsemestre_id"], etud["etudid"] ) # Note moyenne: zéro car ABS assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(0.0) # Supprime note session 2 - _, _, _ = G.create_note( + _ = G.create_note( evaluation_id=e_session2["id"], etudid=etud["etudid"], note=scu.NOTES_SUPPRESS ) b = sco_bulletins.formsemestre_bulletinetud_dict( @@ -216,18 +208,14 @@ def test_notes_rattrapage(test_client): # Note moyenne sans bonus assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(10.0) # Saisie note bonus - _, _, _ = G.create_note( - evaluation_id=e_bonus["id"], etudid=etud["etudid"], note=1.0 - ) + _ = G.create_note(evaluation_id=e_bonus["id"], etudid=etud["etudid"], note=1.0) b = sco_bulletins.formsemestre_bulletinetud_dict( sem["formsemestre_id"], etud["etudid"] ) # Note moyenne sans bonus assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(11.0) # Négatif, avec clip à zéro - _, _, _ = G.create_note( - evaluation_id=e_bonus["id"], etudid=etud["etudid"], note=-20.0 - ) + _ = G.create_note(evaluation_id=e_bonus["id"], etudid=etud["etudid"], note=-20.0) b = sco_bulletins.formsemestre_bulletinetud_dict( sem["formsemestre_id"], etud["etudid"] ) diff --git a/tests/unit/test_sco_basic.py b/tests/unit/test_sco_basic.py index b3bba2e20..e159391c4 100644 --- a/tests/unit/test_sco_basic.py +++ b/tests/unit/test_sco_basic.py @@ -105,7 +105,7 @@ def run_sco_basic(verbose=False, dept=None) -> FormSemestre: # --- Saisie toutes les notes de l'évaluation for idx, etud in enumerate(etuds): - etudids_changed, nb_suppress, existing_decisions = G.create_note( + etudids_changed, nb_suppress, existing_decisions, messages = G.create_note( evaluation_id=e1.id, etudid=etud["etudid"], note=NOTES_T[idx % len(NOTES_T)], @@ -113,6 +113,7 @@ def run_sco_basic(verbose=False, dept=None) -> FormSemestre: assert not existing_decisions assert nb_suppress == 0 assert len(etudids_changed) == 1 + assert messages == [] # --- Vérifie que les notes sont prises en compte: b = sco_bulletins.formsemestre_bulletinetud_dict(formsemestre_id, etud["etudid"]) @@ -139,11 +140,12 @@ def run_sco_basic(verbose=False, dept=None) -> FormSemestre: db.session.commit() # Saisie les notes des 5 premiers étudiants: for idx, etud in enumerate(etuds[:5]): - etudids_changed, nb_suppress, existing_decisions = G.create_note( + etudids_changed, nb_suppress, existing_decisions, messages = G.create_note( evaluation_id=e2.id, etudid=etud["etudid"], note=NOTES_T[idx % len(NOTES_T)], ) + assert messages == [] # Cette éval n'est pas complète etat = sco_evaluations.do_evaluation_etat(e2.id) assert etat["evalcomplete"] is False @@ -162,11 +164,12 @@ def run_sco_basic(verbose=False, dept=None) -> FormSemestre: # Saisie des notes qui manquent: for idx, etud in enumerate(etuds[5:]): - etudids_changed, nb_suppress, existing_decisions = G.create_note( + etudids_changed, nb_suppress, existing_decisions, messages = G.create_note( evaluation_id=e2.id, etudid=etud["etudid"], note=NOTES_T[idx % len(NOTES_T)], ) + assert messages == [] etat = sco_evaluations.do_evaluation_etat(e2.id) assert etat["evalcomplete"] assert etat["nb_att"] == 0