1
0
Fork 0

min/max evals sur bul. json classic. + Tests unitaires bulletin.

This commit is contained in:
Emmanuel Viennet 2022-11-22 13:13:16 +01:00
parent 9dbcd2c8a2
commit 6838c970a4
16 changed files with 331 additions and 171 deletions

View File

@ -101,7 +101,7 @@ Certains tests ont besoin d'un département déjà créé, qui n'est pas créé
scripts de tests: scripts de tests:
Lancer au préalable: Lancer au préalable:
flask delete-dept TEST00 && flask create-dept TEST00 flask delete-dept -fy TEST00 && flask create-dept TEST00
Puis dérouler les tests unitaires: Puis dérouler les tests unitaires:

View File

@ -435,8 +435,6 @@ def initialize_scodoc_database(erase=False, create_all=False):
SQL tables and functions. SQL tables and functions.
If erase is True, _erase_ all database content. If erase is True, _erase_ all database content.
""" """
from app import models
# - ERASE (the truncation sql function has been defined above) # - ERASE (the truncation sql function has been defined above)
if erase: if erase:
truncate_database() truncate_database()

View File

@ -43,6 +43,7 @@ import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
from app.scodoc import sco_abs from app.scodoc import sco_abs
from app.scodoc import sco_edit_ue from app.scodoc import sco_edit_ue
from app.scodoc import sco_evaluations
from app.scodoc import sco_evaluation_db from app.scodoc import sco_evaluation_db
from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups from app.scodoc import sco_groups
@ -84,13 +85,14 @@ def formsemestre_bulletinetud_published_dict(
xml_nodate=False, xml_nodate=False,
xml_with_decisions=False, # inclue les decisions même si non publiées xml_with_decisions=False, # inclue les decisions même si non publiées
version="long", version="long",
): ) -> dict:
"""Dictionnaire representant les informations _publiees_ du bulletin de notes """Dictionnaire representant les informations _publiees_ du bulletin de notes
Utilisé pour JSON, devrait l'être aussi pour XML. (todo) Utilisé pour JSON, devrait l'être aussi pour XML. (todo)
""" """
from app.scodoc import sco_bulletins from app.scodoc import sco_bulletins
formsemestre = FormSemestre.query.get_or_404(formsemestre_id) formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
prefs = sco_preferences.SemPreferences(formsemestre_id)
etud = Identite.query.get(etudid) etud = Identite.query.get(etudid)
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
@ -158,11 +160,8 @@ def formsemestre_bulletinetud_published_dict(
ues = nt.get_ues_stat_dict() ues = nt.get_ues_stat_dict()
modimpls = nt.get_modimpls_dict() modimpls = nt.get_modimpls_dict()
nbetuds = len(nt.etud_moy_gen_ranks) nbetuds = len(nt.etud_moy_gen_ranks)
mg = scu.fmt_note(nt.get_etud_moy_gen(etudid)) moy_gen = scu.fmt_note(nt.get_etud_moy_gen(etudid))
if ( if nt.get_moduleimpls_attente() or not prefs["bul_show_rangs"]:
nt.get_moduleimpls_attente()
or sco_preferences.get_preference("bul_show_rangs", formsemestre_id) == 0
):
# n'affiche pas le rang sur le bulletin s'il y a des # n'affiche pas le rang sur le bulletin s'il y a des
# notes en attente dans ce semestre # notes en attente dans ce semestre
rang = "" rang = ""
@ -175,7 +174,7 @@ def formsemestre_bulletinetud_published_dict(
) )
d["note"] = dict( d["note"] = dict(
value=mg, value=moy_gen,
min=scu.fmt_note(nt.moy_min), min=scu.fmt_note(nt.moy_min),
max=scu.fmt_note(nt.moy_max), max=scu.fmt_note(nt.moy_max),
moy=scu.fmt_note(nt.moy_moy), moy=scu.fmt_note(nt.moy_moy),
@ -217,9 +216,7 @@ def formsemestre_bulletinetud_published_dict(
value=scu.fmt_note(ue_status["cur_moy_ue"] if ue_status else ""), value=scu.fmt_note(ue_status["cur_moy_ue"] if ue_status else ""),
min=scu.fmt_note(ue["min"]), min=scu.fmt_note(ue["min"]),
max=scu.fmt_note(ue["max"]), max=scu.fmt_note(ue["max"]),
moy=scu.fmt_note( moy=scu.fmt_note(ue["moy"]),
ue["moy"]
), # CM : ajout pour faire apparaitre la moyenne des UE
), ),
rang=rang, rang=rang,
effectif=effectif, effectif=effectif,
@ -259,10 +256,7 @@ def formsemestre_bulletinetud_published_dict(
m["note"][k] = scu.fmt_note(m["note"][k]) m["note"][k] = scu.fmt_note(m["note"][k])
u["module"].append(m) u["module"].append(m)
if ( if prefs["bul_show_mod_rangs"] and nt.mod_rangs is not None:
sco_preferences.get_preference("bul_show_mod_rangs", formsemestre_id)
and nt.mod_rangs is not None
):
m["rang"] = dict( m["rang"] = dict(
value=nt.mod_rangs[modimpl["moduleimpl_id"]][0][etudid] value=nt.mod_rangs[modimpl["moduleimpl_id"]][0][etudid]
) )
@ -274,33 +268,40 @@ def formsemestre_bulletinetud_published_dict(
if version != "short": if version != "short":
for e in evals: for e in evals:
if e["visibulletin"] or version == "long": if e["visibulletin"] or version == "long":
val = e["notes"].get(etudid, {"value": "NP"})[ val = e["notes"].get(etudid, {"value": "NP"})["value"]
"value" # nb: val est NA si etud démissionnaire
] # NA si etud demissionnaire
val = scu.fmt_note(val, note_max=e["note_max"]) val = scu.fmt_note(val, note_max=e["note_max"])
m["evaluation"].append( eval_dict = dict(
dict( jour=ndb.DateDMYtoISO(e["jour"], null_is_empty=True),
jour=ndb.DateDMYtoISO(e["jour"], null_is_empty=True), heure_debut=ndb.TimetoISO8601(
heure_debut=ndb.TimetoISO8601( e["heure_debut"], null_is_empty=True
e["heure_debut"], null_is_empty=True ),
), heure_fin=ndb.TimetoISO8601(
heure_fin=ndb.TimetoISO8601( e["heure_fin"], null_is_empty=True
e["heure_fin"], null_is_empty=True ),
), coefficient=e["coefficient"],
coefficient=e["coefficient"], evaluation_type=e["evaluation_type"],
evaluation_type=e["evaluation_type"], # CM : ajout pour permettre de faire le lien sur
evaluation_id=e[ # les bulletins en ligne avec l'évaluation:
"evaluation_id" evaluation_id=e["evaluation_id"],
], # CM : ajout pour permettre de faire le lien sur les bulletins en ligne avec l'évaluation description=quote_xml_attr(e["description"]),
description=quote_xml_attr(e["description"]), note=val,
note=val,
)
) )
if prefs["bul_show_minmax_eval"] or prefs["bul_show_moypromo"]:
etat = sco_evaluations.do_evaluation_etat(
e["evaluation_id"]
)
if prefs["bul_show_minmax_eval"]:
eval_dict["min"] = scu.fmt_note(etat["mini"])
eval_dict["max"] = scu.fmt_note(etat["maxi"])
if prefs["bul_show_moypromo"]:
eval_dict["moy"] = scu.fmt_note(etat["moy"])
m["evaluation"].append(eval_dict)
# Evaluations incomplètes ou futures: # Evaluations incomplètes ou futures:
complete_eval_ids = set([e["evaluation_id"] for e in evals]) complete_eval_ids = set([e["evaluation_id"] for e in evals])
if sco_preferences.get_preference( if prefs["bul_show_all_evals"]:
"bul_show_all_evals", formsemestre_id
):
all_evals = sco_evaluation_db.do_evaluation_list( all_evals = sco_evaluation_db.do_evaluation_list(
args={"moduleimpl_id": modimpl["moduleimpl_id"]} args={"moduleimpl_id": modimpl["moduleimpl_id"]}
) )
@ -344,7 +345,7 @@ def formsemestre_bulletinetud_published_dict(
) )
# --- Absences # --- Absences
if sco_preferences.get_preference("bul_show_abs", formsemestre_id): if prefs["bul_show_abs"]:
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem) nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
d["absences"] = dict(nbabs=nbabs, nbabsjust=nbabsjust) d["absences"] = dict(nbabs=nbabs, nbabsjust=nbabsjust)
@ -403,17 +404,14 @@ def dict_decision_jury(
""" """
from app.scodoc import sco_bulletins from app.scodoc import sco_bulletins
prefs = sco_preferences.SemPreferences(formsemestre.id)
d = {} d = {}
if ( if prefs["bul_show_decision"] or with_decisions:
sco_preferences.get_preference("bul_show_decision", formsemestre.id)
or with_decisions
):
infos, dpv = sco_bulletins.etud_descr_situation_semestre( infos, dpv = sco_bulletins.etud_descr_situation_semestre(
etud.id, etud.id,
formsemestre.id, formsemestre.id,
show_uevalid=sco_preferences.get_preference( show_uevalid=prefs["bul_show_uevalid"],
"bul_show_uevalid", formsemestre.id
),
) )
d["situation"] = infos["situation"] d["situation"] = infos["situation"]
if dpv: if dpv:

View File

@ -46,11 +46,12 @@ de la forme %(XXX)s sont remplacées par la valeur de XXX, pour XXX dans:
Balises img: actuellement interdites. Balises img: actuellement interdites.
""" """
from flask import url_for, g
from reportlab.platypus import KeepTogether, Paragraph, Spacer, Table from reportlab.platypus import KeepTogether, Paragraph, Spacer, Table
from reportlab.lib.units import cm, mm from reportlab.lib.units import cm, mm
from reportlab.lib.colors import Color, blue from reportlab.lib.colors import Color, blue
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.scodoc.sco_pdf import SU, make_paras from app.scodoc.sco_pdf import SU, make_paras
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
@ -434,7 +435,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
plusminus = pluslink plusminus = pluslink
try: try:
ects_txt = str(int(ue["ects"])) ects_txt = str(int(ue["ects"]))
except: except (ValueError, KeyError):
ects_txt = "-" ects_txt = "-"
t = { t = {
@ -602,12 +603,14 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
"rang": mod["mod_rang_txt"], # vide si pas option rang "rang": mod["mod_rang_txt"], # vide si pas option rang
"note": mod["mod_moy_txt"], "note": mod["mod_moy_txt"],
"coef": mod["mod_coef_txt"] if prefs["bul_show_coef"] else "", "coef": mod["mod_coef_txt"] if prefs["bul_show_coef"] else "",
"abs": mod.get( # vide si pas option show abs module:
"mod_abs_txt", "" "abs": mod.get("mod_abs_txt", ""),
), # absent si pas option show abs module "_css_row_class": f"notes_bulletin_row_mod{rowstyle}",
"_css_row_class": "notes_bulletin_row_mod%s" % rowstyle, "_titre_target": url_for(
"_titre_target": "moduleimpl_status?moduleimpl_id=%s" "notes.moduleimpl_status",
% mod["moduleimpl_id"], scodoc_dept=g.scodoc_dept,
moduleimpl_id=mod["moduleimpl_id"],
),
"_titre_help": mod["mod_descr_txt"], "_titre_help": mod["mod_descr_txt"],
"_hidden": hidden, "_hidden": hidden,
"_pdf_style": pdf_style, "_pdf_style": pdf_style,

View File

@ -2034,7 +2034,10 @@ class BasePreferences(object):
if modif: if modif:
sco_cache.invalidate_formsemestre() sco_cache.invalidate_formsemestre()
def set(self, formsemestre_id, name, value): def set(self, formsemestre_id: int, name: str, value: str):
"""Set and save a preference value.
If formsemestre_id is None, global pref.
"""
if not name or name[0] == "_" or name not in self.prefs_name: if not name or name[0] == "_" or name not in self.prefs_name:
raise ValueError(f"invalid preference name: {name}") raise ValueError(f"invalid preference name: {name}")
if formsemestre_id and name in self.prefs_only_global: if formsemestre_id and name in self.prefs_only_global:

View File

@ -39,7 +39,7 @@ from flask_login import current_user
from app.comp import res_sem from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat from app.comp.res_compat import NotesTableCompat
from app.models import Evaluation, FormSemestre from app.models import Evaluation, FormSemestre
from app.models import ScolarNews from app.models import ModuleImpl, ScolarNews
from app.models.etudiants import Identite from app.models.etudiants import Identite
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import ModuleType from app.scodoc.sco_utils import ModuleType
@ -216,7 +216,8 @@ def do_evaluation_upload_xls():
eval_id = None eval_id = None
if eval_id != evaluation_id: if eval_id != evaluation_id:
diag.append( diag.append(
f"Erreur: fichier invalide: le code d'évaluation de correspond pas ! ('{eval_id_str}' != '{evaluation_id}')" f"""Erreur: fichier invalide: le code d'évaluation de correspond pas ! ('{
eval_id_str}' != '{evaluation_id}')"""
) )
raise InvalidNoteValue() raise InvalidNoteValue()
# --- get notes -> list (etudid, value) # --- get notes -> list (etudid, value)
@ -238,15 +239,14 @@ def do_evaluation_upload_xls():
ni += 1 ni += 1
except: except:
diag.append( diag.append(
'Erreur: Ligne invalide ! (erreur ligne %d)<br>"%s"' f"""Erreur: Ligne invalide ! (erreur ligne {ni})<br>{lines[ni]}"""
% (ni, str(lines[ni]))
) )
raise InvalidNoteValue() raise InvalidNoteValue()
# -- check values # -- check values
L, invalids, withoutnotes, absents, _ = _check_notes(notes, E, M["module"]) L, invalids, withoutnotes, absents, _ = _check_notes(notes, E, M["module"])
if len(invalids): if len(invalids):
diag.append( diag.append(
"Erreur: la feuille contient %d notes invalides</p>" % len(invalids) f"Erreur: la feuille contient {len(invalids)} notes invalides</p>"
) )
if len(invalids) < 25: if len(invalids) < 25:
etudsnames = [ etudsnames = [
@ -424,25 +424,34 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
evaluation_id, by_uid=current_user.id evaluation_id, by_uid=current_user.id
) )
else: else:
raise AccessDenied("Modification des notes impossible pour %s" % current_user) raise AccessDenied(f"Modification des notes impossible pour {current_user}")
notes = [(etudid, scu.NOTES_SUPPRESS) for etudid in notes_db.keys()] notes = [(etudid, scu.NOTES_SUPPRESS) for etudid in notes_db.keys()]
status_url = url_for(
"notes.moduleimpl_status",
scodoc_dept=g.scodoc_dept,
moduleimpl_id=E["moduleimpl_id"],
)
if not dialog_confirmed: if not dialog_confirmed:
nb_changed, nb_suppress, existing_decisions = notes_add( nb_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 = ( msg = f"""<p>Confirmer la suppression des {nb_suppress} notes ?
"<p>Confirmer la suppression des %d notes ? <em>(peut affecter plusieurs groupes)</em></p>" <em>(peut affecter plusieurs groupes)</em>
% nb_suppress </p>
) """
if existing_decisions: if existing_decisions:
msg += """<p class="warning">Important: il y a déjà des décisions de jury enregistrées, qui seront potentiellement à revoir suite à cette modification !</p>""" msg += """<p class="warning">Important: il y a déjà des décisions de
jury enregistrées, qui seront potentiellement à revoir suite à
cette modification !</p>"""
return scu.confirm_dialog( return scu.confirm_dialog(
msg, msg,
dest_url="", dest_url="",
OK="Supprimer les notes", OK="Supprimer les notes",
cancel_url="moduleimpl_status?moduleimpl_id=%s" % E["moduleimpl_id"], cancel_url=status_url,
parameters={"evaluation_id": evaluation_id}, parameters={"evaluation_id": evaluation_id},
) )
@ -455,26 +464,28 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
check_inscription=False, check_inscription=False,
) )
assert nb_changed == nb_suppress assert nb_changed == nb_suppress
H = ["<p>%s notes supprimées</p>" % nb_suppress] H = [f"""<p>{nb_suppress} notes supprimées</p>"""]
if existing_decisions: if existing_decisions:
H.append( H.append(
"""<p class="warning">Important: il y avait déjà des décisions de jury enregistrées, qui sont potentiellement à revoir suite à cette modification !</p>""" """<p class="warning">Important: il y avait déjà des décisions
de jury enregistrées, qui sont potentiellement à revoir suite
à cette modification !
</p>"""
) )
H += [ H += [
'<p><a class="stdlink" href="moduleimpl_status?moduleimpl_id=%s">continuer</a>' f"""<p><a class="stdlink" href="{status_url}">continuer</a>
% E["moduleimpl_id"] """
] ]
# news # news
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] modimpl = ModuleImpl.query.get(E["moduleimpl_id"])
mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
mod["moduleimpl_id"] = M["moduleimpl_id"]
mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
ScolarNews.add( ScolarNews.add(
typ=ScolarNews.NEWS_NOTE, typ=ScolarNews.NEWS_NOTE,
obj=M["moduleimpl_id"], obj=modimpl.id,
text='Suppression des notes d\'une évaluation dans <a href="%(url)s">%(titre)s</a>' text=f"""Suppression des notes d'une évaluation dans
% mod, <a class="stdlink" href="{status_url}"
url=mod["url"], >{modimpl.module.titre or 'module sans titre'}</a>
""",
url=status_url,
) )
return html_sco_header.sco_header() + "\n".join(H) + html_sco_header.sco_footer() return html_sco_header.sco_header() + "\n".join(H) + html_sco_header.sco_footer()
@ -555,7 +566,7 @@ def notes_add(
oldval = notes_db[etudid]["value"] oldval = notes_db[etudid]["value"]
if type(value) != type(oldval): if type(value) != type(oldval):
changed = True changed = True
elif type(value) == type(1.0) and ( elif type(value) == float and (
abs(value - oldval) > scu.NOTES_PRECISION abs(value - oldval) > scu.NOTES_PRECISION
): ):
changed = True changed = True
@ -566,10 +577,10 @@ def notes_add(
if do_it: if do_it:
cursor.execute( cursor.execute(
"""INSERT INTO notes_notes_log """INSERT INTO notes_notes_log
(etudid,evaluation_id,value,comment,date,uid) (etudid,evaluation_id,value,comment,date,uid)
SELECT etudid, evaluation_id, value, comment, date, uid SELECT etudid, evaluation_id, value, comment, date, uid
FROM notes_notes FROM notes_notes
WHERE etudid=%(etudid)s WHERE etudid=%(etudid)s
and evaluation_id=%(evaluation_id)s and evaluation_id=%(evaluation_id)s
""", """,
{"etudid": etudid, "evaluation_id": evaluation_id}, {"etudid": etudid, "evaluation_id": evaluation_id},
@ -588,7 +599,7 @@ def notes_add(
cursor.execute( cursor.execute(
"""UPDATE notes_notes """UPDATE notes_notes
SET value=%(value)s, comment=%(comment)s, date=%(date)s, uid=%(uid)s SET value=%(value)s, comment=%(comment)s, date=%(date)s, uid=%(uid)s
WHERE etudid = %(etudid)s WHERE etudid = %(etudid)s
and evaluation_id = %(evaluation_id)s and evaluation_id = %(evaluation_id)s
""", """,
aa, aa,
@ -609,7 +620,7 @@ def notes_add(
# garde trace de la suppression dans l'historique: # garde trace de la suppression dans l'historique:
aa["value"] = scu.NOTES_SUPPRESS aa["value"] = scu.NOTES_SUPPRESS
cursor.execute( cursor.execute(
"""INSERT INTO notes_notes_log (etudid,evaluation_id,value,comment,date,uid) """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) VALUES (%(etudid)s, %(evaluation_id)s, %(value)s, %(comment)s, %(date)s, %(uid)s)
""", """,
aa, aa,

View File

@ -651,7 +651,9 @@ def index_html():
</ul> </ul>
<h3>Référentiels de compétences</h3> <h3>Référentiels de compétences</h3>
<ul> <ul>
<li><a class="stdlink" href="{url_for('notes.refcomp_table', scodoc_dept=g.scodoc_dept)}">Liste des référentiels chargés</a> <li><a class="stdlink" href="{
url_for('notes.refcomp_table', scodoc_dept=g.scodoc_dept)
}">Liste des référentiels chargés</a>
</li> </li>
</ul> </ul>

View File

@ -349,6 +349,7 @@ def abort_if_false(ctx, param, value):
@app.cli.command() @app.cli.command()
@click.option( @click.option(
"-y",
"--yes", "--yes",
is_flag=True, is_flag=True,
callback=abort_if_false, callback=abort_if_false,

View File

@ -38,7 +38,7 @@ def test_client():
u.add_role(admin_role, TestConfig.DEPT_TEST) u.add_role(admin_role, TestConfig.DEPT_TEST)
db.session.add(u) db.session.add(u)
db.session.commit() db.session.commit()
# Creation département de Test # Création département de Test
d = models.Departement(acronym=TestConfig.DEPT_TEST) d = models.Departement(acronym=TestConfig.DEPT_TEST)
db.session.add(d) db.session.add(d)
db.session.commit() db.session.commit()

View File

@ -38,14 +38,28 @@ from app.scodoc import sco_utils as scu
from app import log from app import log
from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_exceptions import ScoValueError
from tests.unit.setup import NOTES_T
random.seed(12345) # tests reproductibles random.seed(12345) # tests reproductibles
NOMS_DIR = Config.SCODOC_DIR + "/tools/fakeportal/nomsprenoms" NOMS_DIR = Config.SCODOC_DIR + "/tools/fakeportal/nomsprenoms"
NOMS = [x.strip() for x in open(NOMS_DIR + "/noms.txt").readlines()] NOMS = [
PRENOMS_H = [x.strip() for x in open(NOMS_DIR + "/prenoms-h.txt").readlines()] x.strip()
PRENOMS_F = [x.strip() for x in open(NOMS_DIR + "/prenoms-f.txt").readlines()] for x in open(NOMS_DIR + "/noms.txt", encoding=scu.SCO_ENCODING).readlines()
PRENOMS_X = [x.strip() for x in open(NOMS_DIR + "/prenoms-x.txt").readlines()] ]
PRENOMS_H = [
x.strip()
for x in open(NOMS_DIR + "/prenoms-h.txt", encoding=scu.SCO_ENCODING).readlines()
]
PRENOMS_F = [
x.strip()
for x in open(NOMS_DIR + "/prenoms-f.txt", encoding=scu.SCO_ENCODING).readlines()
]
PRENOMS_X = [
x.strip()
for x in open(NOMS_DIR + "/prenoms-x.txt", encoding=scu.SCO_ENCODING).readlines()
]
def id_generator(size=6, chars=string.ascii_uppercase + string.digits): def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
@ -297,18 +311,18 @@ class ScoFake(object):
@logging_meth @logging_meth
def create_note( def create_note(
self, self,
evaluation=None, evaluation_id: int = None,
etud=None, etudid: int = None,
note=None, note=None,
comment=None, comment: str = None,
user=None, # User instance user: User = None, # User instance
): ):
if user is None: if user is None:
user = self.default_user user = self.default_user
return sco_saisie_notes.notes_add( return sco_saisie_notes.notes_add(
user, user,
evaluation["evaluation_id"], evaluation_id,
[(etud["etudid"], note)], [(etudid, note)],
comment=comment, comment=comment,
) )
@ -393,24 +407,24 @@ class ScoFake(object):
eval_list.append(e) eval_list.append(e)
return formsemestre_id, eval_list return formsemestre_id, eval_list
def set_etud_notes_sem( def set_etud_notes_evals(
self, sem, eval_list, etuds, notes=None, random_min=0, random_max=20 self, eval_list: list[dict], etuds: list[dict], notes=None
): ):
"""Met des notes aux étudiants indiqués des evals indiquées. """Met des notes aux étudiants indiqués des evals indiquées.
Args: Args:
sem: dict
eval_list: list of dicts
etuds: list of dicts
notes: liste des notes (float). notes: liste des notes (float).
Si non spécifié, tire au hasard dans `[random_min, random_max]` Si non spécifié, utilise la liste NOTES_T
""" """
set_random = notes is None if notes is None:
notes = NOTES_T
for e in eval_list: for e in eval_list:
if set_random: for idx, etud in enumerate(etuds):
notes = [float(random.randint(random_min, random_max)) for _ in etuds] self.create_note(
for etud, note in zip(etuds, notes): evaluation_id=e["id"],
self.create_note(evaluation=e, etud=etud, note=note) etudid=etud["id"],
note=notes[idx % len(notes)],
)
def set_code_jury( def set_code_jury(
self, self,

View File

@ -3,12 +3,16 @@ Quelques fonctions d'initialisation pour tests unitaires
""" """
from tests.unit import sco_fake_gen from tests.unit import sco_fake_gen
from app import db
from app import models from app import models
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.scodoc import sco_codes_parcours from app.scodoc import sco_codes_parcours
# Valeurs des notes saisies par les tests:
NOTES_T = [
float(x) for x in (20, 0, 10, 13 / 7.0, 12.5, 24.0 / 11) + tuple(range(1, 19))
]
def build_formation_test( def build_formation_test(
nb_mods=1, parcours=sco_codes_parcours.ParcoursBUT, with_ue_sport=False nb_mods=1, parcours=sco_codes_parcours.ParcoursBUT, with_ue_sport=False

View File

@ -0,0 +1,95 @@
"""Tests unitaires : bulletins de notes
Utiliser comme:
pytest tests/unit/test_sco_basic.py
Au besoin, créer un base de test neuve:
./tools/create_database.sh SCODOC_TEST
"""
from app.models import FormSemestre, Identite
from config import TestConfig
import app
from app.scodoc import sco_bulletins_json
from app.scodoc import sco_preferences
from tests.unit import sco_fake_gen
from tests.unit import test_sco_basic
DEPT = TestConfig.DEPT_TEST
def test_bulletin(test_client):
"""Vérifications sur les bulletins de notes"""
G = sco_fake_gen.ScoFake(verbose=False)
app.set_sco_dept(DEPT)
formsemestre = test_sco_basic.run_sco_basic()
modimpl = formsemestre.modimpls.first()
evaluation = modimpl.evaluations.first()
# S'assure qu'on a bien une formation classique:
assert formsemestre.formation.is_apc() is False
etud: Identite = formsemestre.etuds.first()
assert etud
# Ici on a un modimpl avec 9 inscrits, 2 evals ayant toutes leurs notes
# Vérification des min/max évaluation sur le bulletin
bul = sco_bulletins_json.formsemestre_bulletinetud_published_dict(
formsemestre.id,
etud.id,
force_publishing=True,
xml_with_decisions=True,
)
assert isinstance(bul, dict)
assert bul["type"] == "classic"
modules_res = bul["ue"][0]["module"]
assert len(modules_res) == 1 # 1 seul module complet
module_res = modules_res[0]
assert modimpl.module.code == module_res["code"]
assert len(module_res["evaluation"]) == 2
note_eval_1 = module_res["evaluation"][0]
assert "note" in note_eval_1
assert "min" not in note_eval_1
assert not sco_preferences.get_preference("bul_show_minmax_eval")
# Change préférence pour avoir min/max évaluation
prefs = sco_preferences.get_base_preferences()
prefs.set(None, "bul_show_minmax_eval", True)
assert sco_preferences.get_preference("bul_show_minmax_eval")
# Redemande le bulletin
bul = sco_bulletins_json.formsemestre_bulletinetud_published_dict(
formsemestre.id,
etud.id,
force_publishing=True,
xml_with_decisions=True,
)
note_eval_1 = bul["ue"][0]["module"][0]["evaluation"][0]
assert "min" in note_eval_1
assert "max" in note_eval_1
min_eval_1 = float(note_eval_1["min"])
max_eval_1 = float(note_eval_1["max"])
# la valeur actuelle est 12.34, on s'assure qu'elle n'est pas extrême:
assert min_eval_1 > 0
assert max_eval_1 < 20
# Saisie note pour changer min/max:
# Met le max à 20:
G.create_note(evaluation_id=evaluation.id, etudid=etud.id, note=20.0)
bul = sco_bulletins_json.formsemestre_bulletinetud_published_dict(
formsemestre.id,
etud.id,
force_publishing=True,
xml_with_decisions=True,
)
note_eval_1 = bul["ue"][0]["module"][0]["evaluation"][0]
assert note_eval_1["max"] == "20.00"
# Met le min à zero:
G.create_note(evaluation_id=evaluation.id, etudid=etud.id, note=0.0)
bul = sco_bulletins_json.formsemestre_bulletinetud_published_dict(
formsemestre.id,
etud.id,
force_publishing=True,
xml_with_decisions=True,
)
note_eval_1 = bul["ue"][0]["module"][0]["evaluation"][0]
assert note_eval_1["min"] == "00.00"

View File

@ -11,7 +11,6 @@ from flask import g
import app import app
from app import db from app import db
from app.models import Departement, ScoPreference, FormSemestre, formsemestre from app.models import Departement, ScoPreference, FormSemestre, formsemestre
from app.scodoc import notesdb as ndb
from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from tests.unit import test_sco_basic from tests.unit import test_sco_basic

View File

@ -108,10 +108,14 @@ 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(evaluation=e1, etud=etuds[0], note=note_1) _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etuds[0]["id"], note=note_1)
_, _, _ = G.create_note(evaluation=e2, etud=etuds[0], note=note_2) _, _, _ = G.create_note(evaluation_id=e2["id"], etudid=etuds[0]["id"], note=note_2)
_, _, _ = G.create_note(evaluation=e1, etud=etuds[1], note=note_1 / 2) _, _, _ = G.create_note(
_, _, _ = G.create_note(evaluation=e2, etud=etuds[1], note=note_2 / 3) evaluation_id=e1["id"], etudid=etuds[1]["id"], note=note_1 / 2
)
_, _, _ = G.create_note(
evaluation_id=e2["id"], etudid=etuds[1]["id"], note=note_2 / 3
)
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )
@ -132,16 +136,16 @@ def test_notes_modules(test_client):
) )
# Absence à une évaluation # Absence à une évaluation
_, _, _ = G.create_note(evaluation=e1, etud=etud, note=None) # abs _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etudid, note=None) # abs
_, _, _ = G.create_note(evaluation=e2, etud=etud, note=note_2) _, _, _ = G.create_note(evaluation_id=e2["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(evaluation=e1, etud=etud, note=None) # abs _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etudid, note=None) # abs
_, _, _ = G.create_note(evaluation=e2, etud=etud, note=None) # abs _, _, _ = G.create_note(evaluation_id=e2["id"], etudid=etudid, note=None) # abs
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )
@ -156,8 +160,10 @@ def test_notes_modules(test_client):
) )
# Note excusée EXC <-> scu.NOTES_NEUTRALISE # Note excusée EXC <-> scu.NOTES_NEUTRALISE
_, _, _ = G.create_note(evaluation=e1, etud=etud, note=note_1) _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etudid, note=note_1)
_, _, _ = G.create_note(evaluation=e2, etud=etud, note=scu.NOTES_NEUTRALISE) # EXC _, _, _ = G.create_note(
evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
) # EXC
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )
@ -171,8 +177,10 @@ 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(evaluation=e1, etud=etud, note=note_1) _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etudid, note=note_1)
_, _, _ = G.create_note(evaluation=e2, etud=etud, note=scu.NOTES_ATTENTE) # ATT _, _, _ = G.create_note(
evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_ATTENTE
) # ATT
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )
@ -186,8 +194,12 @@ 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(evaluation=e1, etud=etud, note=scu.NOTES_NEUTRALISE) # EXC _, _, _ = G.create_note(
_, _, _ = G.create_note(evaluation=e2, etud=etud, note=scu.NOTES_NEUTRALISE) # EXC evaluation_id=e1["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
) # EXC
_, _, _ = G.create_note(
evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
) # EXC
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )
@ -201,8 +213,12 @@ 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(evaluation=e1, etud=etud, note=scu.NOTES_ATTENTE) # ATT _, _, _ = G.create_note(
_, _, _ = G.create_note(evaluation=e2, etud=etud, note=scu.NOTES_ATTENTE) # ATT evaluation_id=e1["id"], etudid=etudid, note=scu.NOTES_ATTENTE
) # ATT
_, _, _ = G.create_note(
evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_ATTENTE
) # ATT
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )
@ -261,7 +277,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=e1, etud=etud, note=12.5) _, _, _ = G.create_note(evaluation_id=e1["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)
@ -289,7 +305,7 @@ def test_notes_modules(test_client):
description="evaluation mod 2", description="evaluation mod 2",
coefficient=1.0, coefficient=1.0,
) )
_, _, _ = G.create_note(evaluation=e_m2, etud=etud, note=19.5) _, _, _ = G.create_note(evaluation_id=e_m2["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)
@ -297,12 +313,16 @@ 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(evaluation=e1, etud=etud, note=scu.NOTES_NEUTRALISE) # EXC _, _, _ = G.create_note(
_, _, _ = G.create_note(evaluation=e2, etud=etud, note=scu.NOTES_NEUTRALISE) # EXC evaluation_id=e1["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
_, _, _ = G.create_note(evaluation=e_m2, etud=etud, note=12.5) ) # EXC
_, _, _ = G.create_note(evaluation=e1, etud=etuds[1], note=11.0) _, _, _ = G.create_note(
_, _, _ = G.create_note(evaluation=e2, etud=etuds[1], note=11.0) evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
_, _, _ = G.create_note(evaluation=e_m2, etud=etuds[1], note=11.0) ) # EXC
_, _, _ = G.create_note(evaluation_id=e_m2["id"], etudid=etudid, note=12.5)
_, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etuds[1]["id"], note=11.0)
_, _, _ = G.create_note(evaluation_id=e2["id"], etudid=etuds[1]["id"], note=11.0)
_, _, _ = G.create_note(evaluation_id=e_m2["id"], etudid=etuds[1]["id"], note=11.0)
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
@ -366,8 +386,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(evaluation=e1, etud=etuds[0], note=scu.NOTES_ATTENTE) # ATT _, _, _ = G.create_note(
_, _, _ = G.create_note(evaluation=e1, etud=etuds[1], note=scu.NOTES_ATTENTE) # ATT evaluation_id=e1["id"], etudid=etuds[0]["id"], note=scu.NOTES_ATTENTE
) # ATT
_, _, _ = G.create_note(
evaluation_id=e1["id"], etudid=etuds[1]["id"], note=scu.NOTES_ATTENTE
) # ATT
# Démission du premier étudiant # Démission du premier étudiant
sco_formsemestre_inscriptions.do_formsemestre_demission( sco_formsemestre_inscriptions.do_formsemestre_demission(
etuds[0]["etudid"], etuds[0]["etudid"],
@ -406,7 +430,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(evaluation=e1, etud=etuds[1], note=None) # ABS _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etuds[1]["id"], note=None)
nt = check_nt( nt = check_nt(
etuds[1]["etudid"], etuds[1]["etudid"],
sem["formsemestre_id"], sem["formsemestre_id"],

View File

@ -1,13 +1,11 @@
"""Test calculs rattrapages """Test calculs rattrapages
""" """
from flask import g
import app import app
from app.but.bulletin_but import *
from app.comp import res_sem from app.comp import res_sem
from app.comp.res_but import ResultatsSemestreBUT from app.comp.res_but import ResultatsSemestreBUT
from app.models import ModuleImpl from app.models import FormSemestre, ModuleImpl
from app.scodoc import ( from app.scodoc import (
sco_bulletins, sco_bulletins,
sco_evaluation_db, sco_evaluation_db,
@ -75,8 +73,8 @@ def test_notes_rattrapage(test_client):
evaluation_type=scu.EVALUATION_RATTRAPAGE, evaluation_type=scu.EVALUATION_RATTRAPAGE,
) )
etud = etuds[0] etud = etuds[0]
_, _, _ = G.create_note(evaluation=e, etud=etud, note=12.0) _, _, _ = G.create_note(evaluation_id=e["id"], etudid=etud["id"], note=12.0)
_, _, _ = G.create_note(evaluation=e_rat, etud=etud, note=11.0) _, _, _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["id"], note=11.0)
# --- Vérifications internes structures ScoDoc # --- Vérifications internes structures ScoDoc
formsemestre = FormSemestre.query.get(formsemestre_id) formsemestre = FormSemestre.query.get(formsemestre_id)
@ -100,21 +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=e_rat, etud=etud, note=18.0) _, _, _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["id"], 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(evaluation=e, etud=etud, note=None) # abs _, _, _ = G.create_note(evaluation_id=e["id"], etudid=etud["id"], note=None) # abs
_, _, _ = G.create_note(evaluation=e_rat, etud=etud, note=17.0) _, _, _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["id"], 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=e, etud=etud, note=10.0) # abs _, _, _ = G.create_note(evaluation_id=e["id"], etudid=etud["id"], note=10.0)
_, _, _ = G.create_note(evaluation=e_rat, etud=etud, note=None) _, _, _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["id"], note=None)
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )
@ -156,21 +154,25 @@ 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(evaluation=e_session2, etud=etud, note=5.0) _, _, _ = G.create_note(evaluation_id=e_session2["id"], etudid=etud["id"], 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(evaluation=e_session2, etud=etud, note=20.0) _, _, _ = G.create_note(
evaluation_id=e_session2["id"], etudid=etud["id"], 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"]
) )
# 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(20.0) assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(20.0)
_, _, _ = G.create_note(evaluation=e_session2, etud=etud, note=None) _, _, _ = G.create_note(
evaluation_id=e_session2["id"], etudid=etud["id"], note=None
)
b = sco_bulletins.formsemestre_bulletinetud_dict( b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"] sem["formsemestre_id"], etud["etudid"]
) )

View File

@ -11,12 +11,11 @@ Au besoin, créer un base de test neuve:
./tools/create_database.sh SCODOC_TEST ./tools/create_database.sh SCODOC_TEST
""" """
import random
from app.models import FormSemestreInscription, Identite from app.models import FormSemestreInscription, Identite
from config import TestConfig from config import TestConfig
from tests.unit import sco_fake_gen from tests.unit import sco_fake_gen
from tests.unit.setup import NOTES_T
import app import app
from app import db from app import db
@ -33,7 +32,6 @@ from app.scodoc import sco_evaluation_db
from app.scodoc import sco_formsemestre_validation from app.scodoc import sco_formsemestre_validation
from app.scodoc import sco_cursus_dut from app.scodoc import sco_cursus_dut
from app.scodoc import sco_saisie_notes from app.scodoc import sco_saisie_notes
from app.scodoc import sco_utils as scu
DEPT = TestConfig.DEPT_TEST DEPT = TestConfig.DEPT_TEST
@ -47,9 +45,10 @@ def test_sco_basic(test_client):
run_sco_basic() run_sco_basic()
def run_sco_basic(verbose=False): def run_sco_basic(verbose=False) -> FormSemestre:
"""Scénario de base: création formation, semestre, étudiants, notes, """Scénario de base: création formation, semestre, étudiants, notes,
décisions jury décisions jury
Renvoie le formsemestre créé.
""" """
G = sco_fake_gen.ScoFake(verbose=verbose) G = sco_fake_gen.ScoFake(verbose=verbose)
@ -91,11 +90,11 @@ def run_sco_basic(verbose=False):
) )
assert q.count() == 1 assert q.count() == 1
ins = q.first() ins = q.first()
assert ins.etape == None assert ins.etape is None
assert ins.etat == "I" assert ins.etat == "I"
assert ins.parcour == None assert ins.parcour is None
# --- Creation évaluation # --- Création évaluation
e = G.create_evaluation( e = G.create_evaluation(
moduleimpl_id=moduleimpl_id, moduleimpl_id=moduleimpl_id,
jour="01/01/2020", jour="01/01/2020",
@ -104,10 +103,13 @@ def run_sco_basic(verbose=False):
) )
# --- Saisie toutes les notes de l'évaluation # --- Saisie toutes les notes de l'évaluation
for etud in etuds: for idx, etud in enumerate(etuds):
nb_changed, nb_suppress, existing_decisions = G.create_note( nb_changed, nb_suppress, existing_decisions = G.create_note(
evaluation=e, etud=etud, note=float(random.randint(0, 20)) evaluation_id=e["id"], etudid=etud["id"], note=NOTES_T[idx % len(NOTES_T)]
) )
assert not existing_decisions
assert nb_suppress == 0
assert nb_changed == 1
# --- 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"])
@ -132,13 +134,13 @@ def run_sco_basic(verbose=False):
coefficient=1.0, coefficient=1.0,
) )
# Saisie les notes des 5 premiers étudiants: # Saisie les notes des 5 premiers étudiants:
for etud in etuds[:5]: for idx, etud in enumerate(etuds[:5]):
nb_changed, nb_suppress, existing_decisions = G.create_note( nb_changed, nb_suppress, existing_decisions = G.create_note(
evaluation=e2, etud=etud, note=float(random.randint(0, 20)) evaluation_id=e2["id"], etudid=etud["id"], note=NOTES_T[idx % len(NOTES_T)]
) )
# Cette éval n'est pas complète # Cette éval n'est pas complète
etat = sco_evaluations.do_evaluation_etat(e2["evaluation_id"]) etat = sco_evaluations.do_evaluation_etat(e2["evaluation_id"])
assert etat["evalcomplete"] == False assert etat["evalcomplete"] is False
# la première éval est toujours complète: # la première éval est toujours complète:
etat = sco_evaluations.do_evaluation_etat(e["evaluation_id"]) etat = sco_evaluations.do_evaluation_etat(e["evaluation_id"])
assert etat["evalcomplete"] assert etat["evalcomplete"]
@ -147,14 +149,14 @@ def run_sco_basic(verbose=False):
e2["publish_incomplete"] = True e2["publish_incomplete"] = True
sco_evaluation_db.do_evaluation_edit(e2) sco_evaluation_db.do_evaluation_edit(e2)
etat = sco_evaluations.do_evaluation_etat(e2["evaluation_id"]) etat = sco_evaluations.do_evaluation_etat(e2["evaluation_id"])
assert etat["evalcomplete"] == False assert etat["evalcomplete"] is False
assert etat["nb_att"] == 0 # il n'y a pas de notes (explicitement) en attente assert etat["nb_att"] == 0 # il n'y a pas de notes (explicitement) en attente
assert etat["evalattente"] # mais l'eval est en attente (prise en compte immédiate) assert etat["evalattente"] # mais l'eval est en attente (prise en compte immédiate)
# Saisie des notes qui manquent: # Saisie des notes qui manquent:
for etud in etuds[5:]: for idx, etud in enumerate(etuds[5:]):
nb_changed, nb_suppress, existing_decisions = G.create_note( nb_changed, nb_suppress, existing_decisions = G.create_note(
evaluation=e2, etud=etud, note=float(random.randint(0, 20)) evaluation_id=e2["id"], etudid=etud["id"], note=NOTES_T[idx % len(NOTES_T)]
) )
etat = sco_evaluations.do_evaluation_etat(e2["evaluation_id"]) etat = sco_evaluations.do_evaluation_etat(e2["evaluation_id"])
assert etat["evalcomplete"] assert etat["evalcomplete"]
@ -173,7 +175,10 @@ def run_sco_basic(verbose=False):
assert f'{etat["nb_inscrits"]} notes changées' in ans assert f'{etat["nb_inscrits"]} notes changées' in ans
etat = sco_evaluations.do_evaluation_etat(e["evaluation_id"]) etat = sco_evaluations.do_evaluation_etat(e["evaluation_id"])
assert etat["evalcomplete"] assert etat["evalcomplete"]
# --- Saisie absences
# -----------------------
# --- Saisie absences ---
# -----------------------
etudid = etuds[0]["etudid"] etudid = etuds[0]["etudid"]
_ = sco_abs_views.doSignaleAbsence( _ = sco_abs_views.doSignaleAbsence(
@ -188,8 +193,8 @@ def run_sco_basic(verbose=False):
) )
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem) nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
assert nbabs == 6, "incorrect nbabs (%d)" % nbabs assert nbabs == 6, f"incorrect nbabs ({nbabs})"
assert nbabsjust == 2, "incorrect nbabsjust (%s)" % nbabsjust assert nbabsjust == 2, f"incorrect nbabsjust ({nbabsjust})"
# --- Permission saisie notes et décisions de jury, avec ou sans démission ou défaillance # --- Permission saisie notes et décisions de jury, avec ou sans démission ou défaillance
# on n'a pas encore saisi de décisions # on n'a pas encore saisi de décisions
@ -227,7 +232,7 @@ def run_sco_basic(verbose=False):
for ue_id in dec_ues: for ue_id in dec_ues:
assert dec_ues[ue_id]["code"] in {"ADM", "CMP"} assert dec_ues[ue_id]["code"] in {"ADM", "CMP"}
# ---- Suppression étudiant, vérification inscription # ---- Suppression d'un étudiant, vérification inscription
# (permet de tester les cascades) # (permet de tester les cascades)
etud = Identite.query.get(etuds[0]["id"]) etud = Identite.query.get(etuds[0]["id"])
assert etud is not None assert etud is not None
@ -239,3 +244,4 @@ def run_sco_basic(verbose=False):
etudid=etudid, formsemestre_id=formsemestre_id etudid=etudid, formsemestre_id=formsemestre_id
) )
assert q.count() == 0 assert q.count() == 0
return formsemestre