From 3d513bb1695121841da20d0d3faf63d436f99115 Mon Sep 17 00:00:00 2001
From: Emmanuel Viennet
Date: Fri, 25 Aug 2023 17:58:57 +0200
Subject: [PATCH] =?UTF-8?q?Modification=20codage=20dates=20=C3=A9valuation?=
=?UTF-8?q?s?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/but/bulletin_but.py | 2 +-
app/but/bulletin_but_xml_compat.py | 8 +-
app/comp/moy_mod.py | 2 +-
app/comp/res_common.py | 15 +-
app/comp/res_compat.py | 42 ++-
app/models/evaluations.py | 273 +++++++++++-------
app/models/formsemestre.py | 16 +
app/scodoc/sco_abs.py | 4 +-
app/scodoc/sco_abs_views.py | 4 +-
app/scodoc/sco_bulletins.py | 148 +++++-----
app/scodoc/sco_bulletins_json.py | 76 ++---
app/scodoc/sco_bulletins_xml.py | 55 ++--
app/scodoc/sco_evaluation_check_abs.py | 127 ++++----
app/scodoc/sco_evaluation_db.py | 32 +-
app/scodoc/sco_evaluation_edit.py | 53 ++--
app/scodoc/sco_evaluation_recap.py | 4 +-
app/scodoc/sco_evaluations.py | 237 ++++++++-------
app/scodoc/sco_excel.py | 3 +-
app/scodoc/sco_formsemestre_edit.py | 6 +-
app/scodoc/sco_formsemestre_status.py | 12 +-
app/scodoc/sco_liste_notes.py | 24 +-
app/scodoc/sco_moduleimpl_status.py | 13 +-
app/scodoc/sco_placement.py | 4 +-
app/scodoc/sco_saisie_notes.py | 12 +-
app/scodoc/sco_undo_notes.py | 14 +-
app/scodoc/sco_utils.py | 2 +
app/tables/recap.py | 4 +-
app/views/notes.py | 2 +-
tests/api/setup_test_api.py | 72 +++++
tests/api/test_api_etudiants.py | 19 +-
tests/api/test_api_formsemestre.py | 21 +-
tests/api/tools_test_api.py | 4 +-
tests/unit/sco_fake_gen.py | 17 +-
tests/unit/setup.py | 4 +-
tests/unit/test_abs_demijournee.py | 4 +-
tests/unit/test_bulletin.py | 2 +-
tests/unit/test_but_modules.py | 5 +-
tests/unit/test_caches.py | 30 +-
tests/unit/test_notes_modules.py | 83 ++++--
tests/unit/test_notes_rattrapage.py | 7 +-
tests/unit/test_sco_basic.py | 12 +-
tests/unit/yaml_setup.py | 2 +-
.../fakedatabase/create_test_api_database.py | 3 +-
43 files changed, 844 insertions(+), 635 deletions(-)
diff --git a/app/but/bulletin_but.py b/app/but/bulletin_but.py
index b9261c4fb2..2e556908fc 100644
--- a/app/but/bulletin_but.py
+++ b/app/but/bulletin_but.py
@@ -297,7 +297,7 @@ class BulletinBUT:
)
if has_request_context()
else "na",
- # deprecated
+ # deprecated (supprimer avant #sco9.7)
"date": e.date_debut.isoformat() if e.date_debut else None,
"heure_debut": e.date_debut.time().isoformat("minutes")
if e.date_debut
diff --git a/app/but/bulletin_but_xml_compat.py b/app/but/bulletin_but_xml_compat.py
index faf932e6f5..45668ac59f 100644
--- a/app/but/bulletin_but_xml_compat.py
+++ b/app/but/bulletin_but_xml_compat.py
@@ -218,12 +218,8 @@ def bulletin_but_xml_compat(
jour=e.date_debut.isoformat()
if e.date_debut
else "",
- heure_debut=e.date_debut.time().isoformat("minutes")
- if e.date_debut
- else "",
- heure_fin=e.date_fin.time().isoformat("minutes")
- if e.date_fin
- else "",
+ heure_debut=e.heure_debut(),
+ heure_fin=e.heure_fin(),
)
x_mod.append(x_eval)
try:
diff --git a/app/comp/moy_mod.py b/app/comp/moy_mod.py
index 11dd05f62b..4180c35e35 100644
--- a/app/comp/moy_mod.py
+++ b/app/comp/moy_mod.py
@@ -250,7 +250,7 @@ class ModuleImplResults:
).reshape(-1, 1)
# was _list_notes_evals_titles
- def get_evaluations_completes(self, moduleimpl: ModuleImpl) -> list:
+ def get_evaluations_completes(self, moduleimpl: ModuleImpl) -> list[Evaluation]:
"Liste des évaluations complètes"
return [
e for e in moduleimpl.evaluations if self.evaluations_completes_dict[e.id]
diff --git a/app/comp/res_common.py b/app/comp/res_common.py
index 03b91e0cc3..175e38df8b 100644
--- a/app/comp/res_common.py
+++ b/app/comp/res_common.py
@@ -80,8 +80,8 @@ class ResultatsSemestre(ResultatsCache):
self.moy_gen_rangs_by_group = None # virtual
self.modimpl_inscr_df: pd.DataFrame = None
"Inscriptions: row etudid, col modimlpl_id"
- self.modimpls_results: ModuleImplResults = None
- "Résultats de chaque modimpl: dict { modimpl.id : ModuleImplResults(Classique ou BUT) }"
+ self.modimpls_results: dict[int, ModuleImplResults] = None
+ "Résultats de chaque modimpl (classique ou BUT)"
self.etud_coef_ue_df = None
"""coefs d'UE effectifs pour chaque étudiant (pour form. classiques)"""
self.modimpl_coefs_df: pd.DataFrame = None
@@ -192,6 +192,17 @@ class ResultatsSemestre(ResultatsCache):
*[mr.etudids_attente for mr in self.modimpls_results.values()]
)
+ # # Etat des évaluations
+ # # (se substitue à do_evaluation_etat, sans les moyennes par groupes)
+ # def get_evaluations_etats(evaluation_id: int) -> dict:
+ # """Renvoie dict avec les clés:
+ # last_modif
+ # nb_evals_completes
+ # nb_evals_en_cours
+ # nb_evals_vides
+ # attente
+ # """
+
# --- JURY...
def get_formsemestre_validations(self) -> ValidationsSemestre:
"""Load validations if not already stored, set attribute and return value"""
diff --git a/app/comp/res_compat.py b/app/comp/res_compat.py
index 88fcd095f3..fe41163091 100644
--- a/app/comp/res_compat.py
+++ b/app/comp/res_compat.py
@@ -16,7 +16,13 @@ from app import db, log
from app.comp import moy_sem
from app.comp.aux_stats import StatsMoyenne
from app.comp.res_common import ResultatsSemestre
-from app.models import Identite, FormSemestre, ModuleImpl, ScolarAutorisationInscription
+from app.models import (
+ Evaluation,
+ Identite,
+ FormSemestre,
+ ModuleImpl,
+ ScolarAutorisationInscription,
+)
from app.scodoc.codes_cursus import UE_SPORT, DEF
from app.scodoc import sco_utils as scu
@@ -389,7 +395,7 @@ class NotesTableCompat(ResultatsSemestre):
"ects_total": ects_total,
}
- def get_evals_in_mod(self, moduleimpl_id: int) -> list[dict]:
+ def get_modimpl_evaluations_completes(self, moduleimpl_id: int) -> list[Evaluation]:
"""Liste d'informations (compat NotesTable) sur évaluations completes
de ce module.
Évaluation "complete" ssi toutes notes saisies ou en attente.
@@ -398,34 +404,24 @@ class NotesTableCompat(ResultatsSemestre):
modimpl_results = self.modimpls_results.get(moduleimpl_id)
if not modimpl_results:
return [] # safeguard
- evals_results = []
+ evaluations = []
for e in modimpl.evaluations:
if modimpl_results.evaluations_completes_dict.get(e.id, False):
- d = e.to_dict()
- d["heure_debut"] = e.heure_debut # datetime.time
- d["heure_fin"] = e.heure_fin
- d["jour"] = e.jour # datetime
- d["notes"] = {
- etud.id: {
- "etudid": etud.id,
- "value": modimpl_results.evals_notes[e.id][etud.id],
- }
- for etud in self.etuds
- }
- d["etat"] = {
- "evalattente": modimpl_results.evaluations_etat[e.id].nb_attente,
- }
- evals_results.append(d)
+ evaluations.append(e)
elif e.id not in modimpl_results.evaluations_completes_dict:
# ne devrait pas arriver ? XXX
log(
- f"Warning: 220213 get_evals_in_mod {e.id} not in mod {moduleimpl_id} ?"
+ f"Warning: 220213 get_modimpl_evaluations_completes {e.id} not in mod {moduleimpl_id} ?"
)
- return evals_results
+ return evaluations
+
+ def get_evaluations_etats(self) -> list[dict]:
+ """Liste de toutes les évaluations du semestre
+ [ {...evaluation et son etat...} ]"""
+ # TODO: à moderniser (voir dans ResultatsSemestre)
+ # utilisé par
+ # do_evaluation_etat_in_sem
- def get_evaluations_etats(self):
- """[ {...evaluation et son etat...} ]"""
- # TODO: à moderniser
from app.scodoc import sco_evaluations
if not hasattr(self, "_evaluations_etats"):
diff --git a/app/models/evaluations.py b/app/models/evaluations.py
index deb4bc263e..249900802e 100644
--- a/app/models/evaluations.py
+++ b/app/models/evaluations.py
@@ -18,8 +18,8 @@ from app.models.ues import UniteEns
from app.scodoc import sco_cache
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
-import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
+from app.scodoc.sco_xml import quote_xml_attr
MAX_EVALUATION_DURATION = datetime.timedelta(days=365)
NOON = datetime.time(12, 00)
@@ -46,6 +46,7 @@ class Evaluation(db.Model):
visibulletin = db.Column(
db.Boolean, nullable=False, default=True, server_default="true"
)
+ "visible sur les bulletins version intermédiaire"
publish_incomplete = db.Column(
db.Boolean, nullable=False, default=False, server_default="false"
)
@@ -67,9 +68,8 @@ class Evaluation(db.Model):
def create(
cls,
moduleimpl: ModuleImpl = None,
- jour=None,
- heure_debut=None,
- heure_fin=None,
+ date_debut: datetime.datetime = None,
+ date_fin: datetime.datetime = None,
description=None,
note_max=None,
coefficient=None,
@@ -124,7 +124,7 @@ class Evaluation(db.Model):
next_eval = None
t = date_debut
for e in evaluations:
- if e.date_debut > t:
+ if e.date_debut and e.date_debut > t:
next_eval = e
break
if next_eval:
@@ -140,26 +140,26 @@ class Evaluation(db.Model):
def to_dict(self) -> dict:
"Représentation dict (riche, compat ScoDoc 7)"
- e = dict(self.__dict__)
- e.pop("_sa_instance_state", None)
+ e_dict = dict(self.__dict__)
+ e_dict.pop("_sa_instance_state", None)
# ScoDoc7 output_formators
- e["evaluation_id"] = self.id
- e["date_debut"] = e.date_debut.isoformat() if e.date_debut else None
- e["date_fin"] = e.date_debut.isoformat() if e.date_fin else None
- e["numero"] = ndb.int_null_is_zero(e["numero"])
- e["poids"] = self.get_ue_poids_dict() # { ue_id : poids }
+ e_dict["evaluation_id"] = self.id
+ e_dict["date_debut"] = self.date_debut.isoformat() if self.date_debut else None
+ e_dict["date_fin"] = self.date_debut.isoformat() if self.date_fin else None
+ e_dict["numero"] = self.numero or 0
+ e_dict["poids"] = self.get_ue_poids_dict() # { ue_id : poids }
# Deprecated
- e["jour"] = e.date_debut.strftime("%d/%m/%Y") if e.date_debut else ""
+ e_dict["jour"] = self.date_debut.strftime("%d/%m/%Y") if self.date_debut else ""
- return evaluation_enrich_dict(e)
+ return evaluation_enrich_dict(self, e_dict)
def to_dict_api(self) -> dict:
"Représentation dict pour API JSON"
return {
"coefficient": self.coefficient,
- "date_debut": self.date_debut.isoformat(),
- "date_fin": self.date_fin.isoformat(),
+ "date_debut": self.date_debut.isoformat() if self.date_debut else "",
+ "date_fin": self.date_fin.isoformat() if self.date_fin else "",
"description": self.description,
"evaluation_type": self.evaluation_type,
"id": self.id,
@@ -168,14 +168,36 @@ class Evaluation(db.Model):
"numero": self.numero,
"poids": self.get_ue_poids_dict(),
"publish_incomplete": self.publish_incomplete,
- "visi_bulletin": self.visibulletin,
+ "visibulletin": self.visibulletin,
+ # Deprecated (supprimer avant #sco9.7)
+ "date": self.date_debut.date().isoformat() if self.date_debut else "",
+ "heure_debut": self.date_debut.time().isoformat()
+ if self.date_debut
+ else "",
+ "heure_fin": self.date_fin.time().isoformat() if self.date_fin else "",
}
+ def to_dict_bul(self) -> dict:
+ "dict pour les bulletins json"
+ # c'est la version API avec quelques champs legacy en plus
+ e_dict = self.to_dict_api()
+ # Pour les bulletins (json ou xml), quote toujours la description
+ e_dict["description"] = quote_xml_attr(self.description or "")
+ # deprecated fields:
+ e_dict["evaluation_id"] = self.id
+ e_dict["jour"] = e_dict["date_debut"] # chaine iso
+ e_dict["heure_debut"] = (
+ self.date_debut.time().isoformat() if self.date_debut else ""
+ )
+ e_dict["heure_fin"] = self.date_fin.time().isoformat() if self.date_fin else ""
+
+ return e_dict
+
def from_dict(self, data):
"""Set evaluation attributes from given dict values."""
check_convert_evaluation_args(self.moduleimpl, data)
if data.get("numero") is None:
- data["numero"] = Evaluation.get_max_numero() + 1
+ data["numero"] = Evaluation.get_max_numero(self.moduleimpl.id) + 1
for k in self.__dict__.keys():
if k != "_sa_instance_state" and k != "id" and k in data:
setattr(self, k, data[k])
@@ -217,28 +239,64 @@ class Evaluation(db.Model):
db.session.commit()
def descr_heure(self) -> str:
- "Description de la plage horaire pour affichages"
- if self.heure_debut and (
- not self.heure_fin or self.heure_fin == self.heure_debut
- ):
- return f"""à {self.heure_debut.strftime("%Hh%M")}"""
- elif self.heure_debut and self.heure_fin:
- return f"""de {self.heure_debut.strftime("%Hh%M")} à {self.heure_fin.strftime("%Hh%M")}"""
+ "Description de la plage horaire pour affichages ('de 13h00 à 14h00')"
+ if self.date_debut and (not self.date_fin or self.date_fin == self.date_debut):
+ return f"""à {self.date_debut.strftime("%Hh%M")}"""
+ elif self.date_debut and self.date_fin:
+ return f"""de {self.date_debut.strftime("%Hh%M")
+ } à {self.date_fin.strftime("%Hh%M")}"""
else:
return ""
def descr_duree(self) -> str:
- "Description de la durée pour affichages"
- if self.heure_debut is None and self.heure_fin is None:
+ "Description de la durée pour affichages ('3h' ou '2h30')"
+ if self.date_debut is None or self.date_fin is None:
return ""
- debut = self.heure_debut or DEFAULT_EVALUATION_TIME
- fin = self.heure_fin or DEFAULT_EVALUATION_TIME
- d = (fin.hour * 60 + fin.minute) - (debut.hour * 60 + debut.minute)
- duree = f"{d//60}h"
- if d % 60:
- duree += f"{d%60:02d}"
+ minutes = (self.date_fin - self.date_debut).seconds // 60
+ duree = f"{minutes // 60}h"
+ minutes = minutes % 60
+ if minutes != 0:
+ duree += f"{minutes:02d}"
return duree
+ def descr_date(self) -> str:
+ """Description de la date pour affichages
+ 'sans date'
+ 'le 21/9/2021 à 13h'
+ 'le 21/9/2021 de 13h à 14h30'
+ 'du 21/9/2021 à 13h30 au 23/9/2021 à 15h'
+ """
+ if self.date_debut is None:
+ return "sans date"
+
+ def _h(dt: datetime.datetime) -> str:
+ if dt.minute:
+ return dt.strftime("%Hh%M")
+ return f"{dt.hour}h"
+
+ if self.date_fin is None:
+ return (
+ f"le {self.date_debut.strftime('%d/%m/%Y')} à {_h(self.date_debut())}"
+ )
+ if self.date_debut.date() == self.date_fin.date(): # même jour
+ if self.date_debut.time() == self.date_fin.time():
+ return f"le {self.date_debut.strftime('%d/%m/%Y')} à {_h(self.date_debut())}"
+ return f"""le {self.date_debut.strftime('%d/%m/%Y')} de {
+ _h(self.date_debut())} à {_h(self.date_fin())}"""
+ # évaluation sur plus d'une journée
+ return f"""du {self.date_debut.strftime('%d/%m/%Y')} à {
+ _h(self.date_debut())} au {self.date_fin.strftime('%d/%m/%Y')} à {_h(self.date_fin())}"""
+
+ def heure_debut(self) -> str:
+ """L'heure de début (sans la date), en ISO.
+ Chaine vide si non renseignée."""
+ return self.date_debut.time().isoformat("minutes") if self.date_debut else ""
+
+ def heure_fin(self) -> str:
+ """L'heure de fin (sans la date), en ISO.
+ Chaine vide si non renseignée."""
+ return self.date_fin.time().isoformat("minutes") if self.date_fin else ""
+
def clone(self, not_copying=()):
"""Clone, not copying the given attrs
Attention: la copie n'a pas d'id avant le prochain commit
@@ -381,41 +439,28 @@ class EvaluationUEPoids(db.Model):
return f""
-# Fonction héritée de ScoDoc7 à refactorer
-def evaluation_enrich_dict(e: dict):
+# Fonction héritée de ScoDoc7
+def evaluation_enrich_dict(e: Evaluation, e_dict: dict):
"""add or convert some fields in an evaluation dict"""
# For ScoDoc7 compat
- heure_debut_dt = e["date_debut"].time()
- heure_fin_dt = e["date_fin"].time()
- e["heure_debut"] = heure_debut_dt.strftime("%Hh%M")
- e["heure_fin"] = heure_fin_dt.strftime("%Hh%M")
- e["jour_iso"] = e["date_debut"].isoformat() # XXX
- heure_debut, heure_fin = e["heure_debut"], e["heure_fin"]
- d = _time_duration_HhM(heure_debut, heure_fin)
- if d is not None:
- m = d % 60
- e["duree"] = "%dh" % (d / 60)
- if m != 0:
- e["duree"] += "%02d" % m
- else:
- e["duree"] = ""
- if heure_debut and (not heure_fin or heure_fin == heure_debut):
- e["descrheure"] = " à " + heure_debut
- elif heure_debut and heure_fin:
- e["descrheure"] = " de %s à %s" % (heure_debut, heure_fin)
- else:
- e["descrheure"] = ""
+ e_dict["heure_debut"] = e.date_debut.strftime("%Hh%M") if e.date_debut else ""
+ e_dict["heure_fin"] = e.date_fin.strftime("%Hh%M") if e.date_fin else ""
+ e_dict["jour_iso"] = e.date_debut.isoformat() if e.date_debut else ""
+ # Calcule durée en minutes
+ e_dict["descrheure"] = e.descr_heure()
+ e_dict["descrduree"] = e.descr_duree()
# matin, apresmidi: utile pour se referer aux absences:
-
- if e["jour"] and heure_debut_dt < datetime.time(12, 00):
- e["matin"] = 1
+ # note août 2023: si l'évaluation s'étend sur plusieurs jours,
+ # cet indicateur n'a pas grand sens
+ if e.date_debut and e.date_debut.time() < datetime.time(12, 00):
+ e_dict["matin"] = 1
else:
- e["matin"] = 0
- if e["jour"] and heure_fin_dt > datetime.time(12, 00):
- e["apresmidi"] = 1
+ e_dict["matin"] = 0
+ if e.date_fin and e.date_fin.time() > datetime.time(12, 00):
+ e_dict["apresmidi"] = 1
else:
- e["apresmidi"] = 0
- return e
+ e_dict["apresmidi"] = 0
+ return e_dict
def check_convert_evaluation_args(moduleimpl: ModuleImpl, data: dict):
@@ -426,71 +471,87 @@ def check_convert_evaluation_args(moduleimpl: ModuleImpl, data: dict):
May raise ScoValueError.
"""
# --- description
- description = data.get("description", "")
- if len(description) > scu.MAX_TEXT_LEN:
+ data["description"] = data.get("description", "") or ""
+ if len(data["description"]) > scu.MAX_TEXT_LEN:
raise ScoValueError("description too large")
+
# --- evaluation_type
try:
data["evaluation_type"] = int(data.get("evaluation_type", 0) or 0)
if not data["evaluation_type"] in VALID_EVALUATION_TYPES:
- raise ScoValueError("Invalid evaluation_type value")
- except ValueError:
- raise ScoValueError("Invalid evaluation_type value")
+ raise ScoValueError("invalid evaluation_type value")
+ except ValueError as exc:
+ raise ScoValueError("invalid evaluation_type value") from exc
# --- note_max (bareme)
note_max = data.get("note_max", 20.0) or 20.0
try:
note_max = float(note_max)
- except ValueError:
- raise ScoValueError("Invalid note_max value")
+ except ValueError as exc:
+ raise ScoValueError("invalid note_max value") from exc
if note_max < 0:
- raise ScoValueError("Invalid note_max value (must be positive or null)")
+ raise ScoValueError("invalid note_max value (must be positive or null)")
data["note_max"] = note_max
# --- coefficient
coef = data.get("coefficient", 1.0) or 1.0
try:
coef = float(coef)
- except ValueError:
- raise ScoValueError("Invalid coefficient value")
+ except ValueError as exc:
+ raise ScoValueError("invalid coefficient value") from exc
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
- # --- jour (date de l'évaluation)
- jour = data.get("jour", None)
- if jour and not isinstance(jour, datetime.date):
- if date_format == "dmy":
- y, m, d = [int(x) for x in ndb.DateDMYtoISO(jour).split("-")]
- jour = datetime.date(y, m, d)
- else: # ISO
- jour = datetime.date.fromisoformat(jour)
- formsemestre = moduleimpl.formsemestre
- if (jour > formsemestre.date_fin) or (jour < formsemestre.date_debut):
+ # --- date de l'évaluation
+ formsemestre = moduleimpl.formsemestre
+ date_debut = data.get("date_debut", None)
+ if date_debut:
+ if isinstance(date_debut, str):
+ data["date_debut"] = datetime.datetime.fromisoformat(date_debut)
+ if data["date_debut"].tzinfo is None:
+ data["date_debut"] = scu.TIME_ZONE.localize(data["date_debut"])
+ if not (
+ formsemestre.date_debut
+ <= data["date_debut"].date()
+ <= formsemestre.date_fin
+ ):
raise ScoValueError(
- f"""La date de l'évaluation ({jour.strftime("%d/%m/%Y")}) n'est pas dans le semestre !""",
+ f"""La date de début de l'évaluation ({
+ data["date_debut"].strftime("%d/%m/%Y")
+ }) n'est pas dans le semestre !""",
dest_url="javascript:history.back();",
)
- data["jour"] = jour
- # --- heures
- heure_debut = data.get("heure_debut", None)
- if heure_debut and not isinstance(heure_debut, datetime.time):
- if date_format == "dmy":
- data["heure_debut"] = heure_to_time(heure_debut)
- else: # ISO
- data["heure_debut"] = datetime.time.fromisoformat(heure_debut)
- heure_fin = data.get("heure_fin", None)
- if heure_fin and not isinstance(heure_fin, datetime.time):
- if date_format == "dmy":
- data["heure_fin"] = heure_to_time(heure_fin)
- else: # ISO
- data["heure_fin"] = datetime.time.fromisoformat(heure_fin)
- if jour and ((not heure_debut) or (not heure_fin)):
- raise ScoValueError("Les heures doivent être précisées")
- if heure_debut and heure_fin:
- duration = ((data["heure_fin"].hour * 60) + data["heure_fin"].minute) - (
- (data["heure_debut"].hour * 60) + data["heure_debut"].minute
- )
- if duration < 0 or duration > 60 * 12:
+ date_fin = data.get("date_fin", None)
+ if date_fin:
+ if isinstance(date_fin, str):
+ data["date_fin"] = datetime.datetime.fromisoformat(date_fin)
+ if data["date_fin"].tzinfo is None:
+ data["date_fin"] = scu.TIME_ZONE.localize(data["date_fin"])
+ if not (
+ formsemestre.date_debut <= data["date_fin"].date() <= formsemestre.date_fin
+ ):
+ raise ScoValueError(
+ f"""La date de fin de l'évaluation ({
+ data["date_fin"].strftime("%d/%m/%Y")
+ }) n'est pas dans le semestre !""",
+ dest_url="javascript:history.back();",
+ )
+ if date_debut and date_fin:
+ duration = data["date_fin"] - data["date_debut"]
+ if duration.total_seconds() < 0 or duration > MAX_EVALUATION_DURATION:
raise ScoValueError("Heures de l'évaluation incohérentes !")
+ # # --- heures
+ # heure_debut = data.get("heure_debut", None)
+ # if heure_debut and not isinstance(heure_debut, datetime.time):
+ # if date_format == "dmy":
+ # data["heure_debut"] = heure_to_time(heure_debut)
+ # else: # ISO
+ # data["heure_debut"] = datetime.time.fromisoformat(heure_debut)
+ # heure_fin = data.get("heure_fin", None)
+ # if heure_fin and not isinstance(heure_fin, datetime.time):
+ # if date_format == "dmy":
+ # data["heure_fin"] = heure_to_time(heure_fin)
+ # else: # ISO
+ # data["heure_fin"] = datetime.time.fromisoformat(heure_fin)
def heure_to_time(heure: str) -> datetime.time:
diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py
index e4690a06a2..677bfb1789 100644
--- a/app/models/formsemestre.py
+++ b/app/models/formsemestre.py
@@ -30,6 +30,7 @@ from app.models.but_refcomp import (
)
from app.models.config import ScoDocSiteConfig
from app.models.etudiants import Identite
+from app.models.evaluations import Evaluation
from app.models.formations import Formation
from app.models.groups import GroupDescr, Partition
from app.models.moduleimpls import ModuleImpl, ModuleImplInscription
@@ -350,6 +351,21 @@ class FormSemestre(db.Model):
_cache[key] = ues
return ues
+ def get_evaluations(self) -> list[Evaluation]:
+ "Liste de toutes les évaluations du semestre, triées par module/numero"
+ return (
+ Evaluation.query.join(ModuleImpl)
+ .filter_by(formsemestre_id=self.id)
+ .join(Module)
+ .order_by(
+ Module.numero,
+ Module.code,
+ Evaluation.numero,
+ Evaluation.date_debut.desc(),
+ )
+ .all()
+ )
+
@cached_property
def modimpls_sorted(self) -> list[ModuleImpl]:
"""Liste des modimpls du semestre (y compris bonus)
diff --git a/app/scodoc/sco_abs.py b/app/scodoc/sco_abs.py
index 8a04cac276..8ef9de03ac 100755
--- a/app/scodoc/sco_abs.py
+++ b/app/scodoc/sco_abs.py
@@ -509,7 +509,7 @@ def _get_abs_description(a, cursor=None):
return ""
-def list_abs_jour(date, am=True, pm=True, is_abs=True, is_just=None):
+def list_abs_jour(date, am=True, pm=True, is_abs=True, is_just=None) -> list[dict]:
"""Liste des absences et/ou justificatifs ce jour.
is_abs: None (peu importe), True, False
is_just: idem
@@ -535,7 +535,7 @@ WHERE A.jour = %(date)s
return A
-def list_abs_non_just_jour(date, am=True, pm=True):
+def list_abs_non_just_jour(date, am=True, pm=True) -> list[dict]:
"Liste des absences non justifiees ce jour"
cnx = ndb.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
diff --git a/app/scodoc/sco_abs_views.py b/app/scodoc/sco_abs_views.py
index 4af828c393..c6f99a2b90 100644
--- a/app/scodoc/sco_abs_views.py
+++ b/app/scodoc/sco_abs_views.py
@@ -972,12 +972,12 @@ def _tables_abs_etud(
FROM notes_evaluation eval,
notes_moduleimpl_inscription mi,
notes_moduleimpl m
- WHERE eval.jour = %(jour)s
+ WHERE DATE(eval.date_debut) = %(date_debut)s
and eval.moduleimpl_id = m.id
and mi.moduleimpl_id = m.id
and mi.etudid = %(etudid)s
""",
- {"jour": a["jour"].strftime("%Y-%m-%d"), "etudid": etudid},
+ {"date_debut": a["jour"], "etudid": etudid},
)
a["evals"] = cursor.dictfetchall()
cursor.execute(
diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py
index 1596a11fb8..cdd8eb7611 100644
--- a/app/scodoc/sco_bulletins.py
+++ b/app/scodoc/sco_bulletins.py
@@ -47,6 +47,7 @@ from app.comp.res_but import ResultatsSemestreBUT
from app.comp.res_compat import NotesTableCompat
from app.models import (
ApcParcours,
+ Evaluation,
Formation,
FormSemestre,
Identite,
@@ -482,6 +483,7 @@ def _ue_mod_bulletin(
mods = [] # result
ue_attente = False # true si une eval en attente dans cette UE
for modimpl in ue_modimpls:
+ modimpl_results = nt.modimpls_results.get(modimpl["moduleimpl_id"])
mod_attente = False
mod = modimpl.copy()
mod_moy = nt.get_etud_mod_moy(
@@ -531,10 +533,13 @@ def _ue_mod_bulletin(
scu.fmt_coef(modimpl["module"]["coefficient"]),
sco_users.user_info(modimpl["responsable_id"])["nomcomplet"],
)
- link_mod = (
- ''
- % (modimpl["moduleimpl_id"], mod["mod_descr_txt"])
- )
+ link_mod = f""""""
+
if sco_preferences.get_preference("bul_show_codemodules", formsemestre_id):
mod["code"] = modimpl["module"]["code"]
mod["code_html"] = link_mod + (mod["code"] or "") + ""
@@ -561,91 +566,88 @@ def _ue_mod_bulletin(
mod["code_txt"] = ""
mod["code_html"] = ""
# Evaluations: notes de chaque eval
- evals = nt.get_evals_in_mod(modimpl["moduleimpl_id"])
+ evaluations_completes = nt.get_modimpl_evaluations_completes(
+ modimpl["moduleimpl_id"]
+ )
+ # On liste séparément les éval. complètes ou non
mod["evaluations"] = []
- for e in evals:
- e = e.copy()
- if e["visibulletin"] or version == "long":
- # affiche "bonus" quand les points de malus sont négatifs
- if is_malus:
- val = e["notes"].get(etudid, {"value": "NP"})[
- "value"
- ] # NA si etud demissionnaire
- if val == "NP" or val > 0:
- e["name"] = "Points de malus sur cette UE"
- else:
- e["name"] = "Points de bonus sur cette UE"
+ mod["evaluations_incompletes"] = []
+ complete_eval_ids = {e.id for e in evaluations_completes}
+ all_evals: list[Evaluation] = Evaluation.query.filter_by(
+ moduleimpl_id=modimpl["moduleimpl_id"]
+ ).order_by(Evaluation.numero, Evaluation.date_debut)
+ # (plus ancienne d'abord)
+ for e in all_evals:
+ if not e.visibulletin and version != "long":
+ continue
+ is_complete = e.id in complete_eval_ids
+ e_dict = e.to_dict_bul()
+ # Note à l'évaluation:
+ val = modimpl_results.evals_notes[e.id].get(etudid, "NP")
+ # Affiche "bonus" quand les points de malus sont négatifs
+ if is_malus:
+ if val == "NP":
+ e_dict["name"] = "Points de bonus/malus sur cette UE"
+ elif val > 0:
+ e_dict["name"] = "Points de malus sur cette UE"
else:
- e["name"] = e["description"] or f"le {e['jour']}"
- e["target_html"] = url_for(
+ e_dict["name"] = "Points de bonus sur cette UE"
+ else:
+ e_dict[
+ "name"
+ ] = f"""{e.description or ""} {
+ e.descr_date()
+ if e.date_debut and not is_complete
+ else ""}"""
+ e_dict["target_html"] = url_for(
"notes.evaluation_listenotes",
scodoc_dept=g.scodoc_dept,
- evaluation_id=e["evaluation_id"],
+ evaluation_id=e.id,
format="html",
tf_submitted=1,
)
- e[
+ e_dict[
"name_html"
] = f"""{e['name']}"""
- val = e["notes"].get(etudid, {"value": "NP"})["value"]
- # val est NP si etud demissionnaire
- if val == "NP":
- e["note_txt"] = "nd"
- e["note_html"] = 'nd'
- e["coef_txt"] = scu.fmt_coef(e["coefficient"])
- else:
- # (-0.15) s'affiche "bonus de 0.15"
- if is_malus:
- val = abs(val)
- e["note_txt"] = scu.fmt_note(val, note_max=e["note_max"])
- e["note_html"] = e["note_txt"]
- if is_malus:
- e["coef_txt"] = ""
+ e_dict['target_html']}">{e_dict['name']}"""
+ if is_complete: # évaluation complète
+ # val est NP si etud demissionnaire
+ if val == "NP":
+ e_dict["note_txt"] = "nd"
+ e_dict["note_html"] = 'nd'
+ e_dict["coef_txt"] = scu.fmt_coef(e["coefficient"])
else:
- e["coef_txt"] = scu.fmt_coef(e["coefficient"])
- if e["evaluation_type"] == scu.EVALUATION_RATTRAPAGE:
- e["coef_txt"] = "rat."
- elif e["evaluation_type"] == scu.EVALUATION_SESSION2:
- e["coef_txt"] = "Ses. 2"
- if e["etat"]["evalattente"]:
+ # (-0.15) s'affiche "bonus de 0.15"
+ if is_malus:
+ val = abs(val)
+ e_dict["note_txt"] = e_dict["note_html"] = scu.fmt_note(
+ val, note_max=e.note_max
+ )
+ else: # évaluation incomplète: pas de note
+ e_dict["note_txt"] = e_dict["note_html"] = ""
+
+ if is_malus:
+ e_dict["coef_txt"] = ""
+ else:
+ e_dict["coef_txt"] = scu.fmt_coef(e.coefficient)
+ if e.evaluation_type == scu.EVALUATION_RATTRAPAGE:
+ e_dict["coef_txt"] = "rat."
+ elif e.evaluation_type == scu.EVALUATION_SESSION2:
+ e_dict["coef_txt"] = "Ses. 2"
+
+ if modimpl_results.evaluations_etat[e.id].nb_attente:
mod_attente = True # une eval en attente dans ce module
+
if ((not is_malus) or (val != "NP")) and (
- (
- e["evaluation_type"] == scu.EVALUATION_NORMALE
- or not np.isnan(val)
- )
+ (e.evaluation_type == scu.EVALUATION_NORMALE or not np.isnan(val))
):
# ne liste pas les eval malus sans notes
# ni les rattrapages et sessions 2 si pas de note
- mod["evaluations"].append(e)
+ if e.id in complete_eval_ids:
+ mod["evaluations"].append(e_dict)
+ else:
+ mod["evaluations_incompletes"].append(e_dict)
- # Evaluations incomplètes ou futures:
- mod["evaluations_incompletes"] = []
- if sco_preferences.get_preference("bul_show_all_evals", formsemestre_id):
- complete_eval_ids = set([e["evaluation_id"] for e in evals])
- all_evals = sco_evaluation_db.do_evaluation_list(
- args={"moduleimpl_id": modimpl["moduleimpl_id"]}
- )
- all_evals.reverse() # plus ancienne d'abord
- for e in all_evals:
- if e["evaluation_id"] not in complete_eval_ids:
- e = e.copy()
- mod["evaluations_incompletes"].append(e)
- e["name"] = (e["description"] or "") + " (%s)" % e["jour"]
- e["target_html"] = url_for(
- "notes.evaluation_listenotes",
- scodoc_dept=g.scodoc_dept,
- evaluation_id=e["evaluation_id"],
- tf_submitted=1,
- format="html",
- )
- e["name_html"] = '%s' % (
- e["target_html"],
- e["name"],
- )
- e["note_txt"] = e["note_html"] = ""
- e["coef_txt"] = scu.fmt_coef(e["coefficient"])
# Classement
if (
bul_show_mod_rangs
diff --git a/app/scodoc/sco_bulletins_json.py b/app/scodoc/sco_bulletins_json.py
index 53849f045c..9bb83358b7 100644
--- a/app/scodoc/sco_bulletins_json.py
+++ b/app/scodoc/sco_bulletins_json.py
@@ -37,7 +37,7 @@ from app import db, ScoDocJSONEncoder
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models import but_validations
-from app.models import Evaluation, Matiere, ModuleImpl, UniteEns
+from app.models import Evaluation, Matiere, UniteEns
from app.models.etudiants import Identite
from app.models.formsemestre import FormSemestre
@@ -46,7 +46,6 @@ import app.scodoc.notesdb as ndb
from app.scodoc import sco_assiduites
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_formsemestre
from app.scodoc import sco_groups
from app.scodoc import sco_photos
@@ -333,6 +332,7 @@ def _list_modimpls(
mod_moy = scu.fmt_note(nt.get_etud_mod_moy(modimpl["moduleimpl_id"], etudid))
if mod_moy == "NI": # ne mentionne pas les modules ou n'est pas inscrit
continue
+ modimpl_results = nt.modimpls_results.get(modimpl["moduleimpl_id"])
mod = modimpl["module"]
# if mod['ects'] is None:
# ects = ''
@@ -363,66 +363,42 @@ def _list_modimpls(
mod_dict["effectif"] = dict(value=nt.mod_rangs[modimpl["moduleimpl_id"]][1])
# --- notes de chaque eval:
- evals = nt.get_evals_in_mod(modimpl["moduleimpl_id"])
+ evaluations_completes = nt.get_modimpl_evaluations_completes(
+ modimpl["moduleimpl_id"]
+ )
mod_dict["evaluation"] = []
if version != "short":
- for e in evals:
- if e["visibulletin"] or version == "long":
- val = e["notes"].get(etudid, {"value": "NP"})["value"]
+ for e in evaluations_completes:
+ if e.visibulletin or version == "long":
+ # Note à l'évaluation:
+ val = modimpl_results.evals_notes[e.id].get(etudid, "NP")
# nb: val est NA si etud démissionnaire
- val = scu.fmt_note(val, note_max=e["note_max"])
- eval_dict = dict(
- jour=ndb.DateDMYtoISO(e["jour"], null_is_empty=True),
- heure_debut=ndb.TimetoISO8601(
- e["heure_debut"], null_is_empty=True
- ),
- heure_fin=ndb.TimetoISO8601(e["heure_fin"], null_is_empty=True),
- coefficient=e["coefficient"],
- evaluation_type=e["evaluation_type"],
- # CM : ajout pour permettre de faire le lien sur
- # les bulletins en ligne avec l'évaluation:
- evaluation_id=e["evaluation_id"],
- description=quote_xml_attr(e["description"]),
- 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"] = etat["mini"] # chaine, sur 20
- eval_dict["max"] = etat["maxi"]
- if prefs["bul_show_moypromo"]:
- eval_dict["moy"] = etat["moy"]
+ e_dict = e.to_dict_bul()
+ e_dict["note"] = scu.fmt_note(val, note_max=e.note_max)
- mod_dict["evaluation"].append(eval_dict)
+ if prefs["bul_show_minmax_eval"] or prefs["bul_show_moypromo"]:
+ # XXX à revoir pour utiliser modimplresult
+ etat = sco_evaluations.do_evaluation_etat(e.id)
+ if prefs["bul_show_minmax_eval"]:
+ e_dict["min"] = etat["mini"] # chaine, sur 20
+ e_dict["max"] = etat["maxi"]
+ if prefs["bul_show_moypromo"]:
+ e_dict["moy"] = etat["moy"]
+
+ mod_dict["evaluation"].append(e_dict)
# Evaluations incomplètes ou futures:
- complete_eval_ids = set([e["evaluation_id"] for e in evals])
+ complete_eval_ids = {e.id for e in evaluations_completes}
if prefs["bul_show_all_evals"]:
- evaluations = Evaluation.query.filter_by(
+ evaluations: list[Evaluation] = Evaluation.query.filter_by(
moduleimpl_id=modimpl["moduleimpl_id"]
).order_by(Evaluation.date_debut)
# plus ancienne d'abord
for e in evaluations:
if e.id not in complete_eval_ids:
- mod_dict["evaluation"].append(
- dict(
- date_debut=e.date_debut.isoformat()
- if e.date_debut
- else None,
- date_fin=e.date_fin.isoformat() if e.date_fin else None,
- coefficient=e.coefficient,
- description=quote_xml_attr(e.description or ""),
- incomplete="1",
- # Deprecated:
- jour=e.date_debut.isoformat() if e.date_debut else "",
- heure_debut=ndb.TimetoISO8601(
- e["heure_debut"], null_is_empty=True
- ),
- heure_fin=ndb.TimetoISO8601(
- e["heure_fin"], null_is_empty=True
- ),
- )
- )
+ e_dict = e.to_dict_bul()
+ e_dict["incomplete"] = 1
+ mod_dict["evaluation"].append(e_dict)
modules_dict.append(mod_dict)
return modules_dict
diff --git a/app/scodoc/sco_bulletins_xml.py b/app/scodoc/sco_bulletins_xml.py
index ca32ec46ac..4d96009b93 100644
--- a/app/scodoc/sco_bulletins_xml.py
+++ b/app/scodoc/sco_bulletins_xml.py
@@ -242,6 +242,7 @@ def make_xml_formsemestre_bulletinetud(
# Liste les modules de l'UE
ue_modimpls = [mod for mod in modimpls if mod["module"]["ue_id"] == ue["ue_id"]]
for modimpl in ue_modimpls:
+ modimpl_results = nt.modimpls_results.get(modimpl["moduleimpl_id"])
mod_moy = scu.fmt_note(
nt.get_etud_mod_moy(modimpl["moduleimpl_id"], etudid)
)
@@ -290,33 +291,24 @@ def make_xml_formsemestre_bulletinetud(
)
)
# --- notes de chaque eval:
- evals = nt.get_evals_in_mod(modimpl["moduleimpl_id"])
+ evaluations_completes = nt.get_modimpl_evaluations_completes(
+ modimpl["moduleimpl_id"]
+ )
if version != "short":
- for e in evals:
- if e["visibulletin"] or version == "long":
- x_eval = Element(
- "evaluation",
- jour=ndb.DateDMYtoISO(e["jour"], null_is_empty=True),
- heure_debut=ndb.TimetoISO8601(
- e["heure_debut"], null_is_empty=True
- ),
- heure_fin=ndb.TimetoISO8601(
- e["heure_fin"], null_is_empty=True
- ),
- coefficient=str(e["coefficient"]),
- evaluation_type=str(e["evaluation_type"]),
- description=quote_xml_attr(e["description"]),
- # notes envoyées sur 20, ceci juste pour garder trace:
- note_max_origin=str(e["note_max"]),
- )
+ for e in evaluations_completes:
+ if e.visibulletin or version == "long":
+ # pour xml, tout convertir en chaines
+ e_dict = {k: str(v) for k, v in e.to_dict_bul().items()}
+ # notes envoyées sur 20, ceci juste pour garder trace:
+ e_dict["note_max_origin"] = str(e.note_max)
+ x_eval = Element("evaluation", **e_dict)
x_mod.append(x_eval)
- val = e["notes"].get(etudid, {"value": "NP"})[
- "value"
- ] # NA si etud demissionnaire
- val = scu.fmt_note(val, note_max=e["note_max"])
+ # Note à l'évaluation:
+ val = modimpl_results.evals_notes[e.id].get(etudid, "NP")
+ val = scu.fmt_note(val, note_max=e.note_max)
x_eval.append(Element("note", value=val))
# Evaluations incomplètes ou futures:
- complete_eval_ids = set([e["evaluation_id"] for e in evals])
+ complete_eval_ids = {e.id for e in evaluations_completes}
if sco_preferences.get_preference(
"bul_show_all_evals", formsemestre_id
):
@@ -325,21 +317,8 @@ def make_xml_formsemestre_bulletinetud(
).order_by(Evaluation.date_debut)
for e in evaluations:
if e.id not in complete_eval_ids:
- x_eval = Element(
- "evaluation",
- jour=ndb.DateDMYtoISO(e["jour"], null_is_empty=True),
- heure_debut=ndb.TimetoISO8601(
- e["heure_debut"], null_is_empty=True
- ),
- heure_fin=ndb.TimetoISO8601(
- e["heure_fin"], null_is_empty=True
- ),
- coefficient=str(e["coefficient"]),
- description=quote_xml_attr(e["description"]),
- incomplete="1",
- # notes envoyées sur 20, ceci juste pour garder trace:
- note_max_origin=str(e["note_max"] or ""),
- )
+ e_dict = e.to_dict_bul()
+ x_eval = Element("evaluation", **e_dict)
x_mod.append(x_eval)
# UE capitalisee (listee seulement si meilleure que l'UE courante)
if ue_status["is_capitalized"]:
diff --git a/app/scodoc/sco_evaluation_check_abs.py b/app/scodoc/sco_evaluation_check_abs.py
index d34137bc0b..87853cc943 100644
--- a/app/scodoc/sco_evaluation_check_abs.py
+++ b/app/scodoc/sco_evaluation_check_abs.py
@@ -29,8 +29,9 @@
"""
from flask import url_for, g
+from app import db
+from app.models import Evaluation, Identite
import app.scodoc.sco_utils as scu
-import app.scodoc.notesdb as ndb
from app.scodoc import html_sco_header
from app.scodoc import sco_abs
from app.scodoc import sco_etud
@@ -40,25 +41,8 @@ from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups
from app.scodoc import sco_moduleimpl
-# matin et/ou après-midi ?
-def _eval_demijournee(E):
- "1 si matin, 0 si apres midi, 2 si toute la journee"
- am, pm = False, False
- if E["heure_debut"] < "13:00":
- am = True
- if E["heure_fin"] > "13:00":
- pm = True
- if am and pm:
- demijournee = 2
- elif am:
- demijournee = 1
- else:
- demijournee = 0
- pm = True
- return am, pm, demijournee
-
-def evaluation_check_absences(evaluation_id):
+def evaluation_check_absences(evaluation: Evaluation):
"""Vérifie les absences au moment de cette évaluation.
Cas incohérents que l'on peut rencontrer pour chaque étudiant:
note et absent
@@ -66,51 +50,58 @@ def evaluation_check_absences(evaluation_id):
ABS et absent justifié
EXC et pas noté absent
EXC et pas justifie
- Ramene 3 listes d'etudid
+ Ramene 5 listes d'etudid
"""
- E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
- if not E["jour"]:
+ if not evaluation.date_debut:
return [], [], [], [], [] # evaluation sans date
- am, pm, demijournee = _eval_demijournee(E)
+ am, pm = evaluation.is_matin(), evaluation.is_apresmidi()
# Liste les absences à ce moment:
- A = sco_abs.list_abs_jour(ndb.DateDMYtoISO(E["jour"]), am=am, pm=pm)
- As = set([x["etudid"] for x in A]) # ensemble des etudiants absents
- NJ = sco_abs.list_abs_non_just_jour(ndb.DateDMYtoISO(E["jour"]), am=am, pm=pm)
- NJs = set([x["etudid"] for x in NJ]) # ensemble des etudiants absents non justifies
- Just = sco_abs.list_abs_jour(
- ndb.DateDMYtoISO(E["jour"]), am=am, pm=pm, is_abs=None, is_just=True
+ absences = sco_abs.list_abs_jour(evaluation.date_debut, am=am, pm=pm)
+ abs_etudids = set([x["etudid"] for x in absences]) # ensemble des etudiants absents
+ abs_non_just = sco_abs.list_abs_non_just_jour(
+ evaluation.date_debut.date(), am=am, pm=pm
)
- Justs = set([x["etudid"] for x in Just]) # ensemble des etudiants avec justif
+ abs_nj_etudids = set(
+ [x["etudid"] for x in abs_non_just]
+ ) # ensemble des etudiants absents non justifies
+ justifs = sco_abs.list_abs_jour(
+ evaluation.date_debut.date(), am=am, pm=pm, is_abs=None, is_just=True
+ )
+ just_etudids = set(
+ [x["etudid"] for x in justifs]
+ ) # ensemble des etudiants avec justif
# Les notes:
- notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
+ notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation.id)
ValButAbs = [] # une note mais noté absent
AbsNonSignalee = [] # note ABS mais pas noté absent
ExcNonSignalee = [] # note EXC mais pas noté absent
ExcNonJust = [] # note EXC mais absent non justifie
AbsButExc = [] # note ABS mais justifié
for etudid, _ in sco_groups.do_evaluation_listeetuds_groups(
- evaluation_id, getallstudents=True
+ evaluation.id, getallstudents=True
):
if etudid in notes_db:
val = notes_db[etudid]["value"]
if (
- val != None and val != scu.NOTES_NEUTRALISE and val != scu.NOTES_ATTENTE
- ) and etudid in As:
+ val is not None
+ and val != scu.NOTES_NEUTRALISE
+ and val != scu.NOTES_ATTENTE
+ ) and etudid in abs_etudids:
# note valide et absent
ValButAbs.append(etudid)
- if val is None and not etudid in As:
+ if val is None and not etudid in abs_etudids:
# absent mais pas signale comme tel
AbsNonSignalee.append(etudid)
- if val == scu.NOTES_NEUTRALISE and not etudid in As:
+ if val == scu.NOTES_NEUTRALISE and not etudid in abs_etudids:
# Neutralisé mais pas signale absent
ExcNonSignalee.append(etudid)
- if val == scu.NOTES_NEUTRALISE and etudid in NJs:
+ if val == scu.NOTES_NEUTRALISE and etudid in abs_nj_etudids:
# EXC mais pas justifié
ExcNonJust.append(etudid)
- if val is None and etudid in Justs:
+ if val is None and etudid in just_etudids:
# ABS mais justificatif
AbsButExc.append(etudid)
@@ -119,9 +110,16 @@ def evaluation_check_absences(evaluation_id):
def evaluation_check_absences_html(evaluation_id, with_header=True, show_ok=True):
"""Affiche état vérification absences d'une évaluation"""
-
- E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
- am, pm, demijournee = _eval_demijournee(E)
+ evaluation: Evaluation = db.session.get(Evaluation, evaluation_id)
+ am, pm = evaluation.is_matin(), evaluation.is_apresmidi()
+ # 1 si matin, 0 si apres midi, 2 si toute la journee:
+ match am, pm:
+ case False, True:
+ demijournee = 0
+ case True, False:
+ demijournee = 1
+ case _:
+ demijournee = 2
(
ValButAbs,
@@ -129,19 +127,23 @@ def evaluation_check_absences_html(evaluation_id, with_header=True, show_ok=True
ExcNonSignalee,
ExcNonJust,
AbsButExc,
- ) = evaluation_check_absences(evaluation_id)
+ ) = evaluation_check_absences(evaluation)
if with_header:
H = [
html_sco_header.html_sem_header("Vérification absences à l'évaluation"),
- sco_evaluations.evaluation_describe(evaluation_id=evaluation_id),
- """Vérification de la cohérence entre les notes saisies et les absences signalées.
""",
+ sco_evaluations.evaluation_describe(evaluation_id=evaluation.id),
+ """Vérification de la cohérence entre les notes saisies
+ et les absences signalées.
""",
]
else:
# pas de header, mais un titre
H = [
- """%s du %s """
- % (E["description"], E["jour"])
+ f"""{
+ evaluation.description or "évaluation"
+ } du {
+ evaluation.date_debut.strftime("%d/%m/%Y") if evaluation.date_debut else ""
+ } """
]
if (
not ValButAbs
@@ -157,26 +159,27 @@ def evaluation_check_absences_html(evaluation_id, with_header=True, show_ok=True
if not etudids and show_ok:
H.append("
aucun")
for etudid in etudids:
- etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
+ etud: Identite = db.session.get(Identite, etudid)
H.append(
- ''
- % url_for(
- "scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud["etudid"]
- )
- + "%(nomprenom)s" % etud
+ f"""{etud.nomprenom}"""
)
if linkabs:
- H.append(
- f"""signaler cette absence"""
+ moduleimpl_id=evaluation.moduleimpl_id,
+ )
+ H.append(
+ f"""signaler cette absence"""
)
H.append("")
H.append("")
@@ -231,7 +234,7 @@ def formsemestre_check_absences_html(formsemestre_id):
# Modules, dans l'ordre
Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
for M in Mlist:
- evals = sco_evaluation_db.do_evaluation_list(
+ evals = sco_evaluation_db.get_evaluation_dict(
{"moduleimpl_id": M["moduleimpl_id"]}
)
if evals:
diff --git a/app/scodoc/sco_evaluation_db.py b/app/scodoc/sco_evaluation_db.py
index f0af65eac0..d7b7b7ed1c 100644
--- a/app/scodoc/sco_evaluation_db.py
+++ b/app/scodoc/sco_evaluation_db.py
@@ -37,14 +37,13 @@ from flask_login import current_user
from app import db, log
from app.models import Evaluation, ModuleImpl, ScolarNews
-from app.models.evaluations import evaluation_enrich_dict, check_convert_evaluation_args
+from app.models.evaluations import check_convert_evaluation_args
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
from app.scodoc import sco_cache
from app.scodoc import sco_moduleimpl
-from app.scodoc import sco_permissions_check
_evaluationEditor = ndb.EditableTable(
@@ -53,9 +52,8 @@ _evaluationEditor = ndb.EditableTable(
(
"evaluation_id",
"moduleimpl_id",
- "jour",
- "heure_debut",
- "heure_fin",
+ "date_debut",
+ "date_fin",
"description",
"note_max",
"coefficient",
@@ -64,15 +62,11 @@ _evaluationEditor = ndb.EditableTable(
"evaluation_type",
"numero",
),
- sortkey="numero desc, jour desc, heure_debut desc", # plus recente d'abord
+ sortkey="numero, date_debut desc", # plus recente d'abord
output_formators={
- "jour": ndb.DateISOtoDMY,
"numero": ndb.int_null_is_zero,
},
input_formators={
- "jour": ndb.DateDMYtoISO,
- "heure_debut": ndb.TimetoISO8601, # converti par evaluation_enrich_dict
- "heure_fin": ndb.TimetoISO8601, # converti par evaluation_enrich_dict
"visibulletin": bool,
"publish_incomplete": bool,
"evaluation_type": int,
@@ -80,8 +74,9 @@ _evaluationEditor = ndb.EditableTable(
)
-def do_evaluation_list(args, sortkey=None):
- """List evaluations, sorted by numero (or most recent date first).
+def get_evaluation_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'
@@ -89,13 +84,8 @@ def do_evaluation_list(args, sortkey=None):
'apresmidi' : 1 (termine après 12:00) ou 0
'descrheure' : ' de 15h00 à 16h30'
"""
- cnx = ndb.GetDBConnexion()
- evals = _evaluationEditor.list(cnx, args, sortkey=sortkey)
# calcule duree (chaine de car.) de chaque evaluation et ajoute jour_iso, matin, apresmidi
- for e in evals:
- evaluation_enrich_dict(e)
-
- return evals
+ return [e.to_dict() for e in Evaluation.query.filter_by(**args)]
def do_evaluation_list_in_formsemestre(formsemestre_id):
@@ -103,7 +93,7 @@ def do_evaluation_list_in_formsemestre(formsemestre_id):
mods = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
evals = []
for modimpl in mods:
- evals += do_evaluation_list(args={"moduleimpl_id": modimpl["moduleimpl_id"]})
+ evals += get_evaluation_dict(args={"moduleimpl_id": modimpl["moduleimpl_id"]})
return evals
@@ -219,12 +209,12 @@ def moduleimpl_evaluation_move(evaluation_id: int, after=0, redirect=1):
Evaluation.moduleimpl_evaluation_renumber(
evaluation.moduleimpl, only_if_unumbered=True
)
- e = do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
+ e = get_evaluation_dict(args={"evaluation_id": evaluation_id})[0]
after = int(after) # 0: deplace avant, 1 deplace apres
if after not in (0, 1):
raise ValueError('invalid value for "after"')
- mod_evals = do_evaluation_list({"moduleimpl_id": e["moduleimpl_id"]})
+ mod_evals = get_evaluation_dict({"moduleimpl_id": e["moduleimpl_id"]})
if len(mod_evals) > 1:
idx = [p["evaluation_id"] for p in mod_evals].index(evaluation_id)
neigh = None # object to swap with
diff --git a/app/scodoc/sco_evaluation_edit.py b/app/scodoc/sco_evaluation_edit.py
index f40a30e945..f1b21ca796 100644
--- a/app/scodoc/sco_evaluation_edit.py
+++ b/app/scodoc/sco_evaluation_edit.py
@@ -27,7 +27,7 @@
"""Formulaire ajout/édition d'une évaluation
"""
-
+import datetime
import time
import flask
@@ -38,6 +38,7 @@ from flask import request
from app import db
from app.models import Evaluation, FormSemestre, ModuleImpl
+from app.models.evaluations import heure_to_time
import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import ModuleType
@@ -47,7 +48,6 @@ from app.scodoc import html_sco_header
from app.scodoc import sco_evaluations
from app.scodoc import sco_evaluation_db
from app.scodoc import sco_moduleimpl
-from app.scodoc import sco_permissions_check
from app.scodoc import sco_preferences
@@ -139,15 +139,8 @@ def evaluation_create_form(
heures = [f"{h:02d}h{m:02d}" for h in range(8, 19) for m in (0, 30)]
#
- initvalues["visibulletin"] = initvalues.get("visibulletin", True)
- if initvalues["visibulletin"]:
- initvalues["visibulletinlist"] = ["X"]
- else:
- initvalues["visibulletinlist"] = []
initvalues["coefficient"] = initvalues.get("coefficient", 1.0)
vals = scu.get_request_args()
- if vals.get("tf_submitted", False) and "visibulletinlist" not in vals:
- vals["visibulletinlist"] = []
#
ue_coef_dict = modimpl.module.get_ue_coef_dict()
if is_apc: # BUT: poids vers les UE
@@ -236,11 +229,9 @@ def evaluation_create_form(
},
),
(
- "visibulletinlist",
+ "visibulletin",
{
- "input_type": "checkbox",
- "allowed_values": ["X"],
- "labels": [""],
+ "input_type": "boolcheckbox",
"title": "Visible sur bulletins",
"explanation": "(pour les bulletins en version intermédiaire)",
},
@@ -349,15 +340,37 @@ def evaluation_create_form(
return flask.redirect(dest_url)
else:
# form submission
- if tf[2]["visibulletinlist"]:
- tf[2]["visibulletin"] = True
+ args = tf[2]
+ # modifie le codage des dates
+ # (nb: ce formulaire ne permet de créer que des évaluation sur la même journée)
+ if args.get("jour"):
+ try:
+ date_debut = datetime.datetime.strptime(args["jour"], "%d/%m/%Y")
+ except ValueError as exc:
+ raise ScoValueError("Date (j/m/a) invalide") from exc
else:
- tf[2]["visibulletin"] = False
+ date_debut = None
+ args.pop("jour", None)
+ if args.get("heure_debut"):
+ try:
+ heure_debut = heure_to_time(args["heure_debut"])
+ except ValueError as exc:
+ raise ScoValueError("Heure début invalide") from exc
+ args["date_debut"] = datetime.datetime.combine(date_debut, heure_debut)
+ args.pop("heure_debut", None)
+ if args.get("heure_fin"):
+ try:
+ heure_fin = heure_to_time(args["heure_fin"])
+ except ValueError as exc:
+ raise ScoValueError("Heure fin invalide") from exc
+ args["date_fin"] = datetime.datetime.combine(date_debut, heure_fin)
+ args.pop("heure_fin", None)
+ #
if edit:
- sco_evaluation_db.do_evaluation_edit(tf[2])
+ evaluation.from_dict(args)
else:
# création d'une evaluation
- evaluation = Evaluation.create(moduleimpl=modimpl, **tf[2])
+ evaluation = Evaluation.create(moduleimpl=modimpl, **args)
db.session.add(evaluation)
db.session.commit()
evaluation_id = evaluation.id
@@ -366,6 +379,6 @@ def evaluation_create_form(
evaluation = db.session.get(Evaluation, evaluation_id)
for ue in sem_ues:
evaluation.set_ue_poids(ue, tf[2][f"poids_{ue.id}"])
- db.session.add(evaluation)
- db.session.commit()
+ db.session.add(evaluation)
+ db.session.commit()
return flask.redirect(dest_url)
diff --git a/app/scodoc/sco_evaluation_recap.py b/app/scodoc/sco_evaluation_recap.py
index c55843ca67..a5896ceb50 100644
--- a/app/scodoc/sco_evaluation_recap.py
+++ b/app/scodoc/sco_evaluation_recap.py
@@ -126,8 +126,8 @@ def evaluations_recap_table(formsemestre: FormSemestre) -> list[dict]:
evaluation_id=evaluation_id,
),
"_titre_target_attrs": 'class="discretelink"',
- "date": e.jour.strftime("%d/%m/%Y") if e.jour else "",
- "_date_order": e.jour.isoformat() if e.jour else "",
+ "date": e.date_debut.strftime("%d/%m/%Y") if e.date_debut else "",
+ "_date_order": e.date_debut.isoformat() if e.date_debut else "",
"complete": "oui" if eval_etat.is_complete else "non",
"_complete_target": "#",
"_complete_target_attrs": 'class="bull_link" title="prise en compte dans les moyennes"'
diff --git a/app/scodoc/sco_evaluations.py b/app/scodoc/sco_evaluations.py
index 12440d1126..4b14a6328e 100644
--- a/app/scodoc/sco_evaluations.py
+++ b/app/scodoc/sco_evaluations.py
@@ -101,6 +101,14 @@ def do_evaluation_etat(
) -> dict:
"""Donne infos sur l'état de l'évaluation.
Ancienne fonction, lente: préférer ModuleImplResults pour tout calcul.
+ XXX utilisée par de très nombreuses fonctions, dont
+ - _eval_etat 1/1/1
+ 'jour': datetime.date(2015, 11, 3), // vide => 1/1/1900
'moduleimpl_id': 'GEAMIP80490',
'note_max': 20.0,
'numero': 0,
@@ -323,11 +332,11 @@ def do_evaluation_list_in_sem(formsemestre_id, with_etat=True):
'visibulletin': 1} ]
"""
- req = """SELECT E.id AS evaluation_id, E.*
+ req = """SELECT E.id AS evaluation_id, E.*
FROM notes_evaluation E, notes_moduleimpl MI
WHERE MI.formsemestre_id = %(formsemestre_id)s
and MI.id = E.moduleimpl_id
- ORDER BY MI.id, numero desc, jour desc, heure_debut DESC
+ ORDER BY MI.id, numero desc, date_debut desc
"""
cnx = ndb.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
@@ -335,9 +344,9 @@ def do_evaluation_list_in_sem(formsemestre_id, with_etat=True):
res = cursor.dictfetchall()
# etat de chaque evaluation:
for r in res:
- r["jour"] = r["jour"] or datetime.date(1900, 1, 1) # pour les comparaisons
if with_etat:
r["etat"] = do_evaluation_etat(r["evaluation_id"])
+ r["jour"] = r["date_debut"] or datetime.date(1900, 1, 1)
return res
@@ -379,7 +388,20 @@ def _eval_etat(evals):
def do_evaluation_etat_in_sem(formsemestre_id):
"""-> nb_eval_completes, nb_evals_en_cours, nb_evals_vides,
- date derniere modif, attente"""
+ date derniere modif, attente
+
+ XXX utilisé par
+ - formsemestre_status_head
+ - gen_formsemestre_recapcomplet_xml
+ - gen_formsemestre_recapcomplet_json
+
+ "nb_evals_completes"
+ "nb_evals_en_cours"
+ "nb_evals_vides"
+ "date_derniere_note"
+ "last_modif"
+ "attente"
+ """
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
evals = nt.get_evaluations_etats()
@@ -403,88 +425,97 @@ def formsemestre_evaluations_cal(formsemestre_id):
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
- evals = nt.get_evaluations_etats()
- nb_evals = len(evals)
+ evaluations = formsemestre.get_evaluations() # TODO
+ nb_evals = len(evaluations)
color_incomplete = "#FF6060"
color_complete = "#A0FFA0"
color_futur = "#70E0FF"
- today = time.strftime("%Y-%m-%d")
-
- year = formsemestre.date_debut.year
- if formsemestre.date_debut.month < 8:
- year -= 1 # calendrier septembre a septembre
+ year = formsemestre.annee_scolaire()
events = {} # (day, halfday) : event
- for e in evals:
- etat = e["etat"]
- if not e["jour"]:
- continue
- day = e["jour"].strftime("%Y-%m-%d")
- mod = sco_moduleimpl.moduleimpl_withmodule_list(
- moduleimpl_id=e["moduleimpl_id"]
- )[0]
- txt = mod["module"]["code"] or mod["module"]["abbrev"] or "eval"
- if e["heure_debut"]:
- debut = e["heure_debut"].strftime("%Hh%M")
+ for e in evaluations:
+ if e.date_debut is None:
+ continue # éval. sans date
+ txt = e.moduleimpl.module.code or e.moduleimpl.module.abbrev or "éval."
+ if e.date_debut == e.date_fin:
+ heure_debut_txt, heure_fin_txt = "?", "?"
else:
- debut = "?"
- if e["heure_fin"]:
- fin = e["heure_fin"].strftime("%Hh%M")
- else:
- fin = "?"
- description = "%s, de %s à %s" % (mod["module"]["titre"], debut, fin)
- if etat["evalcomplete"]:
+ heure_debut_txt = e.date_debut.strftime("%Hh%M") if e.date_debut else "?"
+ heure_fin_txt = e.date_fin.strftime("%Hh%M") if e.date_fin else "?"
+
+ description = f"""{
+ e.moduleimpl.module.titre
+ }, de {heure_debut_txt} à {heure_fin_txt}"""
+
+ # Etat (notes completes) de l'évaluation:
+ modimpl_result = nt.modimpls_results[e.moduleimpl.id]
+ if modimpl_result.evaluations_etat[e.id].is_complete:
color = color_complete
else:
color = color_incomplete
- if day > today:
+ if e.date_debut > datetime.datetime.now(scu.TIME_ZONE):
color = color_futur
- href = "moduleimpl_status?moduleimpl_id=%s" % e["moduleimpl_id"]
- # if e['heure_debut'].hour < 12:
- # halfday = True
- # else:
- # halfday = False
- if not day in events:
- # events[(day,halfday)] = [day, txt, color, href, halfday, description, mod]
- events[day] = [day, txt, color, href, description, mod]
+ href = url_for(
+ "notes.moduleimpl_status",
+ scodoc_dept=g.scodoc_dept,
+ moduleimpl_id=e.moduleimpl_id,
+ )
+ day = e.date_debut.date().isoformat() # yyyy-mm-dd
+ event = events.get(day)
+ if not event:
+ events[day] = [day, txt, color, href, description, e.moduleimpl]
else:
- e = events[day]
- if e[-1]["moduleimpl_id"] != mod["moduleimpl_id"]:
+ if event[-1].id != e.moduleimpl.id:
# plusieurs evals de modules differents a la meme date
- e[1] += ", " + txt
- e[4] += ", " + description
- if not etat["evalcomplete"]:
- e[2] = color_incomplete
- if day > today:
- e[2] = color_futur
+ event[1] += ", " + txt
+ event[4] += ", " + description
+ if color == color_incomplete:
+ event[2] = color_incomplete
+ if color == color_futur:
+ event[2] = color_futur
- CalHTML = sco_abs.YearTable(
+ cal_html = sco_abs.YearTable(
year, events=list(events.values()), halfday=False, pad_width=None
)
- H = [
+ return f"""
+ {
html_sco_header.html_sem_header(
"Evaluations du semestre",
cssstyles=["css/calabs.css"],
- ),
- '',
- CalHTML,
- "
",
- "soit %s évaluations planifiées;" % nb_evals,
- """
- en rouge les évaluations passées auxquelles il manque des notes
- - en vert les évaluations déjà notées
- - en bleu les évaluations futures
"""
- % (color_incomplete, color_complete, color_futur),
- """voir les délais de correction
- """
- % (formsemestre_id,),
- html_sco_header.sco_footer(),
- ]
- return "\n".join(H)
+ )
+ }
+
+ { cal_html }
+
+ soit {nb_evals} évaluations planifiées;
+
+
+ - en rouge
+ les évaluations passées auxquelles il manque des notes
+
+ - en vert
+ les évaluations déjà notées
+
+ - en bleu
+ les évaluations futures
+
+
+ voir les délais de correction
+
+ { html_sco_header.sco_footer() }
+ """
-def evaluation_date_first_completion(evaluation_id):
+def evaluation_date_first_completion(evaluation_id) -> datetime.datetime:
"""Première date à laquelle l'évaluation a été complète
ou None si actuellement incomplète
"""
@@ -496,7 +527,7 @@ def evaluation_date_first_completion(evaluation_id):
# Il faut considerer les inscriptions au semestre
# (pour avoir l'etat et le groupe) et aussi les inscriptions
# au module (pour gerer les modules optionnels correctement)
- # E = do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
+ # E = get_evaluation_dict({"id":evaluation_id})[0]
# M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
# formsemestre_id = M["formsemestre_id"]
# insem = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits( formsemestre_id)
@@ -536,40 +567,44 @@ def formsemestre_evaluations_delai_correction(formsemestre_id, format="html"):
N'indique pas les évaluations de rattrapage ni celles des modules de bonus/malus.
"""
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
- nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
-
- evals = nt.get_evaluations_etats()
- T = []
- for e in evals:
- M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=e["moduleimpl_id"])[0]
- Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
- if (e["evaluation_type"] != scu.EVALUATION_NORMALE) or (
- Mod["module_type"] == ModuleType.MALUS
+ evaluations = formsemestre.get_evaluations()
+ rows = []
+ for e in evaluations:
+ if (e.evaluation_type != scu.EVALUATION_NORMALE) or (
+ e.moduleimpl.module.module_type == ModuleType.MALUS
):
continue
- e["date_first_complete"] = evaluation_date_first_completion(e["evaluation_id"])
- if e["date_first_complete"]:
- e["delai_correction"] = (e["date_first_complete"].date() - e["jour"]).days
+ date_first_complete = evaluation_date_first_completion(e.id)
+ if date_first_complete and e.date_fin:
+ delai_correction = (date_first_complete.date() - e.date_fin).days
else:
- e["delai_correction"] = None
+ delai_correction = None
- e["module_code"] = Mod["code"]
- e["_module_code_target"] = url_for(
- "notes.moduleimpl_status",
- scodoc_dept=g.scodoc_dept,
- moduleimpl_id=M["moduleimpl_id"],
+ rows.append(
+ {
+ "date_first_complete": date_first_complete,
+ "delai_correction": delai_correction,
+ "jour": e.date_debut.strftime("%d/%m/%Y")
+ if e.date_debut
+ else "sans date",
+ "_jour_target": url_for(
+ "notes.evaluation_listenotes",
+ scodoc_dept=g.scodoc_dept,
+ evaluation_id=e["evaluation_id"],
+ ),
+ "module_code": e.moduleimpl.module.code,
+ "_module_code_target": url_for(
+ "notes.moduleimpl_status",
+ scodoc_dept=g.scodoc_dept,
+ moduleimpl_id=e.moduleimpl.id,
+ ),
+ "module_titre": e.moduleimpl.module.abbrev or e.moduleimpl.module.titre,
+ "responsable_id": e.moduleimpl.responsable_id,
+ "responsable_nomplogin": sco_users.user_info(
+ e.moduleimpl.responsable_id
+ )["nomplogin"],
+ }
)
- e["module_titre"] = Mod["titre"]
- e["responsable_id"] = M["responsable_id"]
- e["responsable_nomplogin"] = sco_users.user_info(M["responsable_id"])[
- "nomplogin"
- ]
- e["_jour_target"] = url_for(
- "notes.evaluation_listenotes",
- scodoc_dept=g.scodoc_dept,
- evaluation_id=e["evaluation_id"],
- )
- T.append(e)
columns_ids = (
"module_code",
@@ -592,16 +627,14 @@ def formsemestre_evaluations_delai_correction(formsemestre_id, format="html"):
tab = GenTable(
titles=titles,
columns_ids=columns_ids,
- rows=T,
+ rows=rows,
html_class="table_leftalign table_coldate",
html_sortable=True,
html_title="Correction des évaluations du semestre
",
caption="Correction des évaluations du semestre",
preferences=sco_preferences.SemPreferences(formsemestre_id),
base_url="%s?formsemestre_id=%s" % (request.base_url, formsemestre_id),
- origin="Généré par %s le " % sco_version.SCONAME
- + scu.timedate_human_repr()
- + "",
+ origin=f"""Généré par {sco_version.SCONAME} le {scu.timedate_human_repr()}""",
filename=scu.make_filename("evaluations_delais_" + formsemestre.titre_annee()),
)
return tab.make_page(format=format)
@@ -612,7 +645,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True, link_saisie=True):
"""HTML description of evaluation, for page headers
edit_in_place: allow in-place editing when permitted (not implemented)
"""
- E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
+ E = sco_evaluation_db.get_evaluation_dict({"evaluation_id": evaluation_id})[0]
moduleimpl_id = E["moduleimpl_id"]
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py
index 9229513797..c15f24d4a1 100644
--- a/app/scodoc/sco_excel.py
+++ b/app/scodoc/sco_excel.py
@@ -512,8 +512,7 @@ def excel_feuille_saisie(evaluation: "Evaluation", titreannee, description, line
# description evaluation
ws.append_single_cell_row(scu.unescape_html(description), style_titres)
ws.append_single_cell_row(
- "Evaluation du %s (coef. %g)"
- % (evaluation.jour or "sans date", evaluation.coefficient or 0.0),
+ f"Evaluation {evaluation.descr_date()} (coef. {(evaluation.coefficient or 0.0):g})",
style,
)
# ligne blanche
diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py
index 41347f2d66..7b1ceb41a9 100644
--- a/app/scodoc/sco_formsemestre_edit.py
+++ b/app/scodoc/sco_formsemestre_edit.py
@@ -1245,7 +1245,9 @@ def do_formsemestre_clone(
moduleimpl_id=mod_orig["moduleimpl_id"]
):
# copie en enlevant la date
- new_eval = e.clone(not_copying=("jour", "moduleimpl_id"))
+ new_eval = e.clone(
+ not_copying=("date_debut", "date_fin", "moduleimpl_id")
+ )
new_eval.moduleimpl_id = mid
# Copie les poids APC de l'évaluation
new_eval.set_ue_poids_dict(e.get_ue_poids_dict())
@@ -1443,7 +1445,7 @@ def do_formsemestre_delete(formsemestre_id):
mods = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
for mod in mods:
# evaluations
- evals = sco_evaluation_db.do_evaluation_list(
+ evals = sco_evaluation_db.get_evaluation_dict(
args={"moduleimpl_id": mod["moduleimpl_id"]}
)
for e in evals:
diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py
index 00dfaa0c60..2e572ea309 100755
--- a/app/scodoc/sco_formsemestre_status.py
+++ b/app/scodoc/sco_formsemestre_status.py
@@ -498,7 +498,7 @@ def retreive_formsemestre_from_request() -> int:
modimpl = modimpl[0]
formsemestre_id = modimpl["formsemestre_id"]
elif "evaluation_id" in args:
- E = sco_evaluation_db.do_evaluation_list(
+ E = sco_evaluation_db.get_evaluation_dict(
{"evaluation_id": args["evaluation_id"]}
)
if not E:
@@ -620,7 +620,7 @@ def formsemestre_description_table(
columns_ids += ["Inscrits", "Responsable", "Enseignants"]
if with_evals:
columns_ids += [
- "jour",
+ "date_evaluation",
"description",
"coefficient",
"evalcomplete_str",
@@ -630,7 +630,7 @@ def formsemestre_description_table(
titles = {title: title for title in columns_ids}
titles.update({f"ue_{ue.id}": ue.acronyme for ue in ues})
titles["ects"] = "ECTS"
- titles["jour"] = "Évaluation"
+ titles["date_evaluation"] = "Évaluation"
titles["description"] = ""
titles["coefficient"] = "Coef. éval."
titles["evalcomplete_str"] = "Complète"
@@ -738,8 +738,10 @@ def formsemestre_description_table(
scodoc_dept=g.scodoc_dept,
evaluation_id=e["evaluation_id"],
)
- e["_jour_order"] = e["jour"].isoformat()
- e["jour"] = e["jour"].strftime("%d/%m/%Y") if e["jour"] else ""
+ e["_date_evaluation_order"] = e["jour"].isoformat()
+ e["date_evaluation"] = (
+ e["jour"].strftime("%d/%m/%Y") if e["jour"] else ""
+ )
e["UE"] = row["UE"]
e["_UE_td_attrs"] = row["_UE_td_attrs"]
e["Code"] = row["Code"]
diff --git a/app/scodoc/sco_liste_notes.py b/app/scodoc/sco_liste_notes.py
index 9be70378cc..00818e54d1 100644
--- a/app/scodoc/sco_liste_notes.py
+++ b/app/scodoc/sco_liste_notes.py
@@ -69,38 +69,44 @@ def do_evaluation_listenotes(
mode = None
if moduleimpl_id:
mode = "module"
- evals = sco_evaluation_db.do_evaluation_list({"moduleimpl_id": moduleimpl_id})
+ evals = sco_evaluation_db.get_evaluation_dict({"moduleimpl_id": moduleimpl_id})
elif evaluation_id:
mode = "eval"
- evals = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})
+ evals = sco_evaluation_db.get_evaluation_dict({"evaluation_id": evaluation_id})
else:
raise ValueError("missing argument: evaluation or module")
if not evals:
return "Aucune évaluation !
", "ScoDoc"
- E = evals[0] # il y a au moins une evaluation
- modimpl = db.session.get(ModuleImpl, E["moduleimpl_id"])
+ eval_dict = evals[0] # il y a au moins une evaluation
+ modimpl = db.session.get(ModuleImpl, eval_dict["moduleimpl_id"])
# description de l'evaluation
if mode == "eval":
H = [sco_evaluations.evaluation_describe(evaluation_id=evaluation_id)]
- page_title = f"Notes {E['description'] or modimpl.module.code}"
+ page_title = f"Notes {eval_dict['description'] or modimpl.module.code}"
else:
H = []
page_title = f"Notes {modimpl.module.code}"
# groupes
groups = sco_groups.do_evaluation_listegroupes(
- E["evaluation_id"], include_default=True
+ eval_dict["evaluation_id"], include_default=True
)
grlabs = [g["group_name"] or "tous" for g in groups] # legendes des boutons
grnams = [str(g["group_id"]) for g in groups] # noms des checkbox
if len(evals) > 1:
descr = [
- ("moduleimpl_id", {"default": E["moduleimpl_id"], "input_type": "hidden"})
+ (
+ "moduleimpl_id",
+ {"default": eval_dict["moduleimpl_id"], "input_type": "hidden"},
+ )
]
else:
descr = [
- ("evaluation_id", {"default": E["evaluation_id"], "input_type": "hidden"})
+ (
+ "evaluation_id",
+ {"default": eval_dict["evaluation_id"], "input_type": "hidden"},
+ )
]
if len(grnams) > 1:
descr += [
@@ -199,7 +205,7 @@ def do_evaluation_listenotes(
url_for(
"notes.moduleimpl_status",
scodoc_dept=g.scodoc_dept,
- moduleimpl_id=E["moduleimpl_id"],
+ moduleimpl_id=eval_dict["moduleimpl_id"],
)
),
"",
diff --git a/app/scodoc/sco_moduleimpl_status.py b/app/scodoc/sco_moduleimpl_status.py
index 3f167cf029..af8a90e87a 100644
--- a/app/scodoc/sco_moduleimpl_status.py
+++ b/app/scodoc/sco_moduleimpl_status.py
@@ -61,7 +61,7 @@ from app.tables import list_etuds
# menu evaluation dans moduleimpl
def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0) -> str:
"Menu avec actions sur une evaluation"
- E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
+ E = sco_evaluation_db.get_evaluation_dict({"evaluation_id": evaluation_id})[0]
modimpl = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
group_id = sco_groups.get_default_group(modimpl["formsemestre_id"])
@@ -203,11 +203,10 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
- # Evaluations, la plus RECENTE en tête
+ # Evaluations, par numéros ou la plus RECENTE en tête
evaluations = modimpl.evaluations.order_by(
Evaluation.numero.desc(),
- Evaluation.jour.desc(),
- Evaluation.heure_debut.desc(),
+ Evaluation.date_debut.desc(),
).all()
nb_evaluations = len(evaluations)
max_poids = max(
@@ -571,10 +570,8 @@ def _ligne_evaluation(
# visualisation des poids (Hinton map)
H.append(_evaluation_poids_html(evaluation, max_poids))
H.append("""""")
- if evaluation.jour:
- H.append(
- f"""Le {evaluation.jour.strftime("%d/%m/%Y")} {evaluation.descr_heure()}"""
- )
+ if evaluation.date_debut:
+ H.append(evaluation.descr_date())
else:
H.append(
f"""
int:
+ ) -> dict:
args = locals()
del args["self"]
moduleimpl: ModuleImpl = db.session.get(ModuleImpl, moduleimpl_id)
@@ -322,7 +321,9 @@ class ScoFake(object):
evaluation: Evaluation = Evaluation.create(moduleimpl=moduleimpl, **args)
db.session.add(evaluation)
db.session.commit()
- return evaluation.id
+ eval_dict = evaluation.to_dict()
+ eval_dict["id"] = evaluation.id
+ return eval_dict
@logging_meth
def create_note(
@@ -414,7 +415,7 @@ class ScoFake(object):
for e_idx in range(1, nb_evaluations_per_module + 1):
e = self.create_evaluation(
moduleimpl_id=moduleimpl_id,
- jour=date_debut,
+ date_debut=datetime.datetime.strptime(date_debut, "%d/%m/%Y"),
description="evaluation test %s" % e_idx,
coefficient=1.0,
)
@@ -435,7 +436,7 @@ class ScoFake(object):
for e in eval_list:
for idx, etud in enumerate(etuds):
self.create_note(
- evaluation_id=e["id"],
+ evaluation_id=e["evaluation_id"],
etudid=etud["etudid"],
note=notes[idx % len(notes)],
)
diff --git a/tests/unit/setup.py b/tests/unit/setup.py
index 0930a414a7..a3756d9f93 100644
--- a/tests/unit/setup.py
+++ b/tests/unit/setup.py
@@ -1,9 +1,9 @@
"""
Quelques fonctions d'initialisation pour tests unitaires
"""
+import datetime
from app import db, models
-
import app.scodoc.sco_utils as scu
from app.scodoc import codes_cursus
@@ -133,7 +133,7 @@ def build_modules_with_evaluations(
for _ in range(nb_evals_per_modimpl):
e = G.create_evaluation(
moduleimpl_id=moduleimpl_id,
- jour="01/01/2021",
+ date_debut=datetime.datetime(2021, 1, 1),
description="evaluation 1",
coefficient=0,
)
diff --git a/tests/unit/test_abs_demijournee.py b/tests/unit/test_abs_demijournee.py
index a4783a0f7e..0a54a7ec5d 100644
--- a/tests/unit/test_abs_demijournee.py
+++ b/tests/unit/test_abs_demijournee.py
@@ -5,7 +5,7 @@
Créer et justifier des absences en utilisant le parametre demijournee
"""
# test écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en juillet 2021
-
+import datetime
import json
from tests.unit import sco_fake_gen
@@ -163,7 +163,7 @@ def test_abs_basic(test_client):
# --- Création d'une évaluation
e = G.create_evaluation(
moduleimpl_id=moduleimpl_id,
- jour="22/01/2021",
+ date_debut=datetime.datetime(2021, 1, 22),
description="evaluation test",
coefficient=1.0,
)
diff --git a/tests/unit/test_bulletin.py b/tests/unit/test_bulletin.py
index 68a050d79b..e92d872919 100644
--- a/tests/unit/test_bulletin.py
+++ b/tests/unit/test_bulletin.py
@@ -22,7 +22,7 @@ from tests.unit import test_sco_basic
DEPT = TestConfig.DEPT_TEST
-def test_bulletin(test_client):
+def test_bulletin_data_classic(test_client):
"""Vérifications sur les bulletins de notes"""
G = sco_fake_gen.ScoFake(verbose=False)
app.set_sco_dept(DEPT)
diff --git a/tests/unit/test_but_modules.py b/tests/unit/test_but_modules.py
index 66b818fbca..fd1bc4306d 100644
--- a/tests/unit/test_but_modules.py
+++ b/tests/unit/test_but_modules.py
@@ -2,6 +2,7 @@
Test modèles évaluations avec poids BUT
et calcul moyennes modules
"""
+import datetime
import numpy as np
import pandas as pd
@@ -46,7 +47,7 @@ def test_evaluation_poids(test_client):
)
_e1 = G.create_evaluation(
moduleimpl_id=moduleimpl_id,
- jour="01/01/2021",
+ date_debut=datetime.datetime(2021, 1, 1),
description="evaluation 1",
coefficient=0,
)
@@ -218,7 +219,7 @@ def test_module_moy(test_client):
# Crée une deuxième évaluation dans le même moduleimpl:
evaluation2_id = G.create_evaluation(
moduleimpl_id=evaluation1.moduleimpl_id,
- jour="02/01/2021",
+ date_debut=datetime.datetime(2021, 1, 2),
description="evaluation 2",
coefficient=coef_e2,
)["evaluation_id"]
diff --git a/tests/unit/test_caches.py b/tests/unit/test_caches.py
index 14b9a0c066..3a4882d778 100644
--- a/tests/unit/test_caches.py
+++ b/tests/unit/test_caches.py
@@ -15,12 +15,11 @@ import app
from app import db
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
-from app.models import FormSemestre
+from app.models import Evaluation, FormSemestre, ModuleImpl
from app.scodoc import sco_cache
from app.scodoc import sco_evaluations
from app.scodoc import sco_evaluation_db
from app.scodoc import sco_formsemestre
-from app.scodoc import notesdb as ndb
from config import TestConfig
from tests.unit.test_sco_basic import run_sco_basic
@@ -58,23 +57,24 @@ def test_cache_evaluations(test_client):
# prépare le département avec quelques semestres:
run_sco_basic()
#
- sems = sco_formsemestre.do_formsemestre_list()
- assert len(sems)
- sem_evals = []
- for sem in sems:
- sem_evals = sco_evaluations.do_evaluation_list_in_sem(
- sem["formsemestre_id"], with_etat=False
+ formsemestres = FormSemestre.query
+ assert formsemestres.count()
+ evaluation = None
+ for formsemestre in formsemestres:
+ evaluation: Evaluation = (
+ Evaluation.query.join(ModuleImpl)
+ .filter_by(formsemestre_id=formsemestre.id)
+ .first()
)
- if sem_evals:
+ if evaluation is not None:
break
- if not sem_evals:
+ if evaluation is None:
raise Exception("no evaluations")
#
- evaluation_id = sem_evals[0]["evaluation_id"]
- eval_notes = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
+ eval_notes = sco_evaluation_db.do_evaluation_get_all_notes(evaluation.id)
# should have been be cached, except if empty
if eval_notes:
- assert sco_cache.EvaluationCache.get(evaluation_id)
- sco_cache.invalidate_formsemestre(sem["formsemestre_id"])
+ assert sco_cache.EvaluationCache.get(evaluation.id)
+ sco_cache.invalidate_formsemestre(evaluation.moduleimpl.formsemestre.id)
# should have been erased from cache:
- assert not sco_cache.EvaluationCache.get(evaluation_id)
+ assert not sco_cache.EvaluationCache.get(evaluation.id)
diff --git a/tests/unit/test_notes_modules.py b/tests/unit/test_notes_modules.py
index 55dad99994..1542eaecda 100644
--- a/tests/unit/test_notes_modules.py
+++ b/tests/unit/test_notes_modules.py
@@ -2,6 +2,7 @@
Vérif moyennes de modules des bulletins
et aussi moyennes modules et UE internes (via nt)
"""
+import datetime
import numpy as np
from flask import g
from config import TestConfig
@@ -93,13 +94,13 @@ def test_notes_modules(test_client):
coef_2 = 2.0
e1 = G.create_evaluation(
moduleimpl_id=moduleimpl_id,
- jour="01/01/2020",
+ date_debut=datetime.datetime(2020, 1, 1),
description="evaluation 1",
coefficient=coef_1,
)
e2 = G.create_evaluation(
moduleimpl_id=moduleimpl_id,
- jour="01/01/2020",
+ date_debut=datetime.datetime(2020, 1, 1),
description="evaluation 2",
coefficient=coef_2,
)
@@ -107,16 +108,16 @@ def test_notes_modules(test_client):
note_1 = 12.0
note_2 = 13.0
_, _, _ = G.create_note(
- evaluation_id=e1["id"], etudid=etuds[0]["etudid"], note=note_1
+ evaluation_id=e1["evaluation_id"], etudid=etuds[0]["etudid"], note=note_1
)
_, _, _ = G.create_note(
- evaluation_id=e2["id"], etudid=etuds[0]["etudid"], note=note_2
+ evaluation_id=e2["evaluation_id"], etudid=etuds[0]["etudid"], note=note_2
)
_, _, _ = G.create_note(
- evaluation_id=e1["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(
- evaluation_id=e2["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(
sem["formsemestre_id"], etud["etudid"]
@@ -138,16 +139,24 @@ def test_notes_modules(test_client):
)
# Absence à une évaluation
- _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etudid, note=None) # abs
- _, _, _ = G.create_note(evaluation_id=e2["id"], etudid=etudid, note=note_2)
+ _, _, _ = G.create_note(
+ evaluation_id=e1["evaluation_id"], etudid=etudid, note=None
+ ) # abs
+ _, _, _ = G.create_note(
+ evaluation_id=e2["evaluation_id"], etudid=etudid, note=note_2
+ )
b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"]
)
note_th = (coef_1 * 0.0 + coef_2 * note_2) / (coef_1 + coef_2)
assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(note_th)
# Absences aux deux évaluations
- _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etudid, note=None) # abs
- _, _, _ = G.create_note(evaluation_id=e2["id"], etudid=etudid, note=None) # abs
+ _, _, _ = G.create_note(
+ evaluation_id=e1["evaluation_id"], etudid=etudid, note=None
+ ) # abs
+ _, _, _ = G.create_note(
+ evaluation_id=e2["evaluation_id"], etudid=etudid, note=None
+ ) # abs
b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"]
)
@@ -162,9 +171,11 @@ def test_notes_modules(test_client):
)
# Note excusée EXC <-> scu.NOTES_NEUTRALISE
- _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etudid, note=note_1)
_, _, _ = G.create_note(
- evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
+ evaluation_id=e1["evaluation_id"], etudid=etudid, note=note_1
+ )
+ _, _, _ = G.create_note(
+ evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
) # EXC
b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"]
@@ -179,9 +190,11 @@ def test_notes_modules(test_client):
expected_moy_ue=note_1,
)
# Note en attente ATT <-> scu.NOTES_ATTENTE
- _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etudid, note=note_1)
_, _, _ = G.create_note(
- evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_ATTENTE
+ evaluation_id=e1["evaluation_id"], etudid=etudid, note=note_1
+ )
+ _, _, _ = G.create_note(
+ evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_ATTENTE
) # ATT
b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"]
@@ -197,10 +210,10 @@ def test_notes_modules(test_client):
)
# Neutralisation (EXC) des 2 évals
_, _, _ = G.create_note(
- evaluation_id=e1["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
+ evaluation_id=e1["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
) # EXC
_, _, _ = G.create_note(
- evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
+ evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
) # EXC
b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"]
@@ -216,10 +229,10 @@ def test_notes_modules(test_client):
)
# Attente (ATT) sur les 2 evals
_, _, _ = G.create_note(
- evaluation_id=e1["id"], etudid=etudid, note=scu.NOTES_ATTENTE
+ evaluation_id=e1["evaluation_id"], etudid=etudid, note=scu.NOTES_ATTENTE
) # ATT
_, _, _ = G.create_note(
- evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_ATTENTE
+ evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_ATTENTE
) # ATT
b = sco_bulletins.formsemestre_bulletinetud_dict(
sem["formsemestre_id"], etud["etudid"]
@@ -277,7 +290,7 @@ def test_notes_modules(test_client):
{"etudid": etudid, "moduleimpl_id": moduleimpl_id},
formsemestre_id=formsemestre_id,
)
- _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etudid, note=12.5)
+ _, _, _ = G.create_note(evaluation_id=e1["evaluation_id"], etudid=etudid, note=12.5)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
mod_stats = nt.get_mod_stats(moduleimpl_id)
@@ -301,11 +314,13 @@ def test_notes_modules(test_client):
# Note dans module 2:
e_m2 = G.create_evaluation(
moduleimpl_id=moduleimpl_id2,
- jour="01/01/2020",
+ date_debut=datetime.datetime(2020, 1, 1),
description="evaluation mod 2",
coefficient=1.0,
)
- _, _, _ = G.create_note(evaluation_id=e_m2["id"], etudid=etudid, note=19.5)
+ _, _, _ = G.create_note(
+ evaluation_id=e_m2["evaluation_id"], etudid=etudid, note=19.5
+ )
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
ue_status = nt.get_etud_ue_status(etudid, ue_id)
@@ -314,20 +329,22 @@ def test_notes_modules(test_client):
# 2 modules, notes EXC dans le premier, note valide n dans le second
# la moyenne de l'UE doit être n
_, _, _ = G.create_note(
- evaluation_id=e1["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
+ evaluation_id=e1["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
) # EXC
_, _, _ = G.create_note(
- evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
+ evaluation_id=e2["evaluation_id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
) # EXC
- _, _, _ = G.create_note(evaluation_id=e_m2["id"], etudid=etudid, note=12.5)
_, _, _ = G.create_note(
- evaluation_id=e1["id"], etudid=etuds[1]["etudid"], note=11.0
+ evaluation_id=e_m2["evaluation_id"], etudid=etudid, note=12.5
)
_, _, _ = G.create_note(
- evaluation_id=e2["id"], etudid=etuds[1]["etudid"], note=11.0
+ evaluation_id=e1["evaluation_id"], etudid=etuds[1]["etudid"], note=11.0
)
_, _, _ = G.create_note(
- evaluation_id=e_m2["id"], etudid=etuds[1]["etudid"], note=11.0
+ evaluation_id=e2["evaluation_id"], etudid=etuds[1]["etudid"], note=11.0
+ )
+ _, _, _ = G.create_note(
+ evaluation_id=e_m2["evaluation_id"], etudid=etuds[1]["etudid"], note=11.0
)
b = sco_bulletins.formsemestre_bulletinetud_dict(
@@ -385,16 +402,20 @@ def test_notes_modules_att_dem(test_client):
coef_1 = 1.0
e1 = G.create_evaluation(
moduleimpl_id=moduleimpl_id,
- jour="01/01/2020",
+ date_debut=datetime.datetime(2020, 1, 1),
description="evaluation 1",
coefficient=coef_1,
)
# Attente (ATT) sur les 2 evals
_, _, _ = G.create_note(
- evaluation_id=e1["id"], etudid=etuds[0]["etudid"], note=scu.NOTES_ATTENTE
+ evaluation_id=e1["evaluation_id"],
+ etudid=etuds[0]["etudid"],
+ note=scu.NOTES_ATTENTE,
) # ATT
_, _, _ = G.create_note(
- evaluation_id=e1["id"], etudid=etuds[1]["etudid"], note=scu.NOTES_ATTENTE
+ evaluation_id=e1["evaluation_id"],
+ etudid=etuds[1]["etudid"],
+ note=scu.NOTES_ATTENTE,
) # ATT
# Démission du premier étudiant
sco_formsemestre_inscriptions.do_formsemestre_demission(
@@ -435,7 +456,7 @@ def test_notes_modules_att_dem(test_client):
# Saisie note ABS pour le deuxième etud
_, _, _ = G.create_note(
- evaluation_id=e1["id"], etudid=etuds[1]["etudid"], note=None
+ evaluation_id=e1["evaluation_id"], etudid=etuds[1]["etudid"], note=None
)
nt = check_nt(
etuds[1]["etudid"],
diff --git a/tests/unit/test_notes_rattrapage.py b/tests/unit/test_notes_rattrapage.py
index 853824929e..45433886fd 100644
--- a/tests/unit/test_notes_rattrapage.py
+++ b/tests/unit/test_notes_rattrapage.py
@@ -1,5 +1,6 @@
"""Test calculs rattrapages
"""
+import datetime
import app
from app import db
@@ -58,14 +59,14 @@ def test_notes_rattrapage(test_client):
# --- Creation évaluation
e = G.create_evaluation(
moduleimpl_id=moduleimpl_id,
- jour="01/01/2020",
+ date_debut=datetime.datetime(2020, 1, 1),
description="evaluation test",
coefficient=1.0,
)
# --- Création d'une évaluation "de rattrapage"
e_rat = G.create_evaluation(
moduleimpl_id=moduleimpl_id,
- jour="02/01/2020",
+ date_debut=datetime.datetime(2020, 1, 2),
description="evaluation rattrapage",
coefficient=1.0,
evaluation_type=scu.EVALUATION_RATTRAPAGE,
@@ -139,7 +140,7 @@ def test_notes_rattrapage(test_client):
# Création évaluation session 2:
e_session2 = G.create_evaluation(
moduleimpl_id=moduleimpl_id,
- jour="02/01/2020",
+ date_debut=datetime.datetime(2020, 1, 2),
description="evaluation session 2",
coefficient=1.0,
evaluation_type=scu.EVALUATION_SESSION2,
diff --git a/tests/unit/test_sco_basic.py b/tests/unit/test_sco_basic.py
index 5472c17b61..49eb7c24b6 100644
--- a/tests/unit/test_sco_basic.py
+++ b/tests/unit/test_sco_basic.py
@@ -11,6 +11,8 @@ Au besoin, créer un base de test neuve:
./tools/create_database.sh SCODOC_TEST
"""
+import datetime
+
from app.models import FormSemestreInscription, Identite
from config import TestConfig
@@ -96,7 +98,7 @@ def run_sco_basic(verbose=False) -> FormSemestre:
# --- Création évaluation
e = G.create_evaluation(
moduleimpl_id=moduleimpl_id,
- jour="01/01/2020",
+ date_debut=datetime.datetime(2020, 1, 1),
description="evaluation test",
coefficient=1.0,
)
@@ -104,7 +106,7 @@ def run_sco_basic(verbose=False) -> FormSemestre:
# --- Saisie toutes les notes de l'évaluation
for idx, etud in enumerate(etuds):
etudids_changed, nb_suppress, existing_decisions = G.create_note(
- evaluation_id=e["id"],
+ evaluation_id=e["evaluation_id"],
etudid=etud["etudid"],
note=NOTES_T[idx % len(NOTES_T)],
)
@@ -130,14 +132,14 @@ def run_sco_basic(verbose=False) -> FormSemestre:
# --- Une autre évaluation
e2 = G.create_evaluation(
moduleimpl_id=moduleimpl_id,
- jour="02/01/2020",
+ date_debut=datetime.datetime(2020, 1, 2),
description="evaluation test 2",
coefficient=1.0,
)
# Saisie les notes des 5 premiers étudiants:
for idx, etud in enumerate(etuds[:5]):
etudids_changed, nb_suppress, existing_decisions = G.create_note(
- evaluation_id=e2["id"],
+ evaluation_id=e2["evaluation_id"],
etudid=etud["etudid"],
note=NOTES_T[idx % len(NOTES_T)],
)
@@ -159,7 +161,7 @@ def run_sco_basic(verbose=False) -> FormSemestre:
# Saisie des notes qui manquent:
for idx, etud in enumerate(etuds[5:]):
etudids_changed, nb_suppress, existing_decisions = G.create_note(
- evaluation_id=e2["id"],
+ evaluation_id=e2["evaluation_id"],
etudid=etud["etudid"],
note=NOTES_T[idx % len(NOTES_T)],
)
diff --git a/tests/unit/yaml_setup.py b/tests/unit/yaml_setup.py
index bc51849db7..577855ba5d 100644
--- a/tests/unit/yaml_setup.py
+++ b/tests/unit/yaml_setup.py
@@ -158,7 +158,7 @@ def create_evaluations(formsemestre: FormSemestre, publish_incomplete=True):
for modimpl in formsemestre.modimpls:
evaluation = Evaluation(
moduleimpl=modimpl,
- jour=formsemestre.date_debut,
+ date_debut=formsemestre.date_debut,
description=f"Exam {modimpl.module.titre}",
coefficient=1.0,
note_max=20.0,
diff --git a/tools/fakedatabase/create_test_api_database.py b/tools/fakedatabase/create_test_api_database.py
index 55fbd4d4b9..7ee00b32a0 100644
--- a/tools/fakedatabase/create_test_api_database.py
+++ b/tools/fakedatabase/create_test_api_database.py
@@ -238,7 +238,8 @@ def create_evaluations(formsemestre: FormSemestre):
"Création d'une evaluation dans chaque modimpl du semestre"
for moduleimpl in formsemestre.modimpls:
args = {
- "jour": datetime.date(2022, 3, 1) + datetime.timedelta(days=moduleimpl.id),
+ "jour": datetime.date(2022, 3, 1)
+ + datetime.timedelta(days=moduleimpl.id), # TODO à changer
"heure_debut": "8h00",
"heure_fin": "9h00",
"description": f"Evaluation-{moduleimpl.module.code}",