1
0
forked from ScoDoc/ScoDoc

Edition des évaluations, nettoyage code, fix #799. Tests OK.

This commit is contained in:
Emmanuel Viennet 2024-02-26 17:20:36 +01:00
parent ee601071f5
commit bc5292b165
8 changed files with 217 additions and 239 deletions

View File

@ -91,7 +91,7 @@ class Evaluation(models.ScoDocModel):
evaluation_type=None, evaluation_type=None,
numero=None, numero=None,
**kw, # ceci pour absorber les éventuel arguments excedentaires **kw, # ceci pour absorber les éventuel arguments excedentaires
): ) -> "Evaluation":
"""Create an evaluation. Check permission and all arguments. """Create an evaluation. Check permission and all arguments.
Ne crée pas les poids vers les UEs. Ne crée pas les poids vers les UEs.
Add to session, do not commit. Add to session, do not commit.
@ -103,7 +103,7 @@ class Evaluation(models.ScoDocModel):
args = locals() args = locals()
del args["cls"] del args["cls"]
del args["kw"] del args["kw"]
check_convert_evaluation_args(moduleimpl, args) check_and_convert_evaluation_args(args, moduleimpl)
# Check numeros # Check numeros
Evaluation.moduleimpl_evaluation_renumber(moduleimpl, only_if_unumbered=True) Evaluation.moduleimpl_evaluation_renumber(moduleimpl, only_if_unumbered=True)
if not "numero" in args or args["numero"] is None: if not "numero" in args or args["numero"] is None:
@ -254,15 +254,6 @@ class Evaluation(models.ScoDocModel):
return e_dict return e_dict
def convert_dict_fields(self, args: dict) -> dict:
"""Convert fields in the given dict. No other side effect.
returns: dict to store in model's db.
"""
check_convert_evaluation_args(self.moduleimpl, args)
if args.get("numero") is None:
args["numero"] = Evaluation.get_max_numero(self.moduleimpl.id) + 1
return args
@classmethod @classmethod
def get_evaluation( def get_evaluation(
cls, evaluation_id: int | str, dept_id: int = None cls, evaluation_id: int | str, dept_id: int = None
@ -568,7 +559,7 @@ def evaluation_enrich_dict(e: Evaluation, e_dict: dict):
return e_dict return e_dict
def check_convert_evaluation_args(moduleimpl: "ModuleImpl", data: dict): def check_and_convert_evaluation_args(data: dict, moduleimpl: "ModuleImpl"):
"""Check coefficient, dates and duration, raises exception if invalid. """Check coefficient, dates and duration, raises exception if invalid.
Convert date and time strings to date and time objects. Convert date and time strings to date and time objects.
@ -608,7 +599,7 @@ def check_convert_evaluation_args(moduleimpl: "ModuleImpl", data: dict):
if coef < 0: if coef < 0:
raise ScoValueError("invalid coefficient value (must be positive or null)") raise ScoValueError("invalid coefficient value (must be positive or null)")
data["coefficient"] = coef data["coefficient"] = coef
# --- date de l'évaluation # --- date de l'évaluation dans le semestre ?
formsemestre = moduleimpl.formsemestre formsemestre = moduleimpl.formsemestre
date_debut = data.get("date_debut", None) date_debut = data.get("date_debut", None)
if date_debut: if date_debut:

View File

@ -31,96 +31,15 @@
import flask import flask
from flask import url_for, g from flask import url_for, g
from flask_login import current_user from flask_login import current_user
import sqlalchemy as sa
from app import db, log from app import db, log
from app.models import Evaluation from app.models import Evaluation
from app.models.evaluations import check_convert_evaluation_args
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
from app.scodoc.sco_exceptions import AccessDenied from app.scodoc.sco_exceptions import AccessDenied
from app.scodoc import sco_cache from app.scodoc import sco_cache
from app.scodoc import sco_moduleimpl
_evaluationEditor = ndb.EditableTable(
"notes_evaluation",
"evaluation_id",
(
"evaluation_id",
"moduleimpl_id",
"date_debut",
"date_fin",
"description",
"note_max",
"coefficient",
"visibulletin",
"publish_incomplete",
"evaluation_type",
"numero",
),
sortkey="numero, date_debut desc", # plus recente d'abord
output_formators={
"numero": ndb.int_null_is_zero,
},
input_formators={
"visibulletin": bool,
"publish_incomplete": bool,
"evaluation_type": int,
},
)
def get_evaluations_dict(args: dict) -> list[dict]:
"""Liste evaluations, triées numero (or most recent date first).
Fonction de transition pour ancien code ScoDoc7.
Ajoute les champs:
'duree' : '2h30'
'matin' : 1 (commence avant 12:00) ou 0
'apresmidi' : 1 (termine après 12:00) ou 0
'descrheure' : ' de 15h00 à 16h30'
"""
# calcule duree (chaine de car.) de chaque evaluation et ajoute jour_iso, matin, apresmidi
return [
e.to_dict()
for e in Evaluation.query.filter_by(**args).order_by(
sa.desc(Evaluation.numero), sa.desc(Evaluation.date_debut)
)
]
def do_evaluation_list_in_formsemestre(formsemestre_id):
"list evaluations in this formsemestre"
mods = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
evals = []
for modimpl in mods:
evals += get_evaluations_dict(args={"moduleimpl_id": modimpl["moduleimpl_id"]})
return evals
def do_evaluation_edit(args):
"edit an evaluation"
evaluation_id = args["evaluation_id"]
evaluation: Evaluation = db.session.get(Evaluation, evaluation_id)
if evaluation is None:
raise ValueError("evaluation inexistante !")
if not evaluation.moduleimpl.can_edit_evaluation(current_user):
raise AccessDenied(
f"Modification évaluation impossible pour {current_user.get_nomplogin()}"
)
args["moduleimpl_id"] = evaluation.moduleimpl.id
check_convert_evaluation_args(evaluation.moduleimpl, args)
cnx = ndb.GetDBConnexion()
_evaluationEditor.edit(cnx, args)
# inval cache pour ce semestre
sco_cache.invalidate_formsemestre(
formsemestre_id=evaluation.moduleimpl.formsemestre_id
)
# ancien _notes_getall # ancien _notes_getall

View File

@ -31,14 +31,12 @@ import datetime
import time import time
import flask import flask
from flask import url_for, render_template from flask import g, render_template, request, url_for
from flask import g
from flask_login import current_user from flask_login import current_user
from flask import request
from app import db from app import db
from app.models import Evaluation, Module, ModuleImpl from app.models import Evaluation, Module, ModuleImpl
from app.models.evaluations import heure_to_time from app.models.evaluations import heure_to_time, check_and_convert_evaluation_args
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
@ -383,6 +381,8 @@ def evaluation_create_form(
raise ScoValueError("Date (j/m/a) invalide") from exc raise ScoValueError("Date (j/m/a) invalide") from exc
else: else:
date_debut = None date_debut = None
args["date_debut"] = date_debut
args["date_fin"] = date_debut # même jour
args.pop("jour", None) args.pop("jour", None)
if date_debut and args.get("heure_debut"): if date_debut and args.get("heure_debut"):
try: try:
@ -415,6 +415,7 @@ def evaluation_create_form(
args["blocked_until"] = None args["blocked_until"] = None
# #
if edit: if edit:
check_and_convert_evaluation_args(args, modimpl)
evaluation.from_dict(args) evaluation.from_dict(args)
else: else:
# création d'une evaluation # création d'une evaluation

View File

@ -31,6 +31,7 @@ import flask
from flask import url_for, flash, redirect from flask import url_for, flash, redirect
from flask import g, request from flask import g, request
from flask_login import current_user from flask_login import current_user
import sqlalchemy as sa
from app import db from app import db
from app.auth.models import User from app.auth.models import User
@ -63,8 +64,6 @@ from app.scodoc import html_sco_header
from app.scodoc import codes_cursus from app.scodoc import codes_cursus
from app.scodoc import sco_compute_moy from app.scodoc import sco_compute_moy
from app.scodoc import sco_edit_module from app.scodoc import sco_edit_module
from app.scodoc import sco_edit_ue
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_copy from app.scodoc import sco_groups_copy
from app.scodoc import sco_modalites from app.scodoc import sco_modalites
@ -1113,7 +1112,8 @@ def formsemestre_delete_moduleimpls(formsemestre_id, module_ids_to_del):
f"""<b>impossible de supprimer {module.code} ({module.titre or ""}) f"""<b>impossible de supprimer {module.code} ({module.titre or ""})
car il y a {nb_evals} évaluations définies car il y a {nb_evals} évaluations définies
(<a href="{ (<a href="{
url_for("notes.moduleimpl_status", scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id) url_for("notes.moduleimpl_status",
scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id)
}" class="stdlink">supprimez-les d\'abord</a>)</b>""" }" class="stdlink">supprimez-les d\'abord</a>)</b>"""
] ]
ok = False ok = False
@ -1233,7 +1233,11 @@ def formsemestre_clone(formsemestre_id):
return "".join(H) + msg + tf[1] + html_sco_header.sco_footer() return "".join(H) + msg + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1: # cancel elif tf[0] == -1: # cancel
return flask.redirect( return flask.redirect(
"formsemestre_status?formsemestre_id=%s" % formsemestre_id url_for(
"notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
)
) )
else: else:
resp = User.get_user_from_nomplogin(tf[2]["responsable_id"]) resp = User.get_user_from_nomplogin(tf[2]["responsable_id"])
@ -1356,9 +1360,9 @@ def do_formsemestre_clone(
return formsemestre_id return formsemestre_id
def formsemestre_delete(formsemestre_id): def formsemestre_delete(formsemestre_id: int) -> str | flask.Response:
"""Delete a formsemestre (affiche avertissements)""" """Delete a formsemestre (affiche avertissements)"""
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) formsemestre: FormSemestre = FormSemestre.get_formsemestre(formsemestre_id)
H = [ H = [
html_sco_header.html_sem_header("Suppression du semestre"), html_sco_header.html_sem_header("Suppression du semestre"),
"""<div class="ue_warning"><span>Attention !</span> """<div class="ue_warning"><span>Attention !</span>
@ -1376,17 +1380,18 @@ Ceci n'est possible que si :
</ol> </ol>
</div>""", </div>""",
] ]
evaluations = (
evals = sco_evaluation_db.do_evaluation_list_in_formsemestre(formsemestre_id) Evaluation.query.join(ModuleImpl)
if evals: .filter_by(formsemestre_id=formsemestre.id)
.all()
)
if evaluations:
H.append( H.append(
f"""<p class="warning">Attention: il y a {len(evals)} évaluations f"""<p class="warning">Attention: il y a {len(evaluations)} évaluations
dans ce semestre dans ce semestre
(sa suppression entrainera l'effacement définif des notes) !</p>""" (sa suppression entrainera l'effacement définif des notes) !</p>"""
) )
submit_label = ( submit_label = f"Confirmer la suppression (du semestre et des {len(evaluations)} évaluations !)"
f"Confirmer la suppression (du semestre et des {len(evals)} évaluations !)"
)
else: else:
submit_label = "Confirmer la suppression du semestre" submit_label = "Confirmer la suppression du semestre"
tf = TrivialFormulator( tf = TrivialFormulator(
@ -1413,8 +1418,10 @@ Ceci n'est possible que si :
) )
else: else:
H.append(tf[1]) H.append(tf[1])
return "\n".join(H) + html_sco_header.sco_footer() return "\n".join(H) + html_sco_header.sco_footer()
elif tf[0] == -1: # cancel
if tf[0] == -1: # cancel
return flask.redirect( return flask.redirect(
url_for( url_for(
"notes.formsemestre_status", "notes.formsemestre_status",
@ -1422,7 +1429,6 @@ Ceci n'est possible que si :
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
) )
) )
else:
return flask.redirect( return flask.redirect(
"formsemestre_delete2?formsemestre_id=" + str(formsemestre_id) "formsemestre_delete2?formsemestre_id=" + str(formsemestre_id)
) )
@ -1486,106 +1492,165 @@ def formsemestre_has_decisions_or_compensations(
return False, "" return False, ""
def do_formsemestre_delete(formsemestre_id): def do_formsemestre_delete(formsemestre_id: int):
"""delete formsemestre, and all its moduleimpls. """delete formsemestre, and all its moduleimpls.
No checks, no warnings: erase all ! No checks, no warnings: erase all !
""" """
cnx = ndb.GetDBConnexion() formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sco_cache.EvaluationCache.invalidate_sem(formsemestre.id)
titre_sem = formsemestre.titre_annee()
sco_cache.EvaluationCache.invalidate_sem(formsemestre_id)
# --- Destruction des modules de ce semestre # --- Destruction des modules de ce semestre
mods = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id) for modimpl in formsemestre.modimpls:
for mod in mods:
# evaluations # evaluations
evals = sco_evaluation_db.get_evaluations_dict( for e in modimpl.evaluations:
args={"moduleimpl_id": mod["moduleimpl_id"]} db.session.execute(
sa.text(
"""DELETE FROM notes_notes WHERE evaluation_id=:evaluation_id"""
),
{"evaluation_id": e.id},
) )
for e in evals: db.session.execute(
ndb.SimpleQuery( sa.text(
"DELETE FROM notes_notes WHERE evaluation_id=%(evaluation_id)s", """DELETE FROM notes_notes_log WHERE evaluation_id=:evaluation_id"""
e, ),
) {"evaluation_id": e.id},
ndb.SimpleQuery(
"DELETE FROM notes_notes_log WHERE evaluation_id=%(evaluation_id)s",
e,
)
ndb.SimpleQuery(
"DELETE FROM notes_evaluation WHERE id=%(evaluation_id)s",
e,
) )
sco_moduleimpl.do_moduleimpl_delete( db.session.delete(e)
mod["moduleimpl_id"], formsemestre_id=formsemestre_id db.session.delete(modimpl)
)
# --- Desinscription des etudiants # --- Desinscription des etudiants
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) db.session.execute(
req = "DELETE FROM notes_formsemestre_inscription WHERE formsemestre_id=%(formsemestre_id)s" sa.text(
cursor.execute(req, {"formsemestre_id": formsemestre_id}) "DELETE FROM notes_formsemestre_inscription WHERE formsemestre_id=:formsemestre_id"
),
{"formsemestre_id": formsemestre_id},
)
# --- Suppression des evenements # --- Suppression des evenements
req = "DELETE FROM scolar_events WHERE formsemestre_id=%(formsemestre_id)s" db.session.execute(
cursor.execute(req, {"formsemestre_id": formsemestre_id}) sa.text("DELETE FROM scolar_events WHERE formsemestre_id=:formsemestre_id"),
{"formsemestre_id": formsemestre_id},
)
# --- Suppression des appreciations # --- Suppression des appreciations
req = "DELETE FROM notes_appreciations WHERE formsemestre_id=%(formsemestre_id)s" db.session.execute(
cursor.execute(req, {"formsemestre_id": formsemestre_id}) sa.text(
"DELETE FROM notes_appreciations WHERE formsemestre_id=:formsemestre_id"
),
{"formsemestre_id": formsemestre_id},
)
# --- Supression des validations (!!!) # --- Supression des validations (!!!)
req = "DELETE FROM scolar_formsemestre_validation WHERE formsemestre_id=%(formsemestre_id)s" db.session.execute(
cursor.execute(req, {"formsemestre_id": formsemestre_id}) sa.text(
"DELETE FROM scolar_formsemestre_validation WHERE formsemestre_id=:formsemestre_id"
),
{"formsemestre_id": formsemestre_id},
)
# --- Supression des references a ce semestre dans les compensations: # --- Supression des references a ce semestre dans les compensations:
req = "UPDATE scolar_formsemestre_validation SET compense_formsemestre_id=NULL WHERE compense_formsemestre_id=%(formsemestre_id)s" db.session.execute(
cursor.execute(req, {"formsemestre_id": formsemestre_id}) sa.text(
"""UPDATE scolar_formsemestre_validation
SET compense_formsemestre_id=NULL
WHERE compense_formsemestre_id=:formsemestre_id"""
),
{"formsemestre_id": formsemestre_id},
)
# --- Suppression des autorisations # --- Suppression des autorisations
req = "DELETE FROM scolar_autorisation_inscription WHERE origin_formsemestre_id=%(formsemestre_id)s" db.session.execute(
cursor.execute(req, {"formsemestre_id": formsemestre_id}) sa.text(
"DELETE FROM scolar_autorisation_inscription WHERE origin_formsemestre_id=:formsemestre_id"
),
{"formsemestre_id": formsemestre_id},
)
# --- Suppression des coefs d'UE capitalisées # --- Suppression des coefs d'UE capitalisées
req = "DELETE FROM notes_formsemestre_uecoef WHERE formsemestre_id=%(formsemestre_id)s" db.session.execute(
cursor.execute(req, {"formsemestre_id": formsemestre_id}) sa.text(
"DELETE FROM notes_formsemestre_uecoef WHERE formsemestre_id=:formsemestre_id"
),
{"formsemestre_id": formsemestre_id},
)
# --- Suppression des item du menu custom # --- Suppression des item du menu custom
req = "DELETE FROM notes_formsemestre_custommenu WHERE formsemestre_id=%(formsemestre_id)s" db.session.execute(
cursor.execute(req, {"formsemestre_id": formsemestre_id}) sa.text(
"DELETE FROM notes_formsemestre_custommenu WHERE formsemestre_id=:formsemestre_id"
),
{"formsemestre_id": formsemestre_id},
)
# --- Suppression des formules # --- Suppression des formules
req = "DELETE FROM notes_formsemestre_ue_computation_expr WHERE formsemestre_id=%(formsemestre_id)s" db.session.execute(
cursor.execute(req, {"formsemestre_id": formsemestre_id}) sa.text(
"DELETE FROM notes_formsemestre_ue_computation_expr WHERE formsemestre_id=:formsemestre_id"
),
{"formsemestre_id": formsemestre_id},
)
# --- Suppression des preferences # --- Suppression des preferences
req = "DELETE FROM sco_prefs WHERE formsemestre_id=%(formsemestre_id)s" db.session.execute(
cursor.execute(req, {"formsemestre_id": formsemestre_id}) sa.text("DELETE FROM sco_prefs WHERE formsemestre_id=:formsemestre_id"),
{"formsemestre_id": formsemestre_id},
)
# --- Suppression des groupes et partitions # --- Suppression des groupes et partitions
req = """DELETE FROM group_membership db.session.execute(
sa.text(
"""
DELETE FROM group_membership
WHERE group_id IN WHERE group_id IN
(SELECT gm.group_id FROM group_membership gm, partition p, group_descr gd (SELECT gm.group_id FROM group_membership gm, partition p, group_descr gd
WHERE gm.group_id = gd.id AND gd.partition_id = p.id WHERE gm.group_id = gd.id AND gd.partition_id = p.id
AND p.formsemestre_id=%(formsemestre_id)s) AND p.formsemestre_id=:formsemestre_id)
""" """
cursor.execute(req, {"formsemestre_id": formsemestre_id}) ),
req = """DELETE FROM group_descr {"formsemestre_id": formsemestre_id},
)
db.session.execute(
sa.text(
"""
DELETE FROM group_descr
WHERE id IN WHERE id IN
(SELECT gd.id FROM group_descr gd, partition p (SELECT gd.id FROM group_descr gd, partition p
WHERE gd.partition_id = p.id WHERE gd.partition_id = p.id
AND p.formsemestre_id=%(formsemestre_id)s) AND p.formsemestre_id=:formsemestre_id)
""" """
cursor.execute(req, {"formsemestre_id": formsemestre_id}) ),
req = "DELETE FROM partition WHERE formsemestre_id=%(formsemestre_id)s" {"formsemestre_id": formsemestre_id},
cursor.execute(req, {"formsemestre_id": formsemestre_id}) )
db.session.execute(
sa.text("DELETE FROM partition WHERE formsemestre_id=:formsemestre_id"),
{"formsemestre_id": formsemestre_id},
)
# --- Responsables # --- Responsables
req = """DELETE FROM notes_formsemestre_responsables db.session.execute(
WHERE formsemestre_id=%(formsemestre_id)s""" sa.text(
cursor.execute(req, {"formsemestre_id": formsemestre_id}) "DELETE FROM notes_formsemestre_responsables WHERE formsemestre_id=:formsemestre_id"
),
{"formsemestre_id": formsemestre_id},
)
# --- Etapes # --- Etapes
req = """DELETE FROM notes_formsemestre_etapes db.session.execute(
WHERE formsemestre_id=%(formsemestre_id)s""" sa.text(
cursor.execute(req, {"formsemestre_id": formsemestre_id}) "DELETE FROM notes_formsemestre_etapes WHERE formsemestre_id=:formsemestre_id"
),
{"formsemestre_id": formsemestre_id},
)
# --- SemSets
db.session.execute(
sa.text(
"DELETE FROM notes_semset_formsemestre WHERE formsemestre_id=:formsemestre_id"
),
{"formsemestre_id": formsemestre_id},
)
# --- Dispenses d'UE # --- Dispenses d'UE
req = """DELETE FROM "dispenseUE" WHERE formsemestre_id=%(formsemestre_id)s""" db.session.execute(
cursor.execute(req, {"formsemestre_id": formsemestre_id}) sa.text("""DELETE FROM "dispenseUE" WHERE formsemestre_id=:formsemestre_id"""),
{"formsemestre_id": formsemestre_id},
)
# --- Destruction du semestre # --- Destruction du semestre
sco_formsemestre._formsemestreEditor.delete(cnx, formsemestre_id) db.session.delete(formsemestre)
# news # news
ScolarNews.add( ScolarNews.add(
typ=ScolarNews.NEWS_SEM, typ=ScolarNews.NEWS_SEM,
obj=formsemestre_id, obj=formsemestre_id,
text="Suppression du semestre %(titre)s" % sem, text=f"Suppression du semestre {titre_sem}",
max_frequency=0, max_frequency=0,
) )

View File

@ -91,7 +91,9 @@ def do_moduleimpl_delete(oid, formsemestre_id=None):
) # > moduleimpl_delete ) # > moduleimpl_delete
def moduleimpl_list(moduleimpl_id=None, formsemestre_id=None, module_id=None): def moduleimpl_list(
moduleimpl_id=None, formsemestre_id=None, module_id=None
) -> list[dict]:
"list moduleimpls" "list moduleimpls"
args = locals() args = locals()
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()

View File

@ -48,20 +48,17 @@ from wtforms import (
HiddenField, HiddenField,
SelectMultipleField, SelectMultipleField,
) )
from app.models import ModuleImpl from app.models import Evaluation, ModuleImpl
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
from app import ScoValueError
from app.scodoc import html_sco_header, sco_preferences from app.scodoc import html_sco_header, sco_preferences
from app.scodoc import sco_edit_module from app.scodoc import sco_edit_module
from app.scodoc import sco_evaluations from app.scodoc import sco_evaluations
from app.scodoc import sco_evaluation_db
from app.scodoc import sco_excel from app.scodoc import sco_excel
from app.scodoc.sco_excel import ScoExcelBook, COLORS from app.scodoc.sco_excel import ScoExcelBook, COLORS
from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups from app.scodoc import sco_groups
from app.scodoc import sco_moduleimpl from app.scodoc import sco_moduleimpl
from app.scodoc import sco_permissions_check
from app.scodoc.gen_tables import GenTable from app.scodoc.gen_tables import GenTable
from app.scodoc import sco_etud from app.scodoc import sco_etud
import sco_version import sco_version
@ -138,11 +135,7 @@ class PlacementForm(FlaskForm):
def set_evaluation_infos(self, evaluation_id): def set_evaluation_infos(self, evaluation_id):
"""Initialise les données du formulaire avec les données de l'évaluation.""" """Initialise les données du formulaire avec les données de l'évaluation."""
eval_data = sco_evaluation_db.get_evaluations_dict( _ = Evaluation.get_evaluation(evaluation_id) # check exist ?
{"evaluation_id": evaluation_id}
)
if not eval_data:
raise ScoValueError("invalid evaluation_id")
self.groups_tree, self.has_groups, self.nb_groups = _get_group_info( self.groups_tree, self.has_groups, self.nb_groups = _get_group_info(
evaluation_id evaluation_id
) )
@ -239,14 +232,12 @@ class PlacementRunner:
self.groups_ids = [ self.groups_ids = [
gid if gid != TOUS else form.tous_id for gid in form["groups"].data gid if gid != TOUS else form.tous_id for gid in form["groups"].data
] ]
self.eval_data = sco_evaluation_db.get_evaluations_dict( self.evaluation = Evaluation.get_evaluation(self.evaluation_id)
{"evaluation_id": self.evaluation_id}
)[0]
self.groups = sco_groups.listgroups(self.groups_ids) self.groups = sco_groups.listgroups(self.groups_ids)
self.gr_title_filename = sco_groups.listgroups_filename(self.groups) self.gr_title_filename = sco_groups.listgroups_filename(self.groups)
# gr_title = sco_groups.listgroups_abbrev(d['groups']) # gr_title = sco_groups.listgroups_abbrev(d['groups'])
self.current_user = current_user self.current_user = current_user
self.moduleimpl_id = self.eval_data["moduleimpl_id"] self.moduleimpl_id = self.evaluation.moduleimpl_id
self.moduleimpl: ModuleImpl = ModuleImpl.query.get_or_404(self.moduleimpl_id) self.moduleimpl: ModuleImpl = ModuleImpl.query.get_or_404(self.moduleimpl_id)
# TODO: à revoir pour utiliser modèle ModuleImpl # TODO: à revoir pour utiliser modèle ModuleImpl
self.moduleimpl_data = sco_moduleimpl.moduleimpl_list( self.moduleimpl_data = sco_moduleimpl.moduleimpl_list(
@ -260,20 +251,25 @@ class PlacementRunner:
) )
self.evalname = "%s-%s" % ( self.evalname = "%s-%s" % (
self.module_data["code"] or "?", self.module_data["code"] or "?",
ndb.DateDMYtoISO(self.eval_data["jour"]), (
self.evaluation.date_debut.strftime("%Y-%m-%d_%Hh%M")
if self.evaluation.date_debut
else ""
),
) )
if self.eval_data["description"]: if self.evaluation.description:
self.evaltitre = self.eval_data["description"] self.evaltitre = self.evaluation.description
else: else:
self.evaltitre = "évaluation du %s" % self.eval_data["jour"] self.evaltitre = f"""évaluation{
self.evaluation.date_debut.strftime(' du %d/%m/%Y à %Hh%M')
if self.evaluation.date_debut else ''}"""
self.desceval = [ # une liste de chaines: description de l'evaluation self.desceval = [ # une liste de chaines: description de l'evaluation
"%s" % self.sem["titreannee"], self.sem["titreannee"],
"Module : %s - %s" "Module : %s - %s"
% (self.module_data["code"] or "?", self.module_data["abbrev"] or ""), % (self.module_data["code"] or "?", self.module_data["abbrev"] or ""),
"Surveillants : %s" % self.surveillants, "Surveillants : %s" % self.surveillants,
"Batiment : %(batiment)s - Salle : %(salle)s" % self.__dict__, "Batiment : %(batiment)s - Salle : %(salle)s" % self.__dict__,
"Controle : %s (coef. %g)" "Controle : %s (coef. %g)" % (self.evaltitre, self.evaluation.coefficient),
% (self.evaltitre, self.eval_data["coefficient"]),
] ]
self.styles = None self.styles = None
self.plan = None self.plan = None
@ -339,10 +335,10 @@ class PlacementRunner:
def _production_pdf(self): def _production_pdf(self):
pdf_title = "<br>".join(self.desceval) pdf_title = "<br>".join(self.desceval)
pdf_title += ( pdf_title += f"""\nDate : {self.evaluation.date_debut.strftime("%d/%m/%Y")
"\nDate : %(jour)s - Horaire : %(heure_debut)s à %(heure_fin)s" if self.evaluation.date_debut else '-'
% self.eval_data } - Horaire : {self.evaluation.heure_debut()} à {self.evaluation.heure_fin()
) }"""
filename = "placement_%(evalname)s_%(gr_title_filename)s" % self.__dict__ filename = "placement_%(evalname)s_%(gr_title_filename)s" % self.__dict__
titles = { titles = {
"nom": "Nom", "nom": "Nom",
@ -489,8 +485,10 @@ class PlacementRunner:
worksheet.append_blank_row() worksheet.append_blank_row()
worksheet.append_single_cell_row(desceval, self.styles["titres"]) worksheet.append_single_cell_row(desceval, self.styles["titres"])
worksheet.append_single_cell_row( worksheet.append_single_cell_row(
"Date : %(jour)s - Horaire : %(heure_debut)s à %(heure_fin)s" f"""Date : {self.evaluation.date_debut.strftime("%d/%m/%Y")
% self.eval_data, if self.evaluation.date_debut else '-'
} - Horaire : {self.evaluation.heure_debut()} à {self.evaluation.heure_fin()
}""",
self.styles["titres"], self.styles["titres"],
) )

View File

@ -48,16 +48,15 @@ Opérations:
import datetime import datetime
from flask import request from flask import request
from app.models import FormSemestre from app.models import Evaluation, FormSemestre
from app.scodoc.intervals import intervalmap from app.scodoc.intervals import intervalmap
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
from app.scodoc import sco_evaluation_db from app.scodoc import sco_evaluation_db
from app.scodoc import sco_moduleimpl
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_users from app.scodoc import sco_users
import sco_version
from app.scodoc.gen_tables import GenTable from app.scodoc.gen_tables import GenTable
import sco_version
# deux notes (de même uid) sont considérées comme de la même opération si # deux notes (de même uid) sont considérées comme de la même opération si
# elles sont séparées de moins de 2*tolerance: # elles sont séparées de moins de 2*tolerance:
@ -149,10 +148,8 @@ def list_operations(evaluation_id):
def evaluation_list_operations(evaluation_id): def evaluation_list_operations(evaluation_id):
"""Page listing operations on evaluation""" """Page listing operations on evaluation"""
E = sco_evaluation_db.get_evaluations_dict({"evaluation_id": evaluation_id})[0] evaluation = Evaluation.get_evaluation(evaluation_id)
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] operations = list_operations(evaluation_id)
Ops = list_operations(evaluation_id)
columns_ids = ("datestr", "user_name", "nb_notes", "comment") columns_ids = ("datestr", "user_name", "nb_notes", "comment")
titles = { titles = {
@ -164,11 +161,14 @@ def evaluation_list_operations(evaluation_id):
tab = GenTable( tab = GenTable(
titles=titles, titles=titles,
columns_ids=columns_ids, columns_ids=columns_ids,
rows=Ops, rows=operations,
html_sortable=False, html_sortable=False,
html_title="<h2>Opérations sur l'évaluation %s du %s</h2>" html_title=f"""<h2>Opérations sur l'évaluation {evaluation.description} {
% (E["description"], E["jour"]), evaluation.date_debut.strftime("du %d/%m/%Y") if evaluation.date_debut else "(sans date)"
preferences=sco_preferences.SemPreferences(M["formsemestre_id"]), }</h2>""",
preferences=sco_preferences.SemPreferences(
evaluation.moduleimpl.formsemestre_id
),
) )
return tab.make_page() return tab.make_page()

View File

@ -13,7 +13,7 @@ Au besoin, créer un base de test neuve:
""" """
import datetime import datetime
from app.models import FormSemestreInscription, Identite from app.models import Evaluation, FormSemestreInscription, Identite, ModuleImpl
from config import TestConfig from config import TestConfig
from tests.unit import sco_fake_gen from tests.unit import sco_fake_gen
@ -29,7 +29,6 @@ from app.scodoc import sco_bulletins
from app.scodoc import codes_cursus from app.scodoc import codes_cursus
from app.scodoc import sco_assiduites as scass from app.scodoc import sco_assiduites as scass
from app.scodoc import sco_evaluations from app.scodoc import sco_evaluations
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
@ -81,7 +80,7 @@ def run_sco_basic(verbose=False, dept=None) -> FormSemestre:
module_id=module_id, module_id=module_id,
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
) )
moduleimpl: ModuleImpl = db.session.get(ModuleImpl, moduleimpl_id)
# --- Inscription des étudiants # --- Inscription des étudiants
for etud in etuds: for etud in etuds:
G.inscrit_etudiant(formsemestre_id, etud) G.inscrit_etudiant(formsemestre_id, etud)
@ -97,17 +96,18 @@ def run_sco_basic(verbose=False, dept=None) -> FormSemestre:
assert ins.parcour is None assert ins.parcour is None
# --- Création évaluation # --- Création évaluation
e = G.create_evaluation( e1 = Evaluation.create(
moduleimpl_id=moduleimpl_id, moduleimpl=moduleimpl,
date_debut=datetime.datetime(2020, 1, 1), date_debut=datetime.datetime(2020, 1, 1),
description="evaluation test", description="evaluation test",
coefficient=1.0, coefficient=1.0,
) )
db.session.commit()
# --- 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 = G.create_note(
evaluation_id=e["evaluation_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)],
) )
@ -118,7 +118,7 @@ def run_sco_basic(verbose=False, dept=None) -> FormSemestre:
# --- 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"])
# Toute les notes sont saisies, donc eval complète # Toute les notes sont saisies, donc eval complète
etat = sco_evaluations.do_evaluation_etat(e["evaluation_id"]) etat = sco_evaluations.do_evaluation_etat(e1.id)
assert etat["evalcomplete"] assert etat["evalcomplete"]
assert etat["nb_inscrits"] == len(etuds) assert etat["nb_inscrits"] == len(etuds)
assert etat["nb_notes"] == len(etuds) assert etat["nb_notes"] == len(etuds)
@ -131,30 +131,32 @@ def run_sco_basic(verbose=False, dept=None) -> FormSemestre:
) )
# --- Une autre évaluation # --- Une autre évaluation
e2 = G.create_evaluation( e2 = Evaluation.create(
moduleimpl_id=moduleimpl_id, moduleimpl=moduleimpl,
date_debut=datetime.datetime(2020, 1, 2), date_debut=datetime.datetime(2020, 1, 2),
description="evaluation test 2", description="evaluation test 2",
coefficient=1.0, coefficient=1.0,
) )
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 = G.create_note(
evaluation_id=e2["evaluation_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)],
) )
# 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.id)
assert etat["evalcomplete"] is 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(e1.id)
assert etat["evalcomplete"] assert etat["evalcomplete"]
# Modifie l'évaluation 2 pour "prise en compte immédiate" # Modifie l'évaluation 2 pour "prise en compte immédiate"
e2["publish_incomplete"] = True e2.publish_incomplete = True
sco_evaluation_db.do_evaluation_edit(e2) db.session.add(e2)
etat = sco_evaluations.do_evaluation_etat(e2["evaluation_id"]) db.session.flush()
etat = sco_evaluations.do_evaluation_etat(e2.id)
assert etat["evalcomplete"] is 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)
@ -162,26 +164,26 @@ 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 = G.create_note(
evaluation_id=e2["evaluation_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)],
) )
etat = sco_evaluations.do_evaluation_etat(e2["evaluation_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
assert not etat["evalattente"] # toutes les notes sont présentes assert not etat["evalattente"] # toutes les notes sont présentes
# --- Suppression des notes # --- Suppression des notes
sco_saisie_notes.evaluation_suppress_alln(e["evaluation_id"], dialog_confirmed=True) sco_saisie_notes.evaluation_suppress_alln(e1.id, dialog_confirmed=True)
etat = sco_evaluations.do_evaluation_etat(e["evaluation_id"]) etat = sco_evaluations.do_evaluation_etat(e1.id)
assert etat["nb_notes"] == 0 assert etat["nb_notes"] == 0
assert not etat["evalcomplete"] assert not etat["evalcomplete"]
# --- Saisie des notes manquantes # --- Saisie des notes manquantes
ans = sco_saisie_notes.do_evaluation_set_missing( ans = sco_saisie_notes.do_evaluation_set_missing(
e["evaluation_id"], 12.34, dialog_confirmed=True e1.id, 12.34, dialog_confirmed=True
) )
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(e1.id)
assert etat["evalcomplete"] assert etat["evalcomplete"]
# ----------------------- # -----------------------