Import utilisateurs: strip champs

This commit is contained in:
Emmanuel Viennet 2024-06-14 20:15:20 +02:00
parent c94fea9f9e
commit 07c2f00277
10 changed files with 244 additions and 198 deletions

View File

@ -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

View File

@ -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",

View File

@ -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"""<p>{len(etudids_changed)} notes changées ({len(withoutnotes)} sans notes, {
len(absents)} absents, {nb_suppress} note supprimées)
msg = f"""<p>
{len(etudids_changed)} notes changées ({len(withoutnotes)} sans notes,
{len(absents)} absents, {nb_suppress} note supprimées)
</p>"""
if messages:
msg += f"""<div class="warning">Attention&nbsp;:
<ul>
<li>{
'</li><li>'.join(messages)
}
</li>
</ul>
</div>"""
if etudids_with_decisions:
msg += """<p class="warning">Important: il y avait déjà des décisions de jury
enregistrées, qui sont peut-être à revoir suite à cette modification !</p>
@ -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"""<p>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)
):
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(f"étudiant {etudid} non inscrit dans ce module")
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,20 +597,66 @@ 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:
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):
etudids_with_decision.append(etudid)
except Exception as exc:
log("*** exception in notes_add")
if do_it:
cnx.rollback() # abort
# inval cache
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
sco_cache.EvaluationCache.delete(evaluation_id)
raise ScoException from exc
if do_it:
cnx.commit()
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
sco_cache.EvaluationCache.delete(evaluation_id)
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,
"comment": comment,
# convention scodoc7 quote comment:
"comment": (html.escape(comment) if isinstance(comment, str) else comment),
"uid": user.id,
"date": now,
"date": date,
}
ndb.quote_dict(args)
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(
@ -600,9 +677,7 @@ def notes_add(
oldval = notes_db[etudid]["value"]
if type(value) != type(oldval):
changed = True
elif isinstance(value, float) and (
abs(value - oldval) > scu.NOTES_PRECISION
):
elif isinstance(value, float) and (abs(value - oldval) > scu.NOTES_PRECISION):
changed = True
elif value != oldval:
changed = True
@ -617,17 +692,8 @@ def notes_add(
WHERE etudid=%(etudid)s
and evaluation_id=%(evaluation_id)s
""",
{"etudid": etudid, "evaluation_id": evaluation_id},
args,
)
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(
@ -661,24 +727,8 @@ def notes_add(
""",
args,
)
nb_suppress += 1
if changed:
etudids_changed.append(etudid)
if res.etud_has_decision(etudid, include_rcues=False):
etudids_with_decision.append(etudid)
except Exception as exc:
log("*** exception in notes_add")
if do_it:
cnx.rollback() # abort
# inval cache
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
sco_cache.EvaluationCache.delete(evaluation_id)
raise ScoException from exc
if do_it:
cnx.commit()
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
sco_cache.EvaluationCache.delete(evaluation_id)
return etudids_changed, nb_suppress, etudids_with_decision
suppressed = True
return changed, suppressed
def saisie_notes_tableur(evaluation_id, group_ids=()):
@ -797,9 +847,13 @@ def saisie_notes_tableur(evaluation_id, group_ids=()):
}">
Revenir au tableau de bord du module</a>
&nbsp;&nbsp;&nbsp;
<a class="stdlink" href="{url_for("notes.saisie_notes_tableur",
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">Charger un autre fichier de notes</a>
&nbsp;&nbsp;&nbsp;
<a class="stdlink" href="{url_for("notes.saisie_notes",
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">Charger d'autres notes dans cette évaluation</a>
}">Formulaire de saisie des notes</a>
</p>"""
)
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:

View File

@ -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()),

View File

@ -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

View File

@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION = "9.6.975"
SCOVERSION = "9.6.976"
SCONAME = "ScoDoc"

View File

@ -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(

View File

@ -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"]
)

View File

@ -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