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

@ -973,7 +973,7 @@ class FormSemestre(models.ScoDocModel):
def etudids_actifs(self) -> tuple[list[int], set[int]]: def etudids_actifs(self) -> tuple[list[int], set[int]]:
"""Liste les etudids inscrits (incluant DEM et DEF), """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. et donne l'ensemble des inscrits non DEM ni DEF.
""" """
return [inscr.etudid for inscr in self.inscriptions], { return [inscr.etudid for inscr in self.inscriptions], {

View File

@ -140,7 +140,7 @@ def read_users_excel_file(datafile, titles=TITLES) -> list[dict]:
for line in data[1:]: for line in data[1:]:
d = {} d = {}
for i, field in enumerate(xls_titles): for i, field in enumerate(xls_titles):
d[field] = line[i] d[field] = (line[i] or "").strip()
users.append(d) users.append(d)
return users return users

View File

@ -70,13 +70,21 @@ def moduleimpl_evaluation_menu(evaluation: Evaluation, nbnotes: int = 0) -> str:
menu_eval = [ menu_eval = [
{ {
"title": "Saisir notes", "title": "Saisir les notes",
"endpoint": "notes.saisie_notes", "endpoint": "notes.saisie_notes",
"args": { "args": {
"evaluation_id": evaluation_id, "evaluation_id": evaluation_id,
}, },
"enabled": can_edit_notes_ens, "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", "title": "Modifier évaluation",
"endpoint": "notes.evaluation_edit", "endpoint": "notes.evaluation_edit",

View File

@ -29,12 +29,15 @@
Formulaire revu en juillet 2016 Formulaire revu en juillet 2016
""" """
import html
import time import time
import psycopg2
import flask import flask
from flask import g, url_for, request from flask import g, url_for, request
from flask_login import current_user from flask_login import current_user
from flask_sqlalchemy.query import Query
import psycopg2
from app import db, log from app import db, log
from app.auth.models import User 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 json_error
from app.scodoc.sco_utils import ModuleType from app.scodoc.sco_utils import ModuleType
from flask_sqlalchemy.query import Query
def convert_note_from_string( def convert_note_from_string(
note: str, note: str,
@ -115,7 +116,7 @@ def convert_note_from_string(
return note_value, invalid return note_value, invalid
def _displayNote(val): def _display_note(val):
"""Convert note from DB to viewable string. """Convert note from DB to viewable string.
Utilisé seulement pour I/O vers formulaires (sans perte de precision) Utilisé seulement pour I/O vers formulaires (sans perte de precision)
(Utiliser fmt_note pour les affichages) (Utiliser fmt_note pour les affichages)
@ -272,7 +273,7 @@ def do_evaluation_upload_xls():
diag.append("Notes invalides pour: " + ", ".join(etudsnames)) diag.append("Notes invalides pour: " + ", ".join(etudsnames))
raise InvalidNoteValue() raise InvalidNoteValue()
else: 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 current_user, evaluation_id, valid_notes, comment
) )
# news # news
@ -292,9 +293,19 @@ def do_evaluation_upload_xls():
max_frequency=30 * 60, # 30 minutes max_frequency=30 * 60, # 30 minutes
) )
msg = f"""<p>{len(etudids_changed)} notes changées ({len(withoutnotes)} sans notes, { msg = f"""<p>
len(absents)} absents, {nb_suppress} note supprimées) {len(etudids_changed)} notes changées ({len(withoutnotes)} sans notes,
{len(absents)} absents, {nb_suppress} note supprimées)
</p>""" </p>"""
if messages:
msg += f"""<div class="warning">Attention&nbsp;:
<ul>
<li>{
'</li><li>'.join(messages)
}
</li>
</ul>
</div>"""
if etudids_with_decisions: if etudids_with_decisions:
msg += """<p class="warning">Important: il y avait déjà des décisions de jury 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> 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 # Convert and check value
L, invalids, _, _, _ = _check_notes([(etud.id, value)], evaluation) L, invalids, _, _, _ = _check_notes([(etud.id, value)], evaluation)
if len(invalids) == 0: if len(invalids) == 0:
etudids_changed, _, _ = notes_add( etudids_changed, _, _, _ = notes_add(
current_user, evaluation.id, L, "Initialisation notes" current_user, evaluation.id, L, "Initialisation notes"
) )
if len(etudids_changed) == 1: if len(etudids_changed) == 1:
@ -398,7 +409,9 @@ def do_evaluation_set_missing(
) )
# ok # ok
comment = "Initialisation notes manquantes" 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 # news
url = url_for( url = url_for(
"notes.moduleimpl_status", "notes.moduleimpl_status",
@ -456,7 +469,7 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
) )
if not dialog_confirmed: 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 current_user, evaluation_id, notes, do_it=False, check_inscription=False
) )
msg = f"""<p>Confirmer la suppression des {nb_suppress} notes ? msg = f"""<p>Confirmer la suppression des {nb_suppress} notes ?
@ -477,7 +490,7 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
) )
# modif # modif
etudids_changed, nb_suppress, existing_decisions = notes_add( etudids_changed, nb_suppress, existing_decisions, _ = notes_add(
current_user, current_user,
evaluation_id, evaluation_id,
notes, notes,
@ -519,7 +532,7 @@ def notes_add(
comment=None, comment=None,
do_it=True, do_it=True,
check_inscription=True, check_inscription=True,
) -> tuple[list[int], int, list[int]]: ) -> tuple[list[int], int, list[int], list[str]]:
""" """
Insert or update notes Insert or update notes
notes is a list of tuples (etudid,value) notes is a list of tuples (etudid,value)
@ -528,30 +541,48 @@ def notes_add(
Nota: Nota:
- 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 (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) evaluation = Evaluation.get_evaluation(evaluation_id)
now = psycopg2.Timestamp(*time.localtime()[:6]) now = psycopg2.Timestamp(*time.localtime()[:6])
messages = []
# Vérifie inscription et valeur note # Vérifie inscription au module (même DEM/DEF)
inscrits = { etudids_inscrits_mod = {
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
) )
} }
# Les étudiants inscrits au semestre ni DEM ni DEF # Les étudiants inscrits au semestre et ceux "actifs" (ni DEM ni DEF)
_, etudids_actifs = evaluation.moduleimpl.formsemestre.etudids_actifs() etudids_inscrits_sem, etudids_actifs = (
evaluation.moduleimpl.formsemestre.etudids_actifs()
)
for etudid, value in notes: 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 = "", ""
log(f"notes_add: {etudid} non inscrit ou DEM/DEF: aborting") if etudid not in etudids_inscrits_sem:
raise NoteProcessError(f"étudiant {etudid} non inscrit dans ce module") 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): if (value is not None) and not isinstance(value, float):
log(f"notes_add: {etudid} valeur de note invalide ({value}): aborting") log(f"notes_add: {etudid} valeur de note invalide ({value}): aborting")
etud = Identite.query.get(etudid) if isinstance(etudid, int) else None
raise NoteProcessError( 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 # Recherche notes existantes
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id) notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
@ -566,102 +597,20 @@ def notes_add(
etudids_with_decision = [] etudids_with_decision = []
try: try:
for etudid, value in notes: for etudid, value in notes:
changed = False changed, suppressed = _record_note(
if etudid not in notes_db: cursor,
# nouvelle note notes_db,
if value != scu.NOTES_SUPPRESS: etudid,
if do_it: evaluation_id,
args = { value,
"etudid": etudid, comment=comment,
"evaluation_id": evaluation_id, user=user,
"value": value, date=now,
"comment": comment, do_it=do_it,
"uid": user.id, )
"date": now, if suppressed:
} nb_suppress += 1
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
if changed: if changed:
etudids_changed.append(etudid) etudids_changed.append(etudid)
if res.etud_has_decision(etudid, include_rcues=False): if res.etud_has_decision(etudid, include_rcues=False):
@ -678,7 +627,108 @@ def notes_add(
cnx.commit() cnx.commit()
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id) sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
sco_cache.EvaluationCache.delete(evaluation_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=()): 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" + ( 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: # Informations sur les groupes à afficher:
@ -797,9 +847,13 @@ def saisie_notes_tableur(evaluation_id, group_ids=()):
}"> }">
Revenir au tableau de bord du module</a> Revenir au tableau de bord du module</a>
&nbsp;&nbsp;&nbsp; &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", <a class="stdlink" href="{url_for("notes.saisie_notes",
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id) scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">Charger d'autres notes dans cette évaluation</a> }">Formulaire de saisie des notes</a>
</p>""" </p>"""
) )
else: else:
@ -1015,7 +1069,7 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
"Autres opérations", "Autres opérations",
[ [
{ {
"title": "Saisie par fichier tableur", "title": "Saisir par fichier tableur",
"id": "menu_saisie_tableur", "id": "menu_saisie_tableur",
"endpoint": "notes.saisie_notes_tableur", "endpoint": "notes.saisie_notes_tableur",
"args": { "args": {
@ -1126,7 +1180,7 @@ def _get_sorted_etuds(evaluation: Evaluation, etudids: list, formsemestre_id: in
# Note actuelle de l'étudiant: # Note actuelle de l'étudiant:
if etudid in notes_db: 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"] comment = notes_db[etudid]["comment"]
if comment is None: if comment is None:
comment = "" comment = ""
@ -1368,7 +1422,7 @@ def save_notes(
# #
valid_notes, _, _, _, _ = _check_notes(notes, evaluation) valid_notes, _, _, _, _ = _check_notes(notes, evaluation)
if valid_notes: 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 current_user, evaluation.id, valid_notes, comment=comment, do_it=True
) )
ScolarNews.add( ScolarNews.add(
@ -1386,12 +1440,14 @@ def save_notes(
etudid: get_note_history_menu(evaluation.id, etudid) etudid: get_note_history_menu(evaluation.id, etudid)
for etudid in etudids_changed for etudid in etudids_changed
}, },
"messages": messages,
} }
else: else:
result = { result = {
"etudids_changed": [], "etudids_changed": [],
"etudids_with_decision": [], "etudids_with_decision": [],
"history_menu": [], "history_menu": [],
"messages": [],
} }
return result return result
@ -1420,7 +1476,7 @@ def get_note_history_menu(evaluation_id: int, etudid: int) -> str:
first = True first = True
for i in history: for i in history:
jt = i["date"].strftime("le %d/%m/%Y à %H:%M") + " (%s)" % i["user_name"] 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: if first:
nv = "" # ne repete pas la valeur de la note courante nv = "" # ne repete pas la valeur de la note courante
else: else:

View File

@ -180,7 +180,7 @@ def external_ue_inscrit_et_note(
description="note externe", description="note externe",
) )
# Saisie des notes # Saisie des notes
_, _, _ = sco_saisie_notes.notes_add( _, _, _, _ = sco_saisie_notes.notes_add(
current_user, current_user,
evaluation.id, evaluation.id,
list(notes_etuds.items()), list(notes_etuds.items()),

View File

@ -35,7 +35,7 @@ from flask import url_for, g, request
from flask_login import current_user 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.auth.models import Permission, Role, User, UserRole
from app.models import ScoDocSiteConfig, USERNAME_STR_LEN from app.models import ScoDocSiteConfig, USERNAME_STR_LEN

View File

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

View File

@ -2,6 +2,7 @@
Vérif moyennes de modules des bulletins Vérif moyennes de modules des bulletins
et aussi moyennes modules et UE internes (via nt) et aussi moyennes modules et UE internes (via nt)
""" """
import datetime import datetime
import numpy as np import numpy as np
from flask import g from flask import g
@ -107,16 +108,16 @@ def test_notes_modules(test_client):
# --- Notes ordinaires # --- Notes ordinaires
note_1 = 12.0 note_1 = 12.0
note_2 = 13.0 note_2 = 13.0
_, _, _ = G.create_note( _ = G.create_note(
evaluation_id=e1["evaluation_id"], etudid=etuds[0]["etudid"], note=note_1 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 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 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 evaluation_id=e2["evaluation_id"], etudid=etuds[1]["etudid"], note=note_2 / 3
) )
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
@ -139,22 +140,20 @@ def test_notes_modules(test_client):
) )
# Absence à une évaluation # Absence à une évaluation
_, _, _ = G.create_note( _ = G.create_note(
evaluation_id=e1["evaluation_id"], etudid=etudid, note=None evaluation_id=e1["evaluation_id"], etudid=etudid, note=None
) # abs ) # abs
_, _, _ = G.create_note( _ = G.create_note(evaluation_id=e2["evaluation_id"], etudid=etudid, note=note_2)
evaluation_id=e2["evaluation_id"], etudid=etudid, note=note_2
)
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )
note_th = (coef_1 * 0.0 + coef_2 * note_2) / (coef_1 + coef_2) 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) assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(note_th)
# Absences aux deux évaluations # Absences aux deux évaluations
_, _, _ = G.create_note( _ = G.create_note(
evaluation_id=e1["evaluation_id"], etudid=etudid, note=None evaluation_id=e1["evaluation_id"], etudid=etudid, note=None
) # abs ) # abs
_, _, _ = G.create_note( _ = G.create_note(
evaluation_id=e2["evaluation_id"], etudid=etudid, note=None evaluation_id=e2["evaluation_id"], etudid=etudid, note=None
) # abs ) # abs
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
@ -171,10 +170,8 @@ def test_notes_modules(test_client):
) )
# Note excusée EXC <-> scu.NOTES_NEUTRALISE # Note excusée EXC <-> scu.NOTES_NEUTRALISE
_, _, _ = G.create_note( _ = G.create_note(evaluation_id=e1["evaluation_id"], etudid=etudid, note=note_1)
evaluation_id=e1["evaluation_id"], etudid=etudid, note=note_1 _ = G.create_note(
)
_, _, _ = G.create_note(
evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
) # EXC ) # EXC
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
@ -190,10 +187,8 @@ def test_notes_modules(test_client):
expected_moy_ue=note_1, expected_moy_ue=note_1,
) )
# Note en attente ATT <-> scu.NOTES_ATTENTE # Note en attente ATT <-> scu.NOTES_ATTENTE
_, _, _ = G.create_note( _ = G.create_note(evaluation_id=e1["evaluation_id"], etudid=etudid, note=note_1)
evaluation_id=e1["evaluation_id"], etudid=etudid, note=note_1 _ = G.create_note(
)
_, _, _ = G.create_note(
evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_ATTENTE evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_ATTENTE
) # ATT ) # ATT
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
@ -209,10 +204,10 @@ def test_notes_modules(test_client):
expected_moy_ue=note_1, expected_moy_ue=note_1,
) )
# Neutralisation (EXC) des 2 évals # Neutralisation (EXC) des 2 évals
_, _, _ = G.create_note( _ = G.create_note(
evaluation_id=e1["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE evaluation_id=e1["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
) # EXC ) # EXC
_, _, _ = G.create_note( _ = G.create_note(
evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
) # EXC ) # EXC
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
@ -228,10 +223,10 @@ def test_notes_modules(test_client):
expected_moy_ue=np.nan, expected_moy_ue=np.nan,
) )
# Attente (ATT) sur les 2 evals # Attente (ATT) sur les 2 evals
_, _, _ = G.create_note( _ = G.create_note(
evaluation_id=e1["evaluation_id"], etudid=etudid, note=scu.NOTES_ATTENTE evaluation_id=e1["evaluation_id"], etudid=etudid, note=scu.NOTES_ATTENTE
) # ATT ) # ATT
_, _, _ = G.create_note( _ = G.create_note(
evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_ATTENTE evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_ATTENTE
) # ATT ) # ATT
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
@ -290,7 +285,7 @@ def test_notes_modules(test_client):
{"etudid": etudid, "moduleimpl_id": moduleimpl_id}, {"etudid": etudid, "moduleimpl_id": moduleimpl_id},
formsemestre_id=formsemestre_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) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
mod_stats = nt.get_mod_stats(moduleimpl_id) mod_stats = nt.get_mod_stats(moduleimpl_id)
@ -318,9 +313,7 @@ def test_notes_modules(test_client):
description="evaluation mod 2", description="evaluation mod 2",
coefficient=1.0, coefficient=1.0,
) )
_, _, _ = G.create_note( _ = G.create_note(evaluation_id=e_m2["evaluation_id"], etudid=etudid, note=19.5)
evaluation_id=e_m2["evaluation_id"], etudid=etudid, note=19.5
)
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
ue_status = nt.get_etud_ue_status(etudid, ue_id) 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") # Moyenne d'UE si l'un des modules est EXC ("NA")
# 2 modules, notes EXC dans le premier, note valide n dans le second # 2 modules, notes EXC dans le premier, note valide n dans le second
# la moyenne de l'UE doit être n # 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 evaluation_id=e1["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
) # EXC ) # EXC
_, _, _ = G.create_note( _ = G.create_note(
evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
) # EXC ) # EXC
_, _, _ = G.create_note( _ = G.create_note(evaluation_id=e_m2["evaluation_id"], etudid=etudid, note=12.5)
evaluation_id=e_m2["evaluation_id"], etudid=etudid, note=12.5 _ = G.create_note(
)
_, _, _ = G.create_note(
evaluation_id=e1["evaluation_id"], etudid=etuds[1]["etudid"], note=11.0 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 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 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, coefficient=coef_1,
) )
# Attente (ATT) sur les 2 evals # Attente (ATT) sur les 2 evals
_, _, _ = G.create_note( _ = G.create_note(
evaluation_id=e1["evaluation_id"], evaluation_id=e1["evaluation_id"],
etudid=etuds[0]["etudid"], etudid=etuds[0]["etudid"],
note=scu.NOTES_ATTENTE, note=scu.NOTES_ATTENTE,
) # ATT ) # ATT
_, _, _ = G.create_note( _ = G.create_note(
evaluation_id=e1["evaluation_id"], evaluation_id=e1["evaluation_id"],
etudid=etuds[1]["etudid"], etudid=etuds[1]["etudid"],
note=scu.NOTES_ATTENTE, 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 assert note_e1 == scu.NOTES_ATTENTE # XXXX un peu contestable
# Saisie note ABS pour le deuxième etud # 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 evaluation_id=e1["evaluation_id"], etudid=etuds[1]["etudid"], note=None
) )
nt = check_nt( nt = check_nt(

View File

@ -72,8 +72,8 @@ def test_notes_rattrapage(test_client):
evaluation_type=Evaluation.EVALUATION_RATTRAPAGE, evaluation_type=Evaluation.EVALUATION_RATTRAPAGE,
) )
etud = etuds[0] etud = etuds[0]
_, _, _ = G.create_note(evaluation_id=e["id"], etudid=etud["etudid"], note=12.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_rat["id"], etudid=etud["etudid"], note=11.0)
# --- Vérifications internes structures ScoDoc # --- Vérifications internes structures ScoDoc
formsemestre = db.session.get(FormSemestre, formsemestre_id) 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: # Note moyenne: ici le ratrapage est inférieur à la note:
assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(12.0) assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(12.0)
# rattrapage > moyenne: # 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( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )
assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(18.0) assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(18.0)
# rattrapage vs absences # rattrapage vs absences
_, _, _ = G.create_note( _ = G.create_note(evaluation_id=e["id"], etudid=etud["etudid"], note=None) # abs
evaluation_id=e["id"], etudid=etud["etudid"], note=None _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["etudid"], note=17.0)
) # abs
_, _, _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["etudid"], note=17.0)
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )
assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(17.0) assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(17.0)
# et sans note de rattrapage # et sans note de rattrapage
_, _, _ = G.create_note(evaluation_id=e["id"], etudid=etud["etudid"], note=10.0) _ = 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_rat["id"], etudid=etud["etudid"], note=None)
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )
@ -159,18 +157,14 @@ def test_notes_rattrapage(test_client):
assert len(mod_res.get_evaluations_completes(moduleimpl)) == 2 assert len(mod_res.get_evaluations_completes(moduleimpl)) == 2
# Saisie note session 2: # Saisie note session 2:
_, _, _ = G.create_note( _ = G.create_note(evaluation_id=e_session2["id"], etudid=etud["etudid"], note=5.0)
evaluation_id=e_session2["id"], etudid=etud["etudid"], note=5.0
)
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )
# Note moyenne: utilise session 2 même si inférieure # Note moyenne: utilise session 2 même si inférieure
assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(5.0) assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(5.0)
_, _, _ = G.create_note( _ = G.create_note(evaluation_id=e_session2["id"], etudid=etud["etudid"], note=20.0)
evaluation_id=e_session2["id"], etudid=etud["etudid"], note=20.0
)
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] 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) assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(20.0)
# Met la note session2 à ABS (None) # Met la note session2 à ABS (None)
_, _, _ = G.create_note( _ = G.create_note(evaluation_id=e_session2["id"], etudid=etud["etudid"], note=None)
evaluation_id=e_session2["id"], etudid=etud["etudid"], note=None
)
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )
# Note moyenne: zéro car ABS # Note moyenne: zéro car ABS
assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(0.0) assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(0.0)
# Supprime note session 2 # Supprime note session 2
_, _, _ = G.create_note( _ = G.create_note(
evaluation_id=e_session2["id"], etudid=etud["etudid"], note=scu.NOTES_SUPPRESS evaluation_id=e_session2["id"], etudid=etud["etudid"], note=scu.NOTES_SUPPRESS
) )
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
@ -216,18 +208,14 @@ def test_notes_rattrapage(test_client):
# Note moyenne sans bonus # Note moyenne sans bonus
assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(10.0) assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(10.0)
# Saisie note bonus # Saisie note bonus
_, _, _ = G.create_note( _ = G.create_note(evaluation_id=e_bonus["id"], etudid=etud["etudid"], note=1.0)
evaluation_id=e_bonus["id"], etudid=etud["etudid"], note=1.0
)
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )
# Note moyenne sans bonus # Note moyenne sans bonus
assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(11.0) assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(11.0)
# Négatif, avec clip à zéro # Négatif, avec clip à zéro
_, _, _ = G.create_note( _ = G.create_note(evaluation_id=e_bonus["id"], etudid=etud["etudid"], note=-20.0)
evaluation_id=e_bonus["id"], etudid=etud["etudid"], note=-20.0
)
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] 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 # --- Saisie toutes les notes de l'évaluation
for idx, etud in enumerate(etuds): 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, evaluation_id=e1.id,
etudid=etud["etudid"], etudid=etud["etudid"],
note=NOTES_T[idx % len(NOTES_T)], 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 not existing_decisions
assert nb_suppress == 0 assert nb_suppress == 0
assert len(etudids_changed) == 1 assert len(etudids_changed) == 1
assert messages == []
# --- Vérifie que les notes sont prises en compte: # --- Vérifie que les notes sont prises en compte:
b = sco_bulletins.formsemestre_bulletinetud_dict(formsemestre_id, etud["etudid"]) 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() db.session.commit()
# Saisie les notes des 5 premiers étudiants: # Saisie les notes des 5 premiers étudiants:
for idx, etud in enumerate(etuds[:5]): 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, evaluation_id=e2.id,
etudid=etud["etudid"], etudid=etud["etudid"],
note=NOTES_T[idx % len(NOTES_T)], note=NOTES_T[idx % len(NOTES_T)],
) )
assert messages == []
# Cette éval n'est pas complète # Cette éval n'est pas complète
etat = sco_evaluations.do_evaluation_etat(e2.id) etat = sco_evaluations.do_evaluation_etat(e2.id)
assert etat["evalcomplete"] is False assert etat["evalcomplete"] is False
@ -162,11 +164,12 @@ def run_sco_basic(verbose=False, dept=None) -> FormSemestre:
# Saisie des notes qui manquent: # Saisie des notes qui manquent:
for idx, etud in enumerate(etuds[5:]): 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, evaluation_id=e2.id,
etudid=etud["etudid"], etudid=etud["etudid"],
note=NOTES_T[idx % len(NOTES_T)], note=NOTES_T[idx % len(NOTES_T)],
) )
assert messages == []
etat = sco_evaluations.do_evaluation_etat(e2.id) etat = sco_evaluations.do_evaluation_etat(e2.id)
assert etat["evalcomplete"] assert etat["evalcomplete"]
assert etat["nb_att"] == 0 assert etat["nb_att"] == 0