forked from ScoDoc/ScoDoc
Evaluations bloquées jusqu'à une date. Implements #858
This commit is contained in:
parent
41944bcd29
commit
1c01d987be
@ -291,7 +291,8 @@ class BulletinBUT:
|
||||
"date_fin": e.date_fin.isoformat() if e.date_fin else None,
|
||||
"description": e.description,
|
||||
"evaluation_type": e.evaluation_type,
|
||||
"note": {
|
||||
"note": (
|
||||
{
|
||||
"value": fmt_note(
|
||||
eval_notes[etud.id],
|
||||
note_max=e.note_max,
|
||||
@ -299,7 +300,10 @@ class BulletinBUT:
|
||||
"min": fmt_note(notes_ok.min(), note_max=e.note_max),
|
||||
"max": fmt_note(notes_ok.max(), note_max=e.note_max),
|
||||
"moy": fmt_note(notes_ok.mean(), note_max=e.note_max),
|
||||
},
|
||||
}
|
||||
if not e.is_blocked()
|
||||
else {}
|
||||
),
|
||||
"poids": poids,
|
||||
"url": (
|
||||
url_for(
|
||||
|
@ -35,7 +35,6 @@ moyenne générale d'une UE.
|
||||
"""
|
||||
import dataclasses
|
||||
from dataclasses import dataclass
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import sqlalchemy as sa
|
||||
@ -151,16 +150,18 @@ class ModuleImplResults:
|
||||
self.evaluations_completes_dict = {}
|
||||
for evaluation in moduleimpl.evaluations:
|
||||
eval_df = self._load_evaluation_notes(evaluation)
|
||||
# is_complete ssi tous les inscrits (non dem) au semestre ont une note
|
||||
# is_complete ssi
|
||||
# tous les inscrits (non dem) au module ont une note
|
||||
# ou évaluation déclarée "à prise en compte immédiate"
|
||||
# Les évaluations de rattrapage et 2eme session sont toujours complètes
|
||||
# ou rattrapage, 2eme session, bonus
|
||||
# ET pas bloquée par date (is_blocked)
|
||||
|
||||
etudids_sans_note = inscrits_module - set(eval_df.index) # sans les dem.
|
||||
is_complete = (
|
||||
(evaluation.evaluation_type != Evaluation.EVALUATION_NORMALE)
|
||||
or (evaluation.publish_incomplete)
|
||||
or (not etudids_sans_note)
|
||||
)
|
||||
) and not evaluation.is_blocked()
|
||||
self.evaluations_completes.append(is_complete)
|
||||
self.evaluations_completes_dict[evaluation.id] = is_complete
|
||||
self.evals_etudids_sans_note[evaluation.id] = etudids_sans_note
|
||||
@ -185,7 +186,7 @@ class ModuleImplResults:
|
||||
].index
|
||||
)
|
||||
if evaluation.publish_incomplete:
|
||||
# et en "imédiat", tous ceux sans note
|
||||
# et en "immédiat", tous ceux sans note
|
||||
eval_etudids_attente |= etudids_sans_note
|
||||
# Synthèse pour état du module:
|
||||
self.etudids_attente |= eval_etudids_attente
|
||||
@ -276,7 +277,7 @@ class ModuleImplResults:
|
||||
) / [e.note_max / 20.0 for e in moduleimpl.evaluations]
|
||||
|
||||
def get_eval_notes_dict(self, evaluation_id: int) -> dict:
|
||||
"""Notes d'une évaulation, brutes, sous forme d'un dict
|
||||
"""Notes d'une évaluation, brutes, sous forme d'un dict
|
||||
{ etudid : valeur }
|
||||
avec les valeurs float, ou "ABS" ou EXC
|
||||
"""
|
||||
|
@ -230,8 +230,8 @@ class ResultatsSemestre(ResultatsCache):
|
||||
date_modif = cursor.one_or_none()
|
||||
last_modif = date_modif[0] if date_modif else None
|
||||
return {
|
||||
"coefficient": evaluation.coefficient or 0.0,
|
||||
"description": evaluation.description or "",
|
||||
"coefficient": evaluation.coefficient,
|
||||
"description": evaluation.description,
|
||||
"evaluation_id": evaluation.id,
|
||||
"jour": evaluation.date_debut or datetime.datetime(1900, 1, 1),
|
||||
"etat": {
|
||||
|
@ -10,6 +10,7 @@ from flask_login import current_user
|
||||
import sqlalchemy as sa
|
||||
|
||||
from app import db, log
|
||||
from app import models
|
||||
from app.models.etudiants import Identite
|
||||
from app.models.events import ScolarNews
|
||||
from app.models.notes import NotesNotes
|
||||
@ -24,7 +25,7 @@ NOON = datetime.time(12, 00)
|
||||
DEFAULT_EVALUATION_TIME = datetime.time(8, 0)
|
||||
|
||||
|
||||
class Evaluation(db.Model):
|
||||
class Evaluation(models.ScoDocModel):
|
||||
"""Evaluation (contrôle, examen, ...)"""
|
||||
|
||||
__tablename__ = "notes_evaluation"
|
||||
@ -36,9 +37,9 @@ class Evaluation(db.Model):
|
||||
)
|
||||
date_debut = db.Column(db.DateTime(timezone=True), nullable=True)
|
||||
date_fin = db.Column(db.DateTime(timezone=True), nullable=True)
|
||||
description = db.Column(db.Text)
|
||||
note_max = db.Column(db.Float)
|
||||
coefficient = db.Column(db.Float)
|
||||
description = db.Column(db.Text, nullable=False)
|
||||
note_max = db.Column(db.Float, nullable=False)
|
||||
coefficient = db.Column(db.Float, nullable=False)
|
||||
visibulletin = db.Column(
|
||||
db.Boolean, nullable=False, default=True, server_default="true"
|
||||
)
|
||||
@ -46,10 +47,14 @@ class Evaluation(db.Model):
|
||||
publish_incomplete = db.Column(
|
||||
db.Boolean, nullable=False, default=False, server_default="false"
|
||||
)
|
||||
# type d'evaluation: 0 normale, 1 rattrapage, 2 "2eme session"
|
||||
"prise en compte immédiate"
|
||||
evaluation_type = db.Column(
|
||||
db.Integer, nullable=False, default=0, server_default="0"
|
||||
)
|
||||
"type d'evaluation: 0 normale, 1 rattrapage, 2 2eme session, 3 bonus"
|
||||
blocked_until = db.Column(db.DateTime(timezone=True), nullable=True)
|
||||
"date de prise en compte"
|
||||
BLOCKED_FOREVER = datetime.datetime(2666, 12, 31, tzinfo=scu.TIME_ZONE)
|
||||
# ordre de presentation (par défaut, le plus petit numero
|
||||
# est la plus ancienne eval):
|
||||
numero = db.Column(db.Integer, nullable=False, default=0)
|
||||
@ -79,6 +84,7 @@ class Evaluation(db.Model):
|
||||
date_fin: datetime.datetime = None,
|
||||
description=None,
|
||||
note_max=None,
|
||||
blocked_until=None,
|
||||
coefficient=None,
|
||||
visibulletin=None,
|
||||
publish_incomplete=None,
|
||||
@ -208,6 +214,10 @@ class Evaluation(db.Model):
|
||||
def to_dict_api(self) -> dict:
|
||||
"Représentation dict pour API JSON"
|
||||
return {
|
||||
"blocked": self.is_blocked(),
|
||||
"blocked_until": (
|
||||
self.blocked_until.isoformat() if self.blocked_until else ""
|
||||
),
|
||||
"coefficient": self.coefficient,
|
||||
"date_debut": self.date_debut.isoformat() if self.date_debut else "",
|
||||
"date_fin": self.date_fin.isoformat() if self.date_fin else "",
|
||||
@ -244,14 +254,14 @@ class Evaluation(db.Model):
|
||||
|
||||
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(self.moduleimpl.id) + 1
|
||||
for k in self.__dict__:
|
||||
if k != "_sa_instance_state" and k != "id" and k in data:
|
||||
setattr(self, k, data[k])
|
||||
def convert_dict_fields(self, args: dict) -> dict:
|
||||
"""Convert fields in the given dict. No other side effect.
|
||||
returns: dict to store in model's db.
|
||||
"""
|
||||
check_convert_evaluation_args(self.moduleimpl, args)
|
||||
if args.get("numero") is None:
|
||||
args["numero"] = Evaluation.get_max_numero(self.moduleimpl.id) + 1
|
||||
return args
|
||||
|
||||
@classmethod
|
||||
def get_evaluation(
|
||||
@ -370,19 +380,6 @@ class Evaluation(db.Model):
|
||||
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
|
||||
"""
|
||||
d = dict(self.__dict__)
|
||||
d.pop("id") # get rid of id
|
||||
d.pop("_sa_instance_state") # get rid of SQLAlchemy special attr
|
||||
for k in not_copying:
|
||||
d.pop(k)
|
||||
copy = self.__class__(**d)
|
||||
db.session.add(copy)
|
||||
return copy
|
||||
|
||||
def is_matin(self) -> bool:
|
||||
"Evaluation commençant le matin (faux si pas de date)"
|
||||
if not self.date_debut:
|
||||
@ -395,6 +392,14 @@ class Evaluation(db.Model):
|
||||
return False
|
||||
return self.date_debut.time() >= NOON
|
||||
|
||||
def is_blocked(self, now=None) -> bool:
|
||||
"True si prise en compte bloquée"
|
||||
if self.blocked_until is None:
|
||||
return False
|
||||
if now is None:
|
||||
now = datetime.datetime.now(scu.TIME_ZONE)
|
||||
return self.blocked_until > now
|
||||
|
||||
def set_default_poids(self) -> bool:
|
||||
"""Initialize les poids vers les UE à leurs valeurs par défaut
|
||||
C'est à dire à 1 si le coef. module/UE est non nul, 0 sinon.
|
||||
@ -621,6 +626,8 @@ def check_convert_evaluation_args(moduleimpl: "ModuleImpl", data: dict):
|
||||
"Heures de l'évaluation incohérentes !",
|
||||
dest_url="javascript:history.back();",
|
||||
)
|
||||
if "blocked_until" in data:
|
||||
data["blocked_until"] = data["blocked_until"] or None
|
||||
|
||||
|
||||
def heure_to_time(heure: str) -> datetime.time:
|
||||
|
@ -93,6 +93,10 @@ class FormSemestre(db.Model):
|
||||
db.Boolean(), nullable=False, default=False, server_default="false"
|
||||
)
|
||||
"Si vrai, la moyenne générale indicative BUT n'est pas calculée"
|
||||
mode_calcul_moyennes = db.Column(
|
||||
db.Integer, nullable=False, default=0, server_default="0"
|
||||
)
|
||||
"pour usage futur"
|
||||
gestion_semestrielle = db.Column(
|
||||
db.Boolean(), nullable=False, default=False, server_default="false"
|
||||
)
|
||||
|
@ -318,7 +318,7 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
||||
if nt.bonus_ues is not None:
|
||||
u["cur_moy_ue_txt"] += " (+ues)"
|
||||
u["moy_ue_txt"] = scu.fmt_note(ue_status["moy"])
|
||||
if ue_status["coef_ue"] != None:
|
||||
if ue_status["coef_ue"] is not None:
|
||||
u["coef_ue_txt"] = scu.fmt_coef(ue_status["coef_ue"])
|
||||
else:
|
||||
u["coef_ue_txt"] = "-"
|
||||
@ -558,6 +558,8 @@ def _ue_mod_bulletin(
|
||||
).order_by(Evaluation.numero, Evaluation.date_debut)
|
||||
# (plus ancienne d'abord)
|
||||
for e in all_evals:
|
||||
if e.is_blocked():
|
||||
continue # ignore évaluations bloquées
|
||||
if not e.visibulletin and version != "long":
|
||||
continue
|
||||
is_complete = e.id in complete_eval_ids
|
||||
@ -625,7 +627,7 @@ def _ue_mod_bulletin(
|
||||
)
|
||||
):
|
||||
# ne liste pas les eval malus sans notes
|
||||
# ni les rattrapages et sessions 2 si pas de note
|
||||
# ni les rattrapages, sessions 2 et bonus si pas de note
|
||||
if e.id in complete_eval_ids:
|
||||
mod["evaluations"].append(e_dict)
|
||||
else:
|
||||
|
@ -25,7 +25,7 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""Génération du bulletin en format JSON
|
||||
"""Génération du bulletin en format JSON (formations classiques)
|
||||
|
||||
"""
|
||||
import datetime
|
||||
|
@ -108,7 +108,7 @@ def evaluation_create_form(
|
||||
raise ValueError("missing evaluation_id parameter")
|
||||
initvalues = evaluation.to_dict()
|
||||
moduleimpl_id = initvalues["moduleimpl_id"]
|
||||
submitlabel = "Modifier les données"
|
||||
submitlabel = "Modifier l'évaluation"
|
||||
action = "Modification d'une évaluation"
|
||||
link = ""
|
||||
# Note maximale actuelle dans cette éval ?
|
||||
@ -142,6 +142,15 @@ def evaluation_create_form(
|
||||
else:
|
||||
poids = 0.0
|
||||
initvalues[f"poids_{ue.id}"] = poids
|
||||
# Blocage
|
||||
if edit:
|
||||
initvalues["blocked"] = evaluation.is_blocked()
|
||||
initvalues["blocked_until"] = (
|
||||
evaluation.blocked_until.strftime("%d/%m/%Y")
|
||||
if evaluation.blocked_until
|
||||
and evaluation.blocked_until < Evaluation.BLOCKED_FOREVER
|
||||
else ""
|
||||
)
|
||||
#
|
||||
form = [
|
||||
("evaluation_id", {"default": evaluation_id, "input_type": "hidden"}),
|
||||
@ -260,6 +269,7 @@ def evaluation_create_form(
|
||||
"explanation": """importance de l'évaluation (multiplie les poids ci-dessous).
|
||||
Non utilisé pour les bonus.""",
|
||||
"allow_null": False,
|
||||
"dom_id": "evaluation-edit-coef",
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -301,6 +311,28 @@ def evaluation_create_form(
|
||||
},
|
||||
),
|
||||
)
|
||||
# Bloquage / date prise en compte
|
||||
form += [
|
||||
(
|
||||
"blocked",
|
||||
{
|
||||
"input_type": "boolcheckbox",
|
||||
"title": "Bloquer la prise en compte",
|
||||
"explanation": """empêche la prise en compte
|
||||
(ne sera pas visible sur les bulletins ni dans les tableaux)""",
|
||||
"dom_id": "evaluation-edit-blocked",
|
||||
},
|
||||
),
|
||||
(
|
||||
"blocked_until",
|
||||
{
|
||||
"input_type": "datedmy",
|
||||
"title": "Date déblocage",
|
||||
"size": 12,
|
||||
"explanation": "sera débloquée à partir de cette date",
|
||||
},
|
||||
),
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
request.base_url,
|
||||
vals,
|
||||
@ -331,7 +363,9 @@ def evaluation_create_form(
|
||||
+ "\n".join(H)
|
||||
+ "\n"
|
||||
+ tf[1]
|
||||
+ render_template("scodoc/help/evaluations.j2", is_apc=is_apc)
|
||||
+ render_template(
|
||||
"scodoc/help/evaluations.j2", is_apc=is_apc, modimpl=modimpl
|
||||
)
|
||||
+ render_template("sco_timepicker.j2")
|
||||
+ html_sco_header.sco_footer()
|
||||
)
|
||||
@ -357,7 +391,8 @@ def evaluation_create_form(
|
||||
raise ScoValueError("Heure début invalide") from exc
|
||||
args["date_debut"] = datetime.datetime.combine(date_debut, heure_debut)
|
||||
args.pop("heure_debut", None)
|
||||
# note: ce formulaire ne permet de créer que des évaluation avec debut et fin sur le même jour.
|
||||
# note: ce formulaire ne permet de créer que des évaluations
|
||||
# avec debut et fin sur le même jour.
|
||||
if date_debut and args.get("heure_fin"):
|
||||
try:
|
||||
heure_fin = heure_to_time(args["heure_fin"])
|
||||
@ -365,6 +400,19 @@ def evaluation_create_form(
|
||||
raise ScoValueError("Heure fin invalide") from exc
|
||||
args["date_fin"] = datetime.datetime.combine(date_debut, heure_fin)
|
||||
args.pop("heure_fin", None)
|
||||
# Blocage:
|
||||
if args.get("blocked"):
|
||||
if args.get("blocked_until"):
|
||||
try:
|
||||
args["blocked_until"] = datetime.datetime.strptime(
|
||||
args["blocked_until"], "%d/%m/%Y"
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise ScoValueError("Date déblocage (j/m/a) invalide") from exc
|
||||
else: # bloquage coché sans date
|
||||
args["blocked_until"] = Evaluation.BLOCKED_FOREVER
|
||||
else: # si pas coché, efface date déblocage
|
||||
args["blocked_until"] = None
|
||||
#
|
||||
if edit:
|
||||
evaluation.from_dict(args)
|
||||
|
@ -40,7 +40,7 @@ from app import db
|
||||
from app.auth.models import User
|
||||
from app.comp import res_sem
|
||||
from app.comp.res_compat import NotesTableCompat
|
||||
from app.models import Evaluation, FormSemestre, ModuleImpl
|
||||
from app.models import Evaluation, FormSemestre, ModuleImpl, Module
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
@ -48,7 +48,6 @@ from app.scodoc.gen_tables import GenTable
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import sco_cal
|
||||
from app.scodoc import sco_evaluation_db
|
||||
from app.scodoc import sco_edit_module
|
||||
from app.scodoc import sco_formsemestre_inscriptions
|
||||
from app.scodoc import sco_groups
|
||||
from app.scodoc import sco_moduleimpl
|
||||
@ -113,6 +112,7 @@ def do_evaluation_etat(
|
||||
nb_neutre,
|
||||
nb_att,
|
||||
moy, median, mini, maxi : # notes, en chaine, sur 20
|
||||
maxi_num : note max, numérique
|
||||
last_modif: datetime, *
|
||||
gr_complets, gr_incomplets,
|
||||
evalcomplete *
|
||||
@ -129,11 +129,12 @@ def do_evaluation_etat(
|
||||
) # { etudid : note }
|
||||
|
||||
# ---- Liste des groupes complets et incomplets
|
||||
E = sco_evaluation_db.get_evaluations_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
|
||||
formsemestre_id = M["formsemestre_id"]
|
||||
evaluation = Evaluation.get_evaluation(evaluation_id)
|
||||
modimpl: ModuleImpl = evaluation.moduleimpl
|
||||
module: Module = modimpl.module
|
||||
|
||||
is_malus = module.module_type == ModuleType.MALUS # True si module de malus
|
||||
formsemestre_id = modimpl.formsemestre_id
|
||||
# Si partition_id is None, prend 'all' ou bien la premiere:
|
||||
if partition_id is None:
|
||||
if select_first_partition:
|
||||
@ -149,9 +150,7 @@ def do_evaluation_etat(
|
||||
insem = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits(
|
||||
formsemestre_id
|
||||
)
|
||||
insmod = sco_moduleimpl.do_moduleimpl_inscription_list(
|
||||
moduleimpl_id=E["moduleimpl_id"]
|
||||
)
|
||||
insmod = sco_moduleimpl.do_moduleimpl_inscription_list(moduleimpl_id=modimpl.id)
|
||||
insmodset = {x["etudid"] for x in insmod}
|
||||
# retire de insem ceux qui ne sont pas inscrits au module
|
||||
ins = [i for i in insem if i["etudid"] in insmodset]
|
||||
@ -174,9 +173,9 @@ def do_evaluation_etat(
|
||||
maxi_num = None
|
||||
else:
|
||||
median = scu.fmt_note(median_num)
|
||||
moy = scu.fmt_note(moy_num, E["note_max"])
|
||||
mini = scu.fmt_note(mini_num, E["note_max"])
|
||||
maxi = scu.fmt_note(maxi_num, E["note_max"])
|
||||
moy = scu.fmt_note(moy_num, evaluation.note_max)
|
||||
mini = scu.fmt_note(mini_num, evaluation.note_max)
|
||||
maxi = scu.fmt_note(maxi_num, evaluation.note_max)
|
||||
# cherche date derniere modif note
|
||||
if len(etuds_notes_dict):
|
||||
t = [x["date"] for x in etuds_notes_dict.values()]
|
||||
@ -218,14 +217,16 @@ def do_evaluation_etat(
|
||||
gr_incomplets = list(group_nb_missing.keys())
|
||||
gr_incomplets.sort()
|
||||
|
||||
complete = (total_nb_missing == 0) or (
|
||||
E["evaluation_type"] != Evaluation.EVALUATION_NORMALE
|
||||
complete = (
|
||||
(total_nb_missing == 0)
|
||||
or (evaluation.evaluation_type != Evaluation.EVALUATION_NORMALE)
|
||||
and not evaluation.is_blocked()
|
||||
)
|
||||
evalattente = (total_nb_missing > 0) and (
|
||||
(total_nb_missing == total_nb_att) or E["publish_incomplete"]
|
||||
(total_nb_missing == total_nb_att) or evaluation.publish_incomplete
|
||||
)
|
||||
# mais ne met pas en attente les evals immediates sans aucune notes:
|
||||
if E["publish_incomplete"] and nb_notes == 0:
|
||||
if evaluation.publish_incomplete and nb_notes == 0:
|
||||
evalattente = False
|
||||
|
||||
# Calcul moyenne dans chaque groupe de TD
|
||||
@ -236,10 +237,10 @@ def do_evaluation_etat(
|
||||
{
|
||||
"group_id": group_id,
|
||||
"group_name": group_by_id[group_id]["group_name"],
|
||||
"gr_moy": scu.fmt_note(gr_moy, E["note_max"]),
|
||||
"gr_median": scu.fmt_note(gr_median, E["note_max"]),
|
||||
"gr_mini": scu.fmt_note(gr_mini, E["note_max"]),
|
||||
"gr_maxi": scu.fmt_note(gr_maxi, E["note_max"]),
|
||||
"gr_moy": scu.fmt_note(gr_moy, evaluation.note_max),
|
||||
"gr_median": scu.fmt_note(gr_median, evaluation.note_max),
|
||||
"gr_mini": scu.fmt_note(gr_mini, evaluation.note_max),
|
||||
"gr_maxi": scu.fmt_note(gr_maxi, evaluation.note_max),
|
||||
"gr_nb_notes": len(notes),
|
||||
"gr_nb_att": len([x for x in notes if x == scu.NOTES_ATTENTE]),
|
||||
}
|
||||
|
@ -534,7 +534,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(
|
||||
f"Evaluation {evaluation.descr_date()} (coef. {(evaluation.coefficient or 0.0):g})",
|
||||
f"Evaluation {evaluation.descr_date()} (coef. {(evaluation.coefficient):g})",
|
||||
style,
|
||||
)
|
||||
# ligne blanche
|
||||
|
@ -531,6 +531,10 @@ def _ligne_evaluation(
|
||||
if not evaluation.visibulletin:
|
||||
tr_class += " non_visible_inter"
|
||||
tr_class_1 = "mievr"
|
||||
if evaluation.is_blocked():
|
||||
tr_class += " evaluation_blocked"
|
||||
tr_class_1 += " evaluation_blocked"
|
||||
|
||||
if not first_eval:
|
||||
H.append("""<tr><td colspan="8"> </td></tr>""")
|
||||
tr_class_1 += " mievr_spaced"
|
||||
@ -564,7 +568,7 @@ def _ligne_evaluation(
|
||||
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
|
||||
}" class="mievr_evalnodate">Évaluation sans date</a>"""
|
||||
)
|
||||
H.append(f" <em>{evaluation.description or ''}</em>")
|
||||
H.append(f" <em>{evaluation.description}</em>")
|
||||
if evaluation.evaluation_type == Evaluation.EVALUATION_RATTRAPAGE:
|
||||
H.append(
|
||||
"""<span class="mievr_rattr" title="remplace si meilleure note">rattrapage</span>"""
|
||||
@ -611,8 +615,15 @@ def _ligne_evaluation(
|
||||
else:
|
||||
H.append(arrow_none)
|
||||
|
||||
if etat["evalcomplete"]:
|
||||
etat_txt = f"""(prise en compte{
|
||||
if evaluation.is_blocked():
|
||||
etat_txt = f"""évaluation bloquée {
|
||||
"jusqu'au " + evaluation.blocked_until.strftime("%d/%m/%Y")
|
||||
if evaluation.blocked_until < Evaluation.BLOCKED_FOREVER
|
||||
else "" }
|
||||
"""
|
||||
etat_descr = """prise en compte bloquée"""
|
||||
elif etat["evalcomplete"]:
|
||||
etat_txt = f"""Moyenne (prise en compte{
|
||||
""
|
||||
if evaluation.visibulletin
|
||||
else ", cachée en intermédiaire"})
|
||||
@ -621,7 +632,7 @@ def _ligne_evaluation(
|
||||
", évaluation cachée sur les bulletins en version intermédiaire et sur la passerelle"
|
||||
}"""
|
||||
elif etat["evalattente"] and not evaluation.publish_incomplete:
|
||||
etat_txt = "(prise en compte, mais <b>notes en attente</b>)"
|
||||
etat_txt = "Moyenne (prise en compte, mais <b>notes en attente</b>)"
|
||||
etat_descr = "il y a des notes en attente"
|
||||
elif evaluation.publish_incomplete:
|
||||
etat_txt = """(prise en compte <b>immédiate</b>)"""
|
||||
@ -629,11 +640,12 @@ def _ligne_evaluation(
|
||||
"il manque des notes, mais la prise en compte immédiate a été demandée"
|
||||
)
|
||||
elif etat["nb_notes"] != 0:
|
||||
etat_txt = "(<b>non</b> prise en compte)"
|
||||
etat_txt = "Moyenne (<b>non</b> prise en compte)"
|
||||
etat_descr = "il manque des notes"
|
||||
else:
|
||||
etat_txt = ""
|
||||
if can_edit_evals and etat_txt:
|
||||
if etat_txt:
|
||||
if can_edit_evals:
|
||||
etat_txt = f"""<a href="{ url_for("notes.evaluation_edit",
|
||||
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
|
||||
}" title="{etat_descr}">{etat_txt}</a>"""
|
||||
@ -641,16 +653,16 @@ def _ligne_evaluation(
|
||||
H.append(
|
||||
f"""</span></span></td>
|
||||
</tr>
|
||||
<tr class="{tr_class}">
|
||||
<tr class="{tr_class} mievr_in">
|
||||
<th class="moduleimpl_evaluations" colspan="2"> </th>
|
||||
<th class="moduleimpl_evaluations">Durée</th>
|
||||
<th class="moduleimpl_evaluations">Coef.</th>
|
||||
<th class="moduleimpl_evaluations">Notes</th>
|
||||
<th class="moduleimpl_evaluations">Abs</th>
|
||||
<th class="moduleimpl_evaluations">N</th>
|
||||
<th class="moduleimpl_evaluations" colspan="2">Moyenne {etat_txt}</th>
|
||||
<th class="moduleimpl_evaluations moduleimpl_evaluation_moy" colspan="2"><span>{etat_txt}</span></th>
|
||||
</tr>
|
||||
<tr class="{tr_class}">
|
||||
<tr class="{tr_class} mievr_in">
|
||||
<td class="mievr">"""
|
||||
)
|
||||
if can_edit_evals:
|
||||
@ -832,7 +844,7 @@ def _evaluation_poids_html(evaluation: Evaluation, max_poids: float = 0.0) -> st
|
||||
+ "\n".join(
|
||||
[
|
||||
f"""<div title="poids vers {ue.acronyme}: {poids:g}">
|
||||
<div style="--size:{math.sqrt(poids*(evaluation.coefficient or 0.)/max_poids*144)}px;
|
||||
<div style="--size:{math.sqrt(poids*(evaluation.coefficient)/max_poids*144)}px;
|
||||
{'background-color: ' + ue.color + ';' if ue.color else ''}
|
||||
"></div>
|
||||
</div>"""
|
||||
|
@ -884,7 +884,7 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]):
|
||||
if evaluation.date_debut:
|
||||
indication_date = evaluation.date_debut.date().isoformat()
|
||||
else:
|
||||
indication_date = scu.sanitize_filename(evaluation.description or "")[:12]
|
||||
indication_date = scu.sanitize_filename(evaluation.description)[:12]
|
||||
eval_name = f"{evaluation.moduleimpl.module.code}-{indication_date}"
|
||||
|
||||
date_str = (
|
||||
|
@ -1469,6 +1469,9 @@ span.eval_title {
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
#evaluation-edit-blocked td, #evaluation-edit-coef td {
|
||||
padding-top: 24px;
|
||||
}
|
||||
/* #saisie_notes span.eval_title {
|
||||
border-bottom: 1px solid rgb(100,100,100);
|
||||
}
|
||||
@ -2099,6 +2102,14 @@ th.moduleimpl_evaluations a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
tr.mievr_in.evaluation_blocked th.moduleimpl_evaluation_moy span, tr.evaluation_blocked th.moduleimpl_evaluation_moy a {
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
background-color: yellow;
|
||||
padding: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
tr.mievr {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
@ -2153,6 +2164,15 @@ tr.mievr.non_visible_inter th {
|
||||
);
|
||||
}
|
||||
|
||||
tr.mievr_tit.evaluation_blocked td,tr.mievr_tit.evaluation_blocked th {
|
||||
background-image: radial-gradient(#bd7777 1px, transparent 1px);
|
||||
background-size: 10px 10px;
|
||||
}
|
||||
tr.mievr_in.evaluation_blocked td, tr.mievr_in.evaluation_blocked th {
|
||||
background-color: rgb(195, 235, 255);
|
||||
}
|
||||
|
||||
|
||||
tr.mievr th {
|
||||
background-color: white;
|
||||
}
|
||||
@ -2163,6 +2183,7 @@ tr.mievr td.mievr {
|
||||
|
||||
tr.mievr td.mievr_menu {
|
||||
width: 110px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
tr.mievr td.mievr_dur {
|
||||
|
@ -457,7 +457,7 @@ class TableRecap(tb.Table):
|
||||
row_descr_eval.add_cell(
|
||||
col_id,
|
||||
None,
|
||||
e.description or "",
|
||||
e.description,
|
||||
target=url_for(
|
||||
"notes.evaluation_listenotes",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
|
@ -20,7 +20,7 @@ Assiduité lors de l'évaluation
|
||||
<a class="stdlink" href="{{
|
||||
url_for('notes.evaluation_listenotes',
|
||||
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
|
||||
}}"><em>{{evaluation.description or ''}}</em></a>
|
||||
}}"><em>{{evaluation.description}}</em></a>
|
||||
{% endif %}
|
||||
<a style="margin-left:32px;" href="{{request.url}}&fmt=xlsx">{{scu.ICON_XLS|safe}}</a>
|
||||
</div>
|
||||
|
@ -1642,7 +1642,7 @@ def evaluation_delete(evaluation_id):
|
||||
.first_or_404()
|
||||
)
|
||||
|
||||
tit = f"""Suppression de l'évaluation {evaluation.description or ""} ({evaluation.descr_date()})"""
|
||||
tit = f"""Suppression de l'évaluation {evaluation.description} ({evaluation.descr_date()})"""
|
||||
etat = sco_evaluations.do_evaluation_etat(evaluation.id)
|
||||
H = [
|
||||
f"""
|
||||
|
83
migrations/versions/cddabc3f868a_evaluation_bloquee.py
Normal file
83
migrations/versions/cddabc3f868a_evaluation_bloquee.py
Normal file
@ -0,0 +1,83 @@
|
||||
"""evaluation bloquee
|
||||
|
||||
Revision ID: cddabc3f868a
|
||||
Revises: 2e4875004e12
|
||||
Create Date: 2024-02-25 16:39:45.947342
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.orm import sessionmaker # added by ev
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "cddabc3f868a"
|
||||
down_revision = "2e4875004e12"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
Session = sessionmaker()
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ces champs étaient nullables
|
||||
# Added by ev: remove duplicates
|
||||
bind = op.get_bind()
|
||||
session = Session(bind=bind)
|
||||
session.execute(
|
||||
sa.text(
|
||||
"""UPDATE notes_evaluation SET description='' WHERE description IS NULL;"""
|
||||
)
|
||||
)
|
||||
session.execute(
|
||||
sa.text("""UPDATE notes_evaluation SET note_max=20. WHERE note_max IS NULL;""")
|
||||
)
|
||||
session.execute(
|
||||
sa.text(
|
||||
"""UPDATE notes_evaluation SET coefficient=0. WHERE coefficient IS NULL;"""
|
||||
)
|
||||
)
|
||||
#
|
||||
with op.batch_alter_table("notes_evaluation", schema=None) as batch_op:
|
||||
batch_op.add_column(
|
||||
sa.Column("blocked_until", sa.DateTime(timezone=True), nullable=True)
|
||||
)
|
||||
batch_op.alter_column("description", existing_type=sa.TEXT(), nullable=False)
|
||||
batch_op.alter_column(
|
||||
"note_max", existing_type=sa.DOUBLE_PRECISION(precision=53), nullable=False
|
||||
)
|
||||
batch_op.alter_column(
|
||||
"coefficient",
|
||||
existing_type=sa.DOUBLE_PRECISION(precision=53),
|
||||
nullable=False,
|
||||
)
|
||||
|
||||
with op.batch_alter_table("notes_formsemestre", schema=None) as batch_op:
|
||||
batch_op.add_column(
|
||||
sa.Column(
|
||||
"mode_calcul_moyennes", sa.Integer(), server_default="0", nullable=False
|
||||
)
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("notes_formsemestre", schema=None) as batch_op:
|
||||
batch_op.drop_column("mode_calcul_moyennes")
|
||||
|
||||
with op.batch_alter_table("notes_evaluation", schema=None) as batch_op:
|
||||
batch_op.alter_column(
|
||||
"coefficient",
|
||||
existing_type=sa.DOUBLE_PRECISION(precision=53),
|
||||
nullable=True,
|
||||
)
|
||||
batch_op.alter_column(
|
||||
"note_max", existing_type=sa.DOUBLE_PRECISION(precision=53), nullable=True
|
||||
)
|
||||
batch_op.alter_column("description", existing_type=sa.TEXT(), nullable=True)
|
||||
batch_op.drop_column("blocked_until")
|
||||
|
||||
# ### end Alembic commands ###
|
Loading…
Reference in New Issue
Block a user