forked from ScoDoc/ScoDoc
Modification codage dates évaluations
This commit is contained in:
parent
e6a0d224d1
commit
3d513bb169
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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]
|
||||
|
@ -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"""
|
||||
|
@ -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"):
|
||||
|
@ -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"<EvaluationUEPoids {self.evaluation} {self.ue} poids={self.poids}>"
|
||||
|
||||
|
||||
# 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)
|
||||
# --- date de l'évaluation
|
||||
formsemestre = moduleimpl.formsemestre
|
||||
if (jour > formsemestre.date_fin) or (jour < formsemestre.date_debut):
|
||||
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
|
||||
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 duration < 0 or duration > 60 * 12:
|
||||
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:
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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(
|
||||
|
@ -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 = (
|
||||
'<a class="bull_link" href="moduleimpl_status?moduleimpl_id=%s" title="%s">'
|
||||
% (modimpl["moduleimpl_id"], mod["mod_descr_txt"])
|
||||
link_mod = f"""<a class="bull_link" href="{
|
||||
url_for("notes.moduleimpl_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
moduleimpl_id=modimpl["moduleimpl_id"]
|
||||
)
|
||||
}" title="{mod["mod_descr_txt"]}">"""
|
||||
|
||||
if sco_preferences.get_preference("bul_show_codemodules", formsemestre_id):
|
||||
mod["code"] = modimpl["module"]["code"]
|
||||
mod["code_html"] = link_mod + (mod["code"] or "") + "</a>"
|
||||
@ -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
|
||||
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:
|
||||
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"
|
||||
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"] = "Points de bonus sur cette UE"
|
||||
e_dict["name"] = "Points de bonus sur cette UE"
|
||||
else:
|
||||
e["name"] = e["description"] or f"le {e['jour']}"
|
||||
e["target_html"] = url_for(
|
||||
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"""<a class="bull_link" href="{
|
||||
e['target_html']}">{e['name']}</a>"""
|
||||
val = e["notes"].get(etudid, {"value": "NP"})["value"]
|
||||
e_dict['target_html']}">{e_dict['name']}</a>"""
|
||||
if is_complete: # évaluation complète
|
||||
# val est NP si etud demissionnaire
|
||||
if val == "NP":
|
||||
e["note_txt"] = "nd"
|
||||
e["note_html"] = '<span class="note_nd">nd</span>'
|
||||
e["coef_txt"] = scu.fmt_coef(e["coefficient"])
|
||||
e_dict["note_txt"] = "nd"
|
||||
e_dict["note_html"] = '<span class="note_nd">nd</span>'
|
||||
e_dict["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"] = ""
|
||||
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"]:
|
||||
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_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))
|
||||
):
|
||||
# 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"] = '<a class="bull_link" href="%s">%s</a>' % (
|
||||
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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"])
|
||||
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"]),
|
||||
evaluations_completes = nt.get_modimpl_evaluations_completes(
|
||||
modimpl["moduleimpl_id"]
|
||||
)
|
||||
if version != "short":
|
||||
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"]:
|
||||
|
@ -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),
|
||||
"""<p class="help">Vérification de la cohérence entre les notes saisies et les absences signalées.</p>""",
|
||||
sco_evaluations.evaluation_describe(evaluation_id=evaluation.id),
|
||||
"""<p class="help">Vérification de la cohérence entre les notes saisies
|
||||
et les absences signalées.</p>""",
|
||||
]
|
||||
else:
|
||||
# pas de header, mais un titre
|
||||
H = [
|
||||
"""<h2 class="eval_check_absences">%s du %s """
|
||||
% (E["description"], E["jour"])
|
||||
f"""<h2 class="eval_check_absences">{
|
||||
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("<li>aucun</li>")
|
||||
for etudid in etudids:
|
||||
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
||||
etud: Identite = db.session.get(Identite, etudid)
|
||||
H.append(
|
||||
'<li><a class="discretelink" href="%s">'
|
||||
% url_for(
|
||||
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud["etudid"]
|
||||
f"""<li><a class="discretelink" href="{
|
||||
url_for(
|
||||
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid
|
||||
)
|
||||
+ "%(nomprenom)s</a>" % etud
|
||||
}">{etud.nomprenom}</a>"""
|
||||
)
|
||||
if linkabs:
|
||||
H.append(
|
||||
f"""<a class="stdlink" href="{url_for(
|
||||
'absences.doSignaleAbsence',
|
||||
url = url_for(
|
||||
"absences.doSignaleAbsence",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
etudid=etud["etudid"],
|
||||
datedebut=E["jour"],
|
||||
datefin=E["jour"],
|
||||
etudid=etudid,
|
||||
# par defaut signale le jour du début de l'éval
|
||||
datedebut=evaluation.date_debut.strftime("%d/%m/%Y"),
|
||||
datefin=evaluation.date_debut.strftime("%d/%m/%Y"),
|
||||
demijournee=demijournee,
|
||||
moduleimpl_id=E["moduleimpl_id"],
|
||||
moduleimpl_id=evaluation.moduleimpl_id,
|
||||
)
|
||||
}">signaler cette absence</a>"""
|
||||
H.append(
|
||||
f"""<a class="stdlink" href="{url}">signaler cette absence</a>"""
|
||||
)
|
||||
H.append("</li>")
|
||||
H.append("</ul>")
|
||||
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"'
|
||||
|
@ -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<do_evaluation_etat_in_sem (en cours de remplacement)
|
||||
|
||||
- _eval_etat<do_evaluation_etat_in_mod<formsemestre_tableau_modules
|
||||
qui a seulement besoin de
|
||||
nb_evals_completes, nb_evals_en_cours, nb_evals_vides, attente
|
||||
|
||||
renvoie:
|
||||
{
|
||||
nb_inscrits : inscrits au module
|
||||
nb_notes
|
||||
@ -124,7 +132,7 @@ def do_evaluation_etat(
|
||||
) # { etudid : note }
|
||||
|
||||
# ---- Liste des groupes complets et incomplets
|
||||
E = sco_evaluation_db.do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
|
||||
E = sco_evaluation_db.get_evaluation_dict(args={"evaluation_id": evaluation_id})[0]
|
||||
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
||||
Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
|
||||
is_malus = Mod["module_type"] == ModuleType.MALUS # True si module de malus
|
||||
@ -275,7 +283,8 @@ def do_evaluation_etat(
|
||||
|
||||
|
||||
def do_evaluation_list_in_sem(formsemestre_id, with_etat=True):
|
||||
"""Liste les evaluations de tous les modules de ce semestre.
|
||||
"""Liste les évaluations de tous les modules de ce semestre.
|
||||
Triée par module, numero desc, date_debut desc
|
||||
Donne pour chaque eval son état (voir do_evaluation_etat)
|
||||
{ evaluation_id,nb_inscrits, nb_notes, nb_abs, nb_neutre, moy, median, last_modif ... }
|
||||
|
||||
@ -315,7 +324,7 @@ def do_evaluation_list_in_sem(formsemestre_id, with_etat=True):
|
||||
'evaluation_type': 0,
|
||||
'heure_debut': datetime.time(8, 0),
|
||||
'heure_fin': datetime.time(9, 30),
|
||||
'jour': datetime.date(2015, 11, 3), // vide => 1/1/1
|
||||
'jour': datetime.date(2015, 11, 3), // vide => 1/1/1900
|
||||
'moduleimpl_id': 'GEAMIP80490',
|
||||
'note_max': 20.0,
|
||||
'numero': 0,
|
||||
@ -327,7 +336,7 @@ def do_evaluation_list_in_sem(formsemestre_id, with_etat=True):
|
||||
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"],
|
||||
),
|
||||
'<div class="cal_evaluations">',
|
||||
CalHTML,
|
||||
"</div>",
|
||||
"<p>soit %s évaluations planifiées;" % nb_evals,
|
||||
"""<ul><li>en <span style="background-color: %s">rouge</span> les évaluations passées auxquelles il manque des notes</li>
|
||||
<li>en <span style="background-color: %s">vert</span> les évaluations déjà notées</li>
|
||||
<li>en <span style="background-color: %s">bleu</span> les évaluations futures</li></ul></p>"""
|
||||
% (color_incomplete, color_complete, color_futur),
|
||||
"""<p><a href="formsemestre_evaluations_delai_correction?formsemestre_id=%s" class="stdlink">voir les délais de correction</a></p>
|
||||
)
|
||||
}
|
||||
<div class="cal_evaluations">
|
||||
{ cal_html }
|
||||
</div>
|
||||
<p>soit {nb_evals} évaluations planifiées;
|
||||
</p>
|
||||
<ul>
|
||||
<li>en <span style=
|
||||
"background-color: {color_incomplete}">rouge</span>
|
||||
les évaluations passées auxquelles il manque des notes
|
||||
</li>
|
||||
<li>en <span style=
|
||||
"background-color: {color_complete}">vert</span>
|
||||
les évaluations déjà notées
|
||||
</li>
|
||||
<li>en <span style=
|
||||
"background-color: {color_futur}">bleu</span>
|
||||
les évaluations futures
|
||||
</li>
|
||||
</ul>
|
||||
<p><a href="{
|
||||
url_for("notes.formsemestre_evaluations_delai_correction",
|
||||
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id
|
||||
)
|
||||
}" class="stdlink">voir les délais de correction</a>
|
||||
</p>
|
||||
{ html_sco_header.sco_footer() }
|
||||
"""
|
||||
% (formsemestre_id,),
|
||||
html_sco_header.sco_footer(),
|
||||
]
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
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"],
|
||||
)
|
||||
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(
|
||||
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"],
|
||||
}
|
||||
)
|
||||
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="<h2>Correction des évaluations du semestre</h2>",
|
||||
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]
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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"]
|
||||
|
@ -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 "<p>Aucune évaluation !</p>", "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"],
|
||||
)
|
||||
),
|
||||
"",
|
||||
|
@ -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("""<div class="evaluation_titre">""")
|
||||
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"""<a href="{url_for("notes.evaluation_edit",
|
||||
|
@ -138,7 +138,7 @@ class PlacementForm(FlaskForm):
|
||||
|
||||
def set_evaluation_infos(self, evaluation_id):
|
||||
"""Initialise les données du formulaire avec les données de l'évaluation."""
|
||||
eval_data = sco_evaluation_db.do_evaluation_list(
|
||||
eval_data = sco_evaluation_db.get_evaluation_dict(
|
||||
{"evaluation_id": evaluation_id}
|
||||
)
|
||||
if not eval_data:
|
||||
@ -239,7 +239,7 @@ class PlacementRunner:
|
||||
self.groups_ids = [
|
||||
gid if gid != TOUS else form.tous_id for gid in form["groups"].data
|
||||
]
|
||||
self.eval_data = sco_evaluation_db.do_evaluation_list(
|
||||
self.eval_data = sco_evaluation_db.get_evaluation_dict(
|
||||
{"evaluation_id": self.evaluation_id}
|
||||
)[0]
|
||||
self.groups = sco_groups.listgroups(self.groups_ids)
|
||||
|
@ -884,15 +884,15 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]):
|
||||
modimpl = evaluation.moduleimpl
|
||||
formsemestre = modimpl.formsemestre
|
||||
mod_responsable = sco_users.user_info(modimpl.responsable_id)
|
||||
if evaluation.jour:
|
||||
indication_date = evaluation.jour.isoformat()
|
||||
if evaluation.date_debut:
|
||||
indication_date = evaluation.date_debut.date().isoformat()
|
||||
else:
|
||||
indication_date = scu.sanitize_filename(evaluation.description or "")[:12]
|
||||
eval_name = f"{evaluation.moduleimpl.module.code}-{indication_date}"
|
||||
|
||||
date_str = (
|
||||
f"""du {evaluation.jour.strftime("%d/%m/%Y")}"""
|
||||
if evaluation.jour
|
||||
f"""du {evaluation.date_debut.strftime("%d/%m/%Y")}"""
|
||||
if evaluation.date_debut
|
||||
else "(sans date)"
|
||||
)
|
||||
eval_titre = f"""{evaluation.description if evaluation.description else "évaluation"} {date_str}"""
|
||||
@ -1107,7 +1107,9 @@ def _get_sorted_etuds(evaluation: Evaluation, etudids: list, formsemestre_id: in
|
||||
e["groups"] = sco_groups.get_etud_groups(etudid, formsemestre_id)
|
||||
|
||||
# Information sur absence (tenant compte de la demi-journée)
|
||||
jour_iso = evaluation.jour.isoformat() if evaluation.jour else ""
|
||||
jour_iso = (
|
||||
evaluation.date_debut.date().isoformat() if evaluation.date_debut else ""
|
||||
)
|
||||
warn_abs_lst = []
|
||||
if evaluation.is_matin():
|
||||
nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=True)
|
||||
|
@ -149,7 +149,7 @@ def list_operations(evaluation_id):
|
||||
|
||||
def evaluation_list_operations(evaluation_id):
|
||||
"""Page listing operations on 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]
|
||||
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
||||
|
||||
Ops = list_operations(evaluation_id)
|
||||
@ -179,7 +179,7 @@ def formsemestre_list_saisies_notes(formsemestre_id, format="html"):
|
||||
"""
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
rows = ndb.SimpleDictFetch(
|
||||
"""SELECT i.nom, i.prenom, code_nip, n.*, mod.titre, e.description, e.jour,
|
||||
"""SELECT i.nom, i.prenom, code_nip, n.*, mod.titre, e.description, e.date_debut,
|
||||
u.user_name, e.id as evaluation_id
|
||||
FROM notes_notes n, notes_evaluation e, notes_moduleimpl mi,
|
||||
notes_modules mod, identite i, "user" u
|
||||
@ -197,6 +197,12 @@ def formsemestre_list_saisies_notes(formsemestre_id, format="html"):
|
||||
keep_numeric = format in scu.FORMATS_NUMERIQUES
|
||||
for row in rows:
|
||||
row["value"] = scu.fmt_note(row["value"], keep_numeric=keep_numeric)
|
||||
row["date_evaluation"] = (
|
||||
row["date_debut"].strftime("%d/%m/%Y %H:%M") if row["date_debut"] else ""
|
||||
)
|
||||
row["_date_evaluation_order"] = (
|
||||
row["date_debut"].isoformat() if row["date_debut"] else ""
|
||||
)
|
||||
columns_ids = (
|
||||
"date",
|
||||
"code_nip",
|
||||
@ -207,7 +213,7 @@ def formsemestre_list_saisies_notes(formsemestre_id, format="html"):
|
||||
"titre",
|
||||
"evaluation_id",
|
||||
"description",
|
||||
"jour",
|
||||
"date_evaluation",
|
||||
"comment",
|
||||
)
|
||||
titles = {
|
||||
@ -221,7 +227,7 @@ def formsemestre_list_saisies_notes(formsemestre_id, format="html"):
|
||||
"evaluation_id": "evaluation_id",
|
||||
"titre": "Module",
|
||||
"description": "Evaluation",
|
||||
"jour": "Date éval.",
|
||||
"date_evaluation": "Date éval.",
|
||||
}
|
||||
tab = GenTable(
|
||||
titles=titles,
|
||||
|
@ -75,6 +75,8 @@ MAX_TEXT_LEN = 64 * 1024
|
||||
STATIC_DIR = (
|
||||
os.environ.get("SCRIPT_NAME", "") + "/ScoDoc/static/links/" + sco_version.SCOVERSION
|
||||
)
|
||||
# La time zone du serveur:
|
||||
TIME_ZONE = timezone("/".join(os.path.realpath("/etc/localtime").split("/")[-2:]))
|
||||
|
||||
# ----- CALCUL ET PRESENTATION DES NOTES
|
||||
NOTES_PRECISION = 1e-4 # evite eventuelles erreurs d'arrondis
|
||||
|
@ -385,7 +385,9 @@ class TableRecap(tb.Table):
|
||||
first_eval_of_mod = True
|
||||
for e in evals:
|
||||
col_id = f"eval_{e.id}"
|
||||
title = f'{modimpl.module.code} {eval_index} {e.jour.isoformat() if e.jour else ""}'
|
||||
title = f"""{modimpl.module.code} {eval_index} {
|
||||
e.date_debut.strftime("%d/%m/%Y") if e.date_debut else ""
|
||||
}"""
|
||||
col_classes = []
|
||||
if first_eval:
|
||||
col_classes.append("first")
|
||||
|
@ -1765,7 +1765,7 @@ sco_publish(
|
||||
@scodoc7func
|
||||
def evaluation_delete(evaluation_id):
|
||||
"""Form delete evaluation"""
|
||||
El = sco_evaluation_db.do_evaluation_list(args={"evaluation_id": evaluation_id})
|
||||
El = sco_evaluation_db.get_evaluation_dict(args={"evaluation_id": evaluation_id})
|
||||
if not El:
|
||||
raise ScoValueError("Evaluation inexistante ! (%s)" % evaluation_id)
|
||||
E = El[0]
|
||||
|
@ -116,3 +116,75 @@ def POST_JSON(path: str, data: dict = {}, headers: dict = None, errmsg=None, dep
|
||||
if r.status_code != 200:
|
||||
raise APIError(errmsg or f"erreur status={r.status_code} !", r.json())
|
||||
return r.json() # decode la reponse JSON
|
||||
|
||||
|
||||
def check_fields(data: dict, fields: dict = None):
|
||||
"""
|
||||
Vérifie que le dictionnaire data contient les bonnes clés
|
||||
et les bons types de valeurs.
|
||||
|
||||
Args:
|
||||
data (dict): un dictionnaire (json de retour de l'api)
|
||||
fields (dict, optional): Un dictionnaire représentant les clés et les types d'une réponse.
|
||||
"""
|
||||
if fields is None:
|
||||
fields = ASSIDUITES_FIELDS
|
||||
assert set(data.keys()) == set(fields.keys())
|
||||
for key in data:
|
||||
if key in ("moduleimpl_id", "desc", "user_id", "external_data"):
|
||||
assert (
|
||||
isinstance(data[key], fields[key]) or data[key] is None
|
||||
), f"error [{key}:{type(data[key])}, {data[key]}, {fields[key]}]"
|
||||
else:
|
||||
assert isinstance(
|
||||
data[key], fields[key]
|
||||
), f"error [{key}:{type(data[key])}, {data[key]}, {fields[key]}]"
|
||||
|
||||
|
||||
def check_failure_get(path: str, headers: dict, err: str = None):
|
||||
"""
|
||||
Vérifie que la requête GET renvoie bien un 404
|
||||
|
||||
Args:
|
||||
path (str): la route de l'api
|
||||
headers (dict): le token d'auth de l'api
|
||||
err (str, optional): L'erreur qui est sensée être fournie par l'api.
|
||||
|
||||
Raises:
|
||||
APIError: Une erreur car la requête a fonctionné (mauvais comportement)
|
||||
"""
|
||||
|
||||
try:
|
||||
GET(path=path, headers=headers)
|
||||
# ^ Renvoi un 404
|
||||
except APIError as api_err:
|
||||
if err is not None:
|
||||
assert api_err.payload["message"] == err
|
||||
else:
|
||||
raise APIError("Le GET n'aurait pas du fonctionner")
|
||||
|
||||
|
||||
def check_failure_post(path: str, headers: dict, data: dict, err: str = None):
|
||||
"""
|
||||
Vérifie que la requête POST renvoie bien un 404
|
||||
|
||||
Args:
|
||||
path (str): la route de l'api
|
||||
headers (dict): le token d'auth
|
||||
data (dict): un dictionnaire (json) à envoyer
|
||||
err (str, optional): L'erreur qui est sensée être fournie par l'api.
|
||||
|
||||
Raises:
|
||||
APIError: Une erreur car la requête a fonctionné (mauvais comportement)
|
||||
"""
|
||||
|
||||
try:
|
||||
data = POST_JSON(path=path, headers=headers, data=data)
|
||||
# ^ Renvoie un 404
|
||||
except APIError as api_err:
|
||||
if err is not None:
|
||||
assert (
|
||||
api_err.payload["message"] == err
|
||||
), f"received: {api_err.payload['message']}"
|
||||
else:
|
||||
raise APIError("Le GET n'aurait pas du fonctionner")
|
||||
|
@ -18,6 +18,7 @@ Utilisation :
|
||||
"""
|
||||
|
||||
import re
|
||||
from types import NoneType
|
||||
|
||||
import requests
|
||||
|
||||
@ -513,13 +514,16 @@ def test_etudiant_bulletin_semestre(api_headers):
|
||||
assert evaluation["description"] is None or isinstance(
|
||||
evaluation["description"], str
|
||||
)
|
||||
assert evaluation["date"] is None or isinstance(evaluation["date"], str)
|
||||
assert isinstance(evaluation["heure_debut"], str)
|
||||
assert isinstance(evaluation["heure_fin"], str)
|
||||
assert isinstance(evaluation["coef"], str)
|
||||
assert isinstance(evaluation["poids"], dict)
|
||||
assert isinstance(evaluation["note"], dict)
|
||||
assert isinstance(evaluation["url"], str)
|
||||
assert isinstance(evaluation["date_debut"], (str, NoneType))
|
||||
assert isinstance(evaluation["date_fin"], (str, NoneType))
|
||||
# Deprecated (supprimer avant #sco9.7):
|
||||
assert isinstance(evaluation["date"], (str, NoneType))
|
||||
assert isinstance(evaluation["heure_debut"], (str, NoneType))
|
||||
assert isinstance(evaluation["heure_fin"], (str, NoneType))
|
||||
|
||||
assert (
|
||||
verify_fields(
|
||||
@ -567,13 +571,16 @@ def test_etudiant_bulletin_semestre(api_headers):
|
||||
assert evaluation["description"] is None or isinstance(
|
||||
evaluation["description"], str
|
||||
)
|
||||
assert evaluation["date"] is None or isinstance(evaluation["date"], str)
|
||||
assert isinstance(evaluation["heure_debut"], str)
|
||||
assert isinstance(evaluation["heure_fin"], str)
|
||||
assert isinstance(evaluation["coef"], str)
|
||||
assert isinstance(evaluation["poids"], dict)
|
||||
assert isinstance(evaluation["note"], dict)
|
||||
assert isinstance(evaluation["url"], str)
|
||||
assert isinstance(evaluation["date_fin"], (str, NoneType))
|
||||
assert isinstance(evaluation["date_debut"], (str, NoneType))
|
||||
# Deprecated fields (supprimer avant #sco9.7)
|
||||
assert isinstance(evaluation["date"], (str, NoneType))
|
||||
assert isinstance(evaluation["heure_debut"], (str, NoneType))
|
||||
assert isinstance(evaluation["heure_fin"], (str, NoneType))
|
||||
|
||||
assert (
|
||||
verify_fields(
|
||||
|
@ -18,6 +18,7 @@ Utilisation :
|
||||
"""
|
||||
import json
|
||||
import requests
|
||||
from types import NoneType
|
||||
|
||||
from app.scodoc import sco_utils as scu
|
||||
|
||||
@ -301,14 +302,18 @@ def test_bulletins(api_headers):
|
||||
assert evaluation["description"] is None or isinstance(
|
||||
evaluation["description"], str
|
||||
)
|
||||
assert evaluation["date"] is None or isinstance(evaluation["date"], str)
|
||||
assert isinstance(evaluation["heure_debut"], str)
|
||||
assert isinstance(evaluation["heure_fin"], str)
|
||||
assert isinstance(evaluation["date_debut"], (str, NoneType))
|
||||
assert isinstance(evaluation["date_fin"], (str, NoneType))
|
||||
assert isinstance(evaluation["coef"], str)
|
||||
assert isinstance(evaluation["poids"], dict)
|
||||
assert isinstance(evaluation["note"], dict)
|
||||
assert isinstance(evaluation["url"], str)
|
||||
|
||||
# Deprecated (supprimer avant #sco9.7):
|
||||
assert isinstance(evaluation["date"], (str, NoneType))
|
||||
assert isinstance(evaluation["heure_debut"], (str, NoneType))
|
||||
assert isinstance(evaluation["heure_fin"], (str, NoneType))
|
||||
|
||||
assert (
|
||||
verify_fields(
|
||||
evaluation["poids"],
|
||||
@ -354,14 +359,18 @@ def test_bulletins(api_headers):
|
||||
assert evaluation["description"] is None or isinstance(
|
||||
evaluation["description"], str
|
||||
)
|
||||
assert evaluation["date"] is None or isinstance(evaluation["date"], str)
|
||||
assert isinstance(evaluation["heure_debut"], str)
|
||||
assert isinstance(evaluation["heure_fin"], str)
|
||||
assert isinstance(evaluation["date_debut"], (str, NoneType))
|
||||
assert isinstance(evaluation["date_fin"], (str, NoneType))
|
||||
assert isinstance(evaluation["coef"], str)
|
||||
assert isinstance(evaluation["poids"], dict)
|
||||
assert isinstance(evaluation["note"], dict)
|
||||
assert isinstance(evaluation["url"], str)
|
||||
|
||||
# Deprecated (supprimer avant #sco9.7):
|
||||
assert isinstance(evaluation["date"], (str, NoneType))
|
||||
assert isinstance(evaluation["heure_debut"], (str, NoneType))
|
||||
assert isinstance(evaluation["heure_fin"], (str, NoneType))
|
||||
|
||||
assert (
|
||||
verify_fields(
|
||||
evaluation["poids"],
|
||||
|
@ -491,7 +491,7 @@ EVAL_FIELDS = {
|
||||
"numero",
|
||||
"poids",
|
||||
"publish_incomplete",
|
||||
"visi_bulletin",
|
||||
"visibulletin",
|
||||
"etat",
|
||||
"nb_inscrits",
|
||||
"nb_notes_manquantes",
|
||||
@ -565,7 +565,7 @@ EVALUATIONS_FIELDS = {
|
||||
"numero",
|
||||
"poids",
|
||||
"publish_incomplete",
|
||||
"visi_bulletin",
|
||||
"visibulletin",
|
||||
}
|
||||
|
||||
NOTES_FIELDS = {
|
||||
|
@ -7,7 +7,7 @@ A utiliser avec debug.py (côté serveur).
|
||||
La classe ScoFake offre un ensemble de raccourcis permettant d'écrire
|
||||
facilement des tests ou de reproduire des bugs.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from functools import wraps
|
||||
import random
|
||||
import sys
|
||||
@ -304,9 +304,8 @@ class ScoFake(object):
|
||||
def create_evaluation(
|
||||
self,
|
||||
moduleimpl_id=None,
|
||||
jour=None,
|
||||
heure_debut="8h00",
|
||||
heure_fin="9h00",
|
||||
date_debut: datetime.datetime = None,
|
||||
date_fin: datetime.datetime = None,
|
||||
description=None,
|
||||
note_max=20,
|
||||
coefficient=None,
|
||||
@ -314,7 +313,7 @@ class ScoFake(object):
|
||||
publish_incomplete=None,
|
||||
evaluation_type=None,
|
||||
numero=None,
|
||||
) -> 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)],
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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"]
|
||||
|
@ -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)
|
||||
|
@ -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"],
|
||||
|
@ -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,
|
||||
|
@ -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)],
|
||||
)
|
||||
|
@ -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,
|
||||
|
@ -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}",
|
||||
|
Loading…
Reference in New Issue
Block a user