From a5dd1967fc607e98bf96bd1810995cfb3d73486e Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 1 Oct 2022 18:55:32 +0200 Subject: [PATCH] =?UTF-8?q?Tableau=20bord=20module:=20avertissement=20si?= =?UTF-8?q?=20poids=20d'=C3=A9valuation=20nuls.=20D=C3=A9but=20de=20#411.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/evaluations.py | 513 ++++----- app/scodoc/sco_moduleimpl_status.py | 1506 ++++++++++++++------------- app/static/css/scodoc.css | 8 + 3 files changed, 1046 insertions(+), 981 deletions(-) diff --git a/app/models/evaluations.py b/app/models/evaluations.py index 9f321b07..0cf03680 100644 --- a/app/models/evaluations.py +++ b/app/models/evaluations.py @@ -1,250 +1,263 @@ -# -*- coding: UTF-8 -* - -"""ScoDoc models: evaluations -""" -import datetime - -from app import db -from app.models.moduleimpls import ModuleImpl -from app.models.ues import UniteEns - -from app.scodoc.sco_exceptions import ScoValueError -import app.scodoc.notesdb as ndb - - -class Evaluation(db.Model): - """Evaluation (contrôle, examen, ...)""" - - __tablename__ = "notes_evaluation" - - id = db.Column(db.Integer, primary_key=True) - evaluation_id = db.synonym("id") - moduleimpl_id = db.Column( - db.Integer, db.ForeignKey("notes_moduleimpl.id"), index=True - ) - jour = db.Column(db.Date) - heure_debut = db.Column(db.Time) - heure_fin = db.Column(db.Time) - description = db.Column(db.Text) - note_max = db.Column(db.Float) - coefficient = db.Column(db.Float) - visibulletin = db.Column( - db.Boolean, nullable=False, default=True, server_default="true" - ) - publish_incomplete = db.Column( - db.Boolean, nullable=False, default=False, server_default="false" - ) - # type d'evaluation: 0 normale, 1 rattrapage, 2 "2eme session" - evaluation_type = db.Column( - db.Integer, nullable=False, default=0, server_default="0" - ) - # ordre de presentation (par défaut, le plus petit numero - # est la plus ancienne eval): - numero = db.Column(db.Integer) - ues = db.relationship("UniteEns", secondary="evaluation_ue_poids", viewonly=True) - - def __repr__(self): - return f"""""" - - def to_dict(self) -> dict: - "Représentation dict, pour json" - e = dict(self.__dict__) - e.pop("_sa_instance_state", None) - # ScoDoc7 output_formators - e["evaluation_id"] = self.id - e["jour"] = e["jour"].strftime("%d/%m/%Y") if e["jour"] else "" - if self.jour is None: - e["date_debut"] = None - e["date_fin"] = None - else: - e["date_debut"] = datetime.datetime.combine( - self.jour, self.heure_debut or datetime.time(0, 0) - ).isoformat() - e["date_fin"] = datetime.datetime.combine( - self.jour, self.heure_fin or datetime.time(0, 0) - ).isoformat() - e["numero"] = ndb.int_null_is_zero(e["numero"]) - e["poids"] = self.get_ue_poids_dict() # { ue_id : poids } - return evaluation_enrich_dict(e) - - def from_dict(self, data): - """Set evaluation attributes from given dict values.""" - check_evaluation_args(data) - for k in self.__dict__.keys(): - if k != "_sa_instance_state" and k != "id" and k in data: - setattr(self, k, data[k]) - - 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 set_ue_poids(self, ue, poids: float) -> None: - """Set poids évaluation vers cette UE""" - self.update_ue_poids_dict({ue.id: poids}) - - def set_ue_poids_dict(self, ue_poids_dict: dict) -> None: - """set poids vers les UE (remplace existants) - ue_poids_dict = { ue_id : poids } - """ - L = [] - for ue_id, poids in ue_poids_dict.items(): - ue = UniteEns.query.get(ue_id) - L.append(EvaluationUEPoids(evaluation=self, ue=ue, poids=poids)) - self.ue_poids = L - self.moduleimpl.invalidate_evaluations_poids() # inval cache - - def update_ue_poids_dict(self, ue_poids_dict: dict) -> None: - """update poids vers UE (ajoute aux existants)""" - current = self.get_ue_poids_dict() - current.update(ue_poids_dict) - self.set_ue_poids_dict(current) - - def get_ue_poids_dict(self) -> dict: - """returns { ue_id : poids }""" - return {p.ue.id: p.poids for p in self.ue_poids} - - def get_ue_poids_str(self) -> str: - """string describing poids, for excel cells and pdfs - Note: si les poids ne sont pas initialisés (poids par défaut), - ils ne sont pas affichés. - """ - # restreint aux UE du semestre dans lequel est cette évaluation - # au cas où le module ait changé de semestre et qu'il reste des poids - evaluation_semestre_idx = self.moduleimpl.module.semestre_id - return ", ".join( - [ - f"{p.ue.acronyme}: {p.poids}" - for p in sorted( - self.ue_poids, key=lambda p: (p.ue.numero or 0, p.ue.acronyme) - ) - if evaluation_semestre_idx == p.ue.semestre_idx - ] - ) - - -class EvaluationUEPoids(db.Model): - """Poids des évaluations (BUT) - association many to many - """ - - evaluation_id = db.Column( - db.Integer, - db.ForeignKey("notes_evaluation.id", ondelete="CASCADE"), - primary_key=True, - ) - ue_id = db.Column( - db.Integer, - db.ForeignKey("notes_ue.id", ondelete="CASCADE"), - primary_key=True, - ) - poids = db.Column( - db.Float, - nullable=False, - ) - evaluation = db.relationship( - Evaluation, - backref=db.backref("ue_poids", cascade="all, delete-orphan"), - ) - ue = db.relationship( - UniteEns, - backref=db.backref("evaluation_ue_poids", cascade="all, delete-orphan"), - ) - - def __repr__(self): - return f"" - - -# Fonction héritée de ScoDoc7 à refactorer -def evaluation_enrich_dict(e): - """add or convert some fields in an evaluation dict""" - # For ScoDoc7 compat - heure_debut_dt = e["heure_debut"] or datetime.time( - 8, 00 - ) # au cas ou pas d'heure (note externe?) - heure_fin_dt = e["heure_fin"] or datetime.time(8, 00) - e["heure_debut"] = ndb.TimefromISO8601(e["heure_debut"]) - e["heure_fin"] = ndb.TimefromISO8601(e["heure_fin"]) - e["jouriso"] = ndb.DateDMYtoISO(e["jour"]) - heure_debut, heure_fin = e["heure_debut"], e["heure_fin"] - d = ndb.TimeDuration(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"] = "" - # matin, apresmidi: utile pour se referer aux absences: - - if e["jour"] and heure_debut_dt < datetime.time(12, 00): - e["matin"] = 1 - else: - e["matin"] = 0 - if e["jour"] and heure_fin_dt > datetime.time(12, 00): - e["apresmidi"] = 1 - else: - e["apresmidi"] = 0 - return e - - -def check_evaluation_args(args): - "Check coefficient, dates and duration, raises exception if invalid" - moduleimpl_id = args["moduleimpl_id"] - # check bareme - note_max = args.get("note_max", None) - if note_max is None: - raise ScoValueError("missing note_max") - try: - note_max = float(note_max) - except ValueError: - raise ScoValueError("Invalid note_max value") - if note_max < 0: - raise ScoValueError("Invalid note_max value (must be positive or null)") - # check coefficient - coef = args.get("coefficient", None) - if coef is None: - raise ScoValueError("missing coefficient") - try: - coef = float(coef) - except ValueError: - raise ScoValueError("Invalid coefficient value") - if coef < 0: - raise ScoValueError("Invalid coefficient value (must be positive or null)") - # check date - jour = args.get("jour", None) - args["jour"] = jour - if jour: - modimpl = ModuleImpl.query.get(moduleimpl_id) - formsemestre = modimpl.formsemestre - y, m, d = [int(x) for x in ndb.DateDMYtoISO(jour).split("-")] - jour = datetime.date(y, m, d) - if (jour > formsemestre.date_fin) or (jour < formsemestre.date_debut): - raise ScoValueError( - "La date de l'évaluation (%s/%s/%s) n'est pas dans le semestre !" - % (d, m, y), - dest_url="javascript:history.back();", - ) - heure_debut = args.get("heure_debut", None) - args["heure_debut"] = heure_debut - heure_fin = args.get("heure_fin", None) - args["heure_fin"] = heure_fin - if jour and ((not heure_debut) or (not heure_fin)): - raise ScoValueError("Les heures doivent être précisées") - d = ndb.TimeDuration(heure_debut, heure_fin) - if d and ((d < 0) or (d > 60 * 12)): - raise ScoValueError("Heures de l'évaluation incohérentes !") +# -*- coding: UTF-8 -* + +"""ScoDoc models: evaluations +""" +import datetime + +from app import db +from app.models.moduleimpls import ModuleImpl +from app.models.ues import UniteEns + +from app.scodoc.sco_exceptions import ScoValueError +import app.scodoc.notesdb as ndb + + +class Evaluation(db.Model): + """Evaluation (contrôle, examen, ...)""" + + __tablename__ = "notes_evaluation" + + id = db.Column(db.Integer, primary_key=True) + evaluation_id = db.synonym("id") + moduleimpl_id = db.Column( + db.Integer, db.ForeignKey("notes_moduleimpl.id"), index=True + ) + jour = db.Column(db.Date) + heure_debut = db.Column(db.Time) + heure_fin = db.Column(db.Time) + description = db.Column(db.Text) + note_max = db.Column(db.Float) + coefficient = db.Column(db.Float) + visibulletin = db.Column( + db.Boolean, nullable=False, default=True, server_default="true" + ) + publish_incomplete = db.Column( + db.Boolean, nullable=False, default=False, server_default="false" + ) + # type d'evaluation: 0 normale, 1 rattrapage, 2 "2eme session" + evaluation_type = db.Column( + db.Integer, nullable=False, default=0, server_default="0" + ) + # ordre de presentation (par défaut, le plus petit numero + # est la plus ancienne eval): + numero = db.Column(db.Integer) + ues = db.relationship("UniteEns", secondary="evaluation_ue_poids", viewonly=True) + + def __repr__(self): + return f"""""" + + def to_dict(self) -> dict: + "Représentation dict, pour json" + e = dict(self.__dict__) + e.pop("_sa_instance_state", None) + # ScoDoc7 output_formators + e["evaluation_id"] = self.id + e["jour"] = e["jour"].strftime("%d/%m/%Y") if e["jour"] else "" + if self.jour is None: + e["date_debut"] = None + e["date_fin"] = None + else: + e["date_debut"] = datetime.datetime.combine( + self.jour, self.heure_debut or datetime.time(0, 0) + ).isoformat() + e["date_fin"] = datetime.datetime.combine( + self.jour, self.heure_fin or datetime.time(0, 0) + ).isoformat() + e["numero"] = ndb.int_null_is_zero(e["numero"]) + e["poids"] = self.get_ue_poids_dict() # { ue_id : poids } + return evaluation_enrich_dict(e) + + def from_dict(self, data): + """Set evaluation attributes from given dict values.""" + check_evaluation_args(data) + for k in self.__dict__.keys(): + if k != "_sa_instance_state" and k != "id" and k in data: + setattr(self, k, data[k]) + + 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("%H:%M")}""" + elif self.heure_debut and self.heure_fin: + return f"""de {self.heure_debut.strftime("%H:%M")} à {self.heure_fin.strftime("%H:%M")}""" + else: + return "" + + 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 set_ue_poids(self, ue, poids: float) -> None: + """Set poids évaluation vers cette UE""" + self.update_ue_poids_dict({ue.id: poids}) + + def set_ue_poids_dict(self, ue_poids_dict: dict) -> None: + """set poids vers les UE (remplace existants) + ue_poids_dict = { ue_id : poids } + """ + L = [] + for ue_id, poids in ue_poids_dict.items(): + ue = UniteEns.query.get(ue_id) + L.append(EvaluationUEPoids(evaluation=self, ue=ue, poids=poids)) + self.ue_poids = L + self.moduleimpl.invalidate_evaluations_poids() # inval cache + + def update_ue_poids_dict(self, ue_poids_dict: dict) -> None: + """update poids vers UE (ajoute aux existants)""" + current = self.get_ue_poids_dict() + current.update(ue_poids_dict) + self.set_ue_poids_dict(current) + + def get_ue_poids_dict(self) -> dict: + """returns { ue_id : poids }""" + return {p.ue.id: p.poids for p in self.ue_poids} + + def get_ue_poids_str(self) -> str: + """string describing poids, for excel cells and pdfs + Note: si les poids ne sont pas initialisés (poids par défaut), + ils ne sont pas affichés. + """ + # restreint aux UE du semestre dans lequel est cette évaluation + # au cas où le module ait changé de semestre et qu'il reste des poids + evaluation_semestre_idx = self.moduleimpl.module.semestre_id + return ", ".join( + [ + f"{p.ue.acronyme}: {p.poids}" + for p in sorted( + self.ue_poids, key=lambda p: (p.ue.numero or 0, p.ue.acronyme) + ) + if evaluation_semestre_idx == p.ue.semestre_idx + ] + ) + + +class EvaluationUEPoids(db.Model): + """Poids des évaluations (BUT) + association many to many + """ + + evaluation_id = db.Column( + db.Integer, + db.ForeignKey("notes_evaluation.id", ondelete="CASCADE"), + primary_key=True, + ) + ue_id = db.Column( + db.Integer, + db.ForeignKey("notes_ue.id", ondelete="CASCADE"), + primary_key=True, + ) + poids = db.Column( + db.Float, + nullable=False, + ) + evaluation = db.relationship( + Evaluation, + backref=db.backref("ue_poids", cascade="all, delete-orphan"), + ) + ue = db.relationship( + UniteEns, + backref=db.backref("evaluation_ue_poids", cascade="all, delete-orphan"), + ) + + def __repr__(self): + return f"" + + +# Fonction héritée de ScoDoc7 à refactorer +def evaluation_enrich_dict(e: dict): + """add or convert some fields in an evaluation dict""" + # For ScoDoc7 compat + heure_debut_dt = e["heure_debut"] or datetime.time( + 8, 00 + ) # au cas ou pas d'heure (note externe?) + heure_fin_dt = e["heure_fin"] or datetime.time(8, 00) + e["heure_debut"] = ndb.TimefromISO8601(e["heure_debut"]) + e["heure_fin"] = ndb.TimefromISO8601(e["heure_fin"]) + e["jouriso"] = ndb.DateDMYtoISO(e["jour"]) + heure_debut, heure_fin = e["heure_debut"], e["heure_fin"] + d = ndb.TimeDuration(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"] = "" + # matin, apresmidi: utile pour se referer aux absences: + + if e["jour"] and heure_debut_dt < datetime.time(12, 00): + e["matin"] = 1 + else: + e["matin"] = 0 + if e["jour"] and heure_fin_dt > datetime.time(12, 00): + e["apresmidi"] = 1 + else: + e["apresmidi"] = 0 + return e + + +def check_evaluation_args(args): + "Check coefficient, dates and duration, raises exception if invalid" + moduleimpl_id = args["moduleimpl_id"] + # check bareme + note_max = args.get("note_max", None) + if note_max is None: + raise ScoValueError("missing note_max") + try: + note_max = float(note_max) + except ValueError: + raise ScoValueError("Invalid note_max value") + if note_max < 0: + raise ScoValueError("Invalid note_max value (must be positive or null)") + # check coefficient + coef = args.get("coefficient", None) + if coef is None: + raise ScoValueError("missing coefficient") + try: + coef = float(coef) + except ValueError: + raise ScoValueError("Invalid coefficient value") + if coef < 0: + raise ScoValueError("Invalid coefficient value (must be positive or null)") + # check date + jour = args.get("jour", None) + args["jour"] = jour + if jour: + modimpl = ModuleImpl.query.get(moduleimpl_id) + formsemestre = modimpl.formsemestre + y, m, d = [int(x) for x in ndb.DateDMYtoISO(jour).split("-")] + jour = datetime.date(y, m, d) + if (jour > formsemestre.date_fin) or (jour < formsemestre.date_debut): + raise ScoValueError( + "La date de l'évaluation (%s/%s/%s) n'est pas dans le semestre !" + % (d, m, y), + dest_url="javascript:history.back();", + ) + heure_debut = args.get("heure_debut", None) + args["heure_debut"] = heure_debut + heure_fin = args.get("heure_fin", None) + args["heure_fin"] = heure_fin + if jour and ((not heure_debut) or (not heure_fin)): + raise ScoValueError("Les heures doivent être précisées") + d = ndb.TimeDuration(heure_debut, heure_fin) + if d and ((d < 0) or (d > 60 * 12)): + raise ScoValueError("Heures de l'évaluation incohérentes !") diff --git a/app/scodoc/sco_moduleimpl_status.py b/app/scodoc/sco_moduleimpl_status.py index 77fcef2a..e91b62cc 100644 --- a/app/scodoc/sco_moduleimpl_status.py +++ b/app/scodoc/sco_moduleimpl_status.py @@ -1,731 +1,775 @@ -# -*- mode: python -*- -# -*- coding: utf-8 -*- - -############################################################################## -# -# Gestion scolarite IUT -# -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# Emmanuel Viennet emmanuel.viennet@viennet.net -# -############################################################################## - -"""Tableau de bord module -""" -import time - -from flask import g, url_for -from flask_login import current_user - -from app.auth.models import User -from app.comp import res_sem -from app.comp.res_compat import NotesTableCompat -from app.models import ModuleImpl -from app.models.evaluations import Evaluation -import app.scodoc.sco_utils as scu -from app.scodoc.sco_exceptions import ScoInvalidIdType -from app.scodoc.sco_cursus_dut import formsemestre_has_decisions -from app.scodoc.sco_permissions import Permission - -from app.scodoc import html_sco_header -from app.scodoc import htmlutils -from app.scodoc import sco_abs -from app.scodoc import sco_compute_moy -from app.scodoc import sco_cache -from app.scodoc import sco_edit_module -from app.scodoc import sco_evaluations -from app.scodoc import sco_evaluation_db -from app.scodoc import sco_formations -from app.scodoc import sco_formsemestre -from app.scodoc import sco_formsemestre_status -from app.scodoc import sco_groups -from app.scodoc import sco_moduleimpl -from app.scodoc import sco_permissions_check -from app.scodoc import sco_users - -# ported from old DTML code in oct 2009 - -# menu evaluation dans moduleimpl -def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0): - "Menu avec actions sur une evaluation" - E = sco_evaluation_db.do_evaluation_list({"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"]) - - if ( - sco_permissions_check.can_edit_notes( - current_user, E["moduleimpl_id"], allow_ens=False - ) - and nbnotes != 0 - ): - sup_label = "Supprimer évaluation impossible (il y a des notes)" - else: - sup_label = "Supprimer évaluation" - - menuEval = [ - { - "title": "Saisir notes", - "endpoint": "notes.saisie_notes", - "args": { - "evaluation_id": evaluation_id, - }, - "enabled": sco_permissions_check.can_edit_notes( - current_user, E["moduleimpl_id"] - ), - }, - { - "title": "Modifier évaluation", - "endpoint": "notes.evaluation_edit", - "args": { - "evaluation_id": evaluation_id, - }, - "enabled": sco_permissions_check.can_edit_notes( - current_user, E["moduleimpl_id"], allow_ens=False - ), - }, - { - "title": sup_label, - "endpoint": "notes.evaluation_delete", - "args": { - "evaluation_id": evaluation_id, - }, - "enabled": nbnotes == 0 - and sco_permissions_check.can_edit_notes( - current_user, E["moduleimpl_id"], allow_ens=False - ), - }, - { - "title": "Supprimer toutes les notes", - "endpoint": "notes.evaluation_suppress_alln", - "args": { - "evaluation_id": evaluation_id, - }, - "enabled": sco_permissions_check.can_edit_notes( - current_user, E["moduleimpl_id"], allow_ens=False - ), - }, - { - "title": "Afficher les notes", - "endpoint": "notes.evaluation_listenotes", - "args": { - "evaluation_id": evaluation_id, - }, - "enabled": nbnotes > 0, - }, - { - "title": "Placement étudiants", - "endpoint": "notes.placement_eval_selectetuds", - "args": { - "evaluation_id": evaluation_id, - }, - "enabled": sco_permissions_check.can_edit_notes( - current_user, E["moduleimpl_id"] - ), - }, - { - "title": "Absences ce jour", - "endpoint": "absences.EtatAbsencesDate", - "args": { - "date": E["jour"], - "group_ids": group_id, - }, - "enabled": E["jour"], - }, - { - "title": "Vérifier notes vs absents", - "endpoint": "notes.evaluation_check_absences_html", - "args": { - "evaluation_id": evaluation_id, - }, - "enabled": nbnotes > 0 and E["jour"], - }, - ] - - return htmlutils.make_menu("actions", menuEval, alone=True) - - -def _ue_coefs_html(coefs_lst) -> str: - """ """ - max_coef = max([x[1] for x in coefs_lst]) if coefs_lst else 1.0 - H = """ -
-
Coefficients vers les UE
- """ - if coefs_lst: - H += ( - f""" -
- """ - + "\n".join( - [ - f"""
{coef}
{ue.acronyme}
""" - for ue, coef in coefs_lst - ] - ) - + "
" - ) - else: - H += """
non définis
""" - H += "
" - return H - - -def moduleimpl_status(moduleimpl_id=None, partition_id=None): - """Tableau de bord module (liste des evaluations etc)""" - if not isinstance(moduleimpl_id, int): - raise ScoInvalidIdType("moduleimpl_id must be an integer !") - modimpl: ModuleImpl = ModuleImpl.query.get_or_404(moduleimpl_id) - M = modimpl.to_dict() - formsemestre_id = modimpl.formsemestre_id - Mod = sco_edit_module.module_list(args={"module_id": modimpl.module_id})[0] - sem = sco_formsemestre.get_formsemestre(formsemestre_id) - F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0] - mod_inscrits = sco_moduleimpl.do_moduleimpl_inscription_list( - moduleimpl_id=M["moduleimpl_id"] - ) - - nt: NotesTableCompat = res_sem.load_formsemestre_results(modimpl.formsemestre) - - mod_evals = sco_evaluation_db.do_evaluation_list({"moduleimpl_id": moduleimpl_id}) - mod_evals.sort( - key=lambda x: (x["numero"], x["jour"], x["heure_debut"]), reverse=True - ) # la plus RECENTE en tête - - # - sem_locked = not sem["etat"] - can_edit_evals = ( - sco_permissions_check.can_edit_notes( - current_user, moduleimpl_id, allow_ens=sem["ens_can_edit_eval"] - ) - and not sem_locked - ) - can_edit_notes = ( - sco_permissions_check.can_edit_notes(current_user, moduleimpl_id) - and not sem_locked - ) - arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags() - # - module_resp = User.query.get(M["responsable_id"]) - mod_type_name = scu.MODULE_TYPE_NAMES[Mod["module_type"]] - H = [ - html_sco_header.sco_header( - page_title=f"{mod_type_name} {Mod['code']} {Mod['titre']}" - ), - f"""

{mod_type_name} - {Mod['code']} {Mod['titre']} - {"dans l'UE " + modimpl.module.ue.acronyme if modimpl.module.module_type == scu.ModuleType.MALUS else ""} -

-
- - - """) - - # 2ieme ligne: Semestre, Coef - H.append("""""") - # 3ieme ligne: Formation - H.append( - """""" % F - ) - # Ligne: Inscrits - H.append( - """") - # Ligne: règle de calcul - has_expression = sco_compute_moy.moduleimpl_has_expression(M) - if has_expression: - H.append( - '") - else: - H.append( - '") - H.append( - '
Responsable: - {module_resp.get_nomcomplet()} - ({module_resp.user_name}) - """, - ] - try: - sco_moduleimpl.can_change_module_resp(moduleimpl_id) - H.append( - """modifier""" - % moduleimpl_id - ) - except: - pass - H.append("""""") - H.append( - ", ".join([sco_users.user_info(m["ens_id"])["nomprenom"] for m in M["ens"]]) - ) - H.append("""""") - try: - sco_moduleimpl.can_change_ens(moduleimpl_id) - H.append( - """modifier les enseignants""" - % moduleimpl_id - ) - except: - pass - H.append("""
""") - if sem["semestre_id"] >= 0: - H.append("""Semestre: %s""" % sem["semestre_id"]) - else: - H.append("""""") - if sem_locked: - H.append(scu.icontag("lock32_img", title="verrouillé")) - H.append("""""") - if modimpl.module.is_apc(): - H.append(_ue_coefs_html(modimpl.module.ue_coefs_list())) - else: - H.append( - f"""Coef. dans le semestre: { - "non défini" if modimpl.module.coefficient is None else modimpl.module.coefficient - }""" - ) - H.append("""
Formation: %(titre)s
Inscrits: %d étudiants""" - % len(mod_inscrits) - ) - if current_user.has_permission(Permission.ScoEtudInscrit): - H.append( - """modifier""" - % M["moduleimpl_id"] - ) - H.append("
Règle de calcul: moyenne=%s' - % M["computation_expr"] - ) - H.append("""inutilisée dans cette version de ScoDoc""") - if sco_moduleimpl.can_change_ens(moduleimpl_id, raise_exc=False): - H.append( - f""" supprimer""" - ) - H.append("""""") - H.append("
' # règle de calcul standard' - ) - # if sco_moduleimpl.can_change_ens(moduleimpl_id, raise_exc=False): - # H.append( - # f' (changer)' - # ) - H.append("
Absences dans ce module' - % moduleimpl_id - ) - # Adapté à partir d'une suggestion de DS (Le Havre) - # Liens saisies absences seulement si permission et date courante dans le semestre - if current_user.has_permission( - Permission.ScoAbsChange - ) and sco_formsemestre.sem_est_courant(sem): - datelundi = sco_abs.ddmmyyyy(time.strftime("%d/%m/%Y")).prev_monday() - group_id = sco_groups.get_default_group(formsemestre_id) - H.append( - f""" - - Saisie Absences hebdo. - """ - ) - - H.append("
") - # - if not modimpl.check_apc_conformity(): - H.append( - """
Les poids des évaluations de ce module ne sont - pas encore conformes au PN. - Ses notes ne peuvent pas être prises en compte dans les moyennes d'UE. -
""" - ) - # - if has_expression and nt.expr_diagnostics: - H.append(sco_formsemestre_status.html_expr_diagnostic(nt.expr_diagnostics)) - # - if formsemestre_has_decisions(formsemestre_id): - H.append( - """
  • Décisions de jury saisies: seul le responsable du semestre peut saisir des notes (il devra modifier les décisions de jury).
""" - ) - # - H.append( - """

%d évaluations : - -""" - % (len(mod_evals), moduleimpl_id) - ) - # - # Liste les noms de partitions - partitions = sco_groups.get_partitions_list(sem["formsemestre_id"]) - H.append( - """Afficher les groupes de  -     -Voir toutes les notes - -
-

-""" - % M - ) - - # -------- Tableau des evaluations - top_table_links = "" - if can_edit_evals: - top_table_links = f"""Créer nouvelle évaluation - """ - if mod_evals: - top_table_links += f""" - Trier par date - """ - if mod_evals: - H.append( - '" - ) - H.append("""""") - eval_index = len(mod_evals) - 1 - first_eval = True - for eval in mod_evals: - evaluation: Evaluation = Evaluation.query.get( - eval["evaluation_id"] - ) # TODO unifier - etat = sco_evaluations.do_evaluation_etat( - eval["evaluation_id"], - partition_id=partition_id, - select_first_partition=True, - ) - if eval["evaluation_type"] in ( - scu.EVALUATION_RATTRAPAGE, - scu.EVALUATION_SESSION2, - ): - tr_class = "mievr mievr_rattr" - else: - tr_class = "mievr" - tr_class_1 = "mievr" - if not first_eval: - H.append("""""") - tr_class_1 += " mievr_spaced" - H.append("""""") - H.append( - """""") - - H.append("""") - # - H.append( - """ -""" - % (eval["duree"], "%g" % eval["coefficient"]) - ) - H.append( - """ - - -""") - # - if etat["nb_notes"] == 0: - H.append("""""" % tr_class) - if modimpl.module.is_apc(): - H.append( - f"""""" - ) - else: - H.append('') - H.append("""""") - else: # il y a deja des notes saisies - gr_moyennes = etat["gr_moyennes"] - first_group = True - for gr_moyenne in gr_moyennes: - H.append("""""" % tr_class) - H.append("""""") - if first_group and modimpl.module.is_apc(): - H.append( - f"""""" - ) - else: - H.append("""""") - first_group = False - if gr_moyenne["group_name"] is None: - name = "Tous" # tous - else: - name = "Groupe %s" % gr_moyenne["group_name"] - H.append( - """""") - first_eval = False - - # - - H.append(""" -
 
""" % tr_class_1) - if eval["jour"]: - H.append("""Le %(jour)s%(descrheure)s""" % eval) - else: - H.append( - """Evaluation sans date""" - % eval - ) - H.append("    %(description)s" % eval) - if eval["evaluation_type"] == scu.EVALUATION_RATTRAPAGE: - H.append( - """rattrapage""" - ) - elif eval["evaluation_type"] == scu.EVALUATION_SESSION2: - H.append( - """session 2""" - ) - if etat["last_modif"]: - H.append( - """(dernière modif le %s)""" - % etat["last_modif"].strftime("%d/%m/%Y à %Hh%M") - ) - H.append('') - if has_expression or True: - H.append( - """%2d""" - % eval_index - ) - # Fleches: - H.append('') - if eval_index != (len(mod_evals) - 1) and can_edit_evals: - H.append( - '%s' - % (eval["evaluation_id"], arrow_up) - ) - else: - H.append(arrow_none) - if (eval_index > 0) and can_edit_evals: - H.append( - '%s' - % (eval["evaluation_id"], arrow_down) - ) - else: - H.append(arrow_none) - H.append("") - - eval_index -= 1 - H.append("""
 DuréeCoef.NotesAbsNMoyenne """ - % tr_class - ) - - if etat["evalcomplete"]: - etat_txt = """(prise en compte)""" - etat_descr = "notes utilisées dans les moyennes" - elif eval["publish_incomplete"]: - etat_txt = """(prise en compte immédiate)""" - etat_descr = ( - "il manque des notes, mais la prise en compte immédiate a été demandée" - ) - elif etat["nb_notes"] != 0: - etat_txt = "(non prise en compte)" - etat_descr = "il manque des notes" - else: - etat_txt = "" - if can_edit_evals and etat_txt: - etat_txt = ( - '%s' - % (eval["evaluation_id"], etat_descr, etat_txt) - ) - H.append(etat_txt) - H.append("""
""" % tr_class) - if can_edit_evals: - H.append( - """%s""" - % ( - eval["evaluation_id"], - scu.icontag( - "edit_img", alt="modifier", title="Modifier informations" - ), - ) - ) - if can_edit_notes: - H.append( - """%s""" - % ( - eval["evaluation_id"], - scu.icontag( - "notes_img", alt="saisie notes", title="Saisie des notes" - ), - ) - ) - if etat["nb_notes"] == 0: - if can_edit_evals: - H.append( - """""" - % eval - ) - H.append(scu.icontag("delete_img", alt="supprimer", title="Supprimer")) - if can_edit_evals: - H.append("""""") - elif etat["evalcomplete"]: - H.append( - """%s""" - % (eval["evaluation_id"], scu.icontag("status_green_img", title="ok")) - ) - else: - if etat["evalattente"]: - H.append( - """%s""" - % ( - eval["evaluation_id"], - scu.icontag( - "status_greenorange_img", - file_format="gif", - title="notes en attente", - ), - ) - ) - else: - H.append( - """%s""" - % ( - eval["evaluation_id"], - scu.icontag("status_orange_img", title="il manque des notes"), - ) - ) - # - if eval["visibulletin"]: - H.append( - scu.icontag( - "status_visible_img", title="visible dans bulletins intermédiaires" - ) - ) - else: - H.append(" ") - H.append('') - if can_edit_notes: - H.append( - moduleimpl_evaluation_menu( - eval["evaluation_id"], - nbnotes=etat["nb_notes"], - ) - ) - H.append("%s%s%(nb_notes)s / %(nb_inscrits)s%(nb_abs)s%(nb_neutre)s""" - % etat - ) - if etat["moy"]: - H.append("%s / %g" % (etat["moy"], eval["note_max"])) - H.append( - """  (afficher)""" - % (eval["evaluation_id"],) - ) - else: - H.append( - """saisir notes""" - % (eval["evaluation_id"]) - ) - H.append("""
{ - evaluation.get_ue_poids_str()}
 { - evaluation.get_ue_poids_str()}%s  """ % name - ) - if gr_moyenne["gr_nb_notes"] > 0: - H.append("%(gr_moy)s" % gr_moyenne) - H.append( - """  (%s notes""" - % ( - eval["evaluation_id"], - gr_moyenne["group_id"], - gr_moyenne["gr_nb_notes"], - ) - ) - if gr_moyenne["gr_nb_att"] > 0: - H.append( - """, %s en attente""" - % gr_moyenne["gr_nb_att"] - ) - H.append(""")""") - if gr_moyenne["group_id"] in etat["gr_incomplets"]: - H.append("""[""") - if can_edit_notes: - H.append( - """incomplet]""" - % (eval["evaluation_id"], gr_moyenne["group_id"]) - ) - else: - H.append("""incomplet]""") - else: - H.append("""  """) - if can_edit_notes: - H.append( - """""" - % (eval["evaluation_id"], gr_moyenne["group_id"]) - ) - H.append("pas de notes") - if can_edit_notes: - H.append("""""") - H.append("") - H.append("""
""") - if sem_locked: - H.append(f"""{scu.icontag("lock32_img")} semestre verrouillé""") - elif can_edit_evals: - H.append(top_table_links) - - H.append( - """
- -
- - -
-

Légende

-
    -
  • %s : modifie description de l'évaluation (date, heure, coefficient, ...)
  • -
  • %s : saisie des notes
  • -
  • %s : indique qu'il n'y a aucune note entrée (cliquer pour supprimer cette évaluation)
  • -
  • %s : indique qu'il manque quelques notes dans cette évaluation
  • -
  • %s : toutes les notes sont entrées (cliquer pour les afficher)
  • -
  • %s : indique que cette évaluation sera mentionnée dans les bulletins au format "intermédiaire" -
- -

Rappel : seules les notes des évaluations complètement saisies (affichées en vert) apparaissent dans les bulletins. -

- """ - % ( - scu.icontag("edit_img"), - scu.icontag("notes_img"), - scu.icontag("delete_img"), - scu.icontag("status_orange_img"), - scu.icontag("status_green_img"), - scu.icontag("status_visible_img"), - ) - ) - H.append(html_sco_header.sco_footer()) - return "".join(H) +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Emmanuel Viennet emmanuel.viennet@viennet.net +# +############################################################################## + +"""Tableau de bord module +""" +import time + +from flask import g, url_for +from flask_login import current_user + +from app.auth.models import User +from app.comp import res_sem +from app.comp.res_compat import NotesTableCompat +from app.models import ModuleImpl +from app.models.evaluations import Evaluation +from app.models.ues import UniteEns +import app.scodoc.sco_utils as scu +from app.scodoc.sco_exceptions import ScoInvalidIdType +from app.scodoc.sco_cursus_dut import formsemestre_has_decisions +from app.scodoc.sco_permissions import Permission + +from app.scodoc import html_sco_header +from app.scodoc import htmlutils +from app.scodoc import sco_abs +from app.scodoc import sco_compute_moy +from app.scodoc import sco_cache +from app.scodoc import sco_edit_module +from app.scodoc import sco_evaluations +from app.scodoc import sco_evaluation_db +from app.scodoc import sco_formations +from app.scodoc import sco_formsemestre +from app.scodoc import sco_formsemestre_status +from app.scodoc import sco_groups +from app.scodoc import sco_moduleimpl +from app.scodoc import sco_permissions_check +from app.scodoc import sco_users + +# ported from old DTML code in oct 2009 + +# menu evaluation dans moduleimpl +def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0): + "Menu avec actions sur une evaluation" + E = sco_evaluation_db.do_evaluation_list({"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"]) + + if ( + sco_permissions_check.can_edit_notes( + current_user, E["moduleimpl_id"], allow_ens=False + ) + and nbnotes != 0 + ): + sup_label = "Supprimer évaluation impossible (il y a des notes)" + else: + sup_label = "Supprimer évaluation" + + menuEval = [ + { + "title": "Saisir notes", + "endpoint": "notes.saisie_notes", + "args": { + "evaluation_id": evaluation_id, + }, + "enabled": sco_permissions_check.can_edit_notes( + current_user, E["moduleimpl_id"] + ), + }, + { + "title": "Modifier évaluation", + "endpoint": "notes.evaluation_edit", + "args": { + "evaluation_id": evaluation_id, + }, + "enabled": sco_permissions_check.can_edit_notes( + current_user, E["moduleimpl_id"], allow_ens=False + ), + }, + { + "title": sup_label, + "endpoint": "notes.evaluation_delete", + "args": { + "evaluation_id": evaluation_id, + }, + "enabled": nbnotes == 0 + and sco_permissions_check.can_edit_notes( + current_user, E["moduleimpl_id"], allow_ens=False + ), + }, + { + "title": "Supprimer toutes les notes", + "endpoint": "notes.evaluation_suppress_alln", + "args": { + "evaluation_id": evaluation_id, + }, + "enabled": sco_permissions_check.can_edit_notes( + current_user, E["moduleimpl_id"], allow_ens=False + ), + }, + { + "title": "Afficher les notes", + "endpoint": "notes.evaluation_listenotes", + "args": { + "evaluation_id": evaluation_id, + }, + "enabled": nbnotes > 0, + }, + { + "title": "Placement étudiants", + "endpoint": "notes.placement_eval_selectetuds", + "args": { + "evaluation_id": evaluation_id, + }, + "enabled": sco_permissions_check.can_edit_notes( + current_user, E["moduleimpl_id"] + ), + }, + { + "title": "Absences ce jour", + "endpoint": "absences.EtatAbsencesDate", + "args": { + "date": E["jour"], + "group_ids": group_id, + }, + "enabled": E["jour"], + }, + { + "title": "Vérifier notes vs absents", + "endpoint": "notes.evaluation_check_absences_html", + "args": { + "evaluation_id": evaluation_id, + }, + "enabled": nbnotes > 0 and E["jour"], + }, + ] + + return htmlutils.make_menu("actions", menuEval, alone=True) + + +def _ue_coefs_html(coefs_lst) -> str: + """ """ + max_coef = max([x[1] for x in coefs_lst]) if coefs_lst else 1.0 + H = """ +
+
Coefficients vers les UE
+ """ + if coefs_lst: + H += ( + f""" +
+ """ + + "\n".join( + [ + f"""
{coef}
{ue.acronyme}
""" + for ue, coef in coefs_lst + ] + ) + + "
" + ) + else: + H += """
non définis
""" + H += "
" + return H + + +def moduleimpl_status(moduleimpl_id=None, partition_id=None): + """Tableau de bord module (liste des evaluations etc)""" + if not isinstance(moduleimpl_id, int): + raise ScoInvalidIdType("moduleimpl_id must be an integer !") + modimpl: ModuleImpl = ModuleImpl.query.get_or_404(moduleimpl_id) + M = modimpl.to_dict() + formsemestre_id = modimpl.formsemestre_id + Mod = sco_edit_module.module_list(args={"module_id": modimpl.module_id})[0] + sem = sco_formsemestre.get_formsemestre(formsemestre_id) + F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0] + mod_inscrits = sco_moduleimpl.do_moduleimpl_inscription_list( + moduleimpl_id=M["moduleimpl_id"] + ) + + nt: NotesTableCompat = res_sem.load_formsemestre_results(modimpl.formsemestre) + + mod_evals = sco_evaluation_db.do_evaluation_list({"moduleimpl_id": moduleimpl_id}) + mod_evals.sort( + key=lambda x: (x["numero"], x["jour"], x["heure_debut"]), reverse=True + ) # la plus RECENTE en tête + + # + sem_locked = not sem["etat"] + can_edit_evals = ( + sco_permissions_check.can_edit_notes( + current_user, moduleimpl_id, allow_ens=sem["ens_can_edit_eval"] + ) + and not sem_locked + ) + can_edit_notes = ( + sco_permissions_check.can_edit_notes(current_user, moduleimpl_id) + and not sem_locked + ) + arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags() + # + module_resp = User.query.get(M["responsable_id"]) + mod_type_name = scu.MODULE_TYPE_NAMES[Mod["module_type"]] + H = [ + html_sco_header.sco_header( + page_title=f"{mod_type_name} {Mod['code']} {Mod['titre']}" + ), + f"""

{mod_type_name} + {Mod['code']} {Mod['titre']} + {"dans l'UE " + modimpl.module.ue.acronyme if modimpl.module.module_type == scu.ModuleType.MALUS else ""} +

+
+ + + """) + + # 2ieme ligne: Semestre, Coef + H.append("""""") + # 3ieme ligne: Formation + H.append( + """""" % F + ) + # Ligne: Inscrits + H.append( + """") + # Ligne: règle de calcul + has_expression = sco_compute_moy.moduleimpl_has_expression(M) + if has_expression: + H.append( + '") + else: + H.append( + '") + H.append( + '
Responsable: + {module_resp.get_nomcomplet()} + ({module_resp.user_name}) + """, + ] + try: + sco_moduleimpl.can_change_module_resp(moduleimpl_id) + H.append( + """modifier""" + % moduleimpl_id + ) + except: + pass + H.append("""""") + H.append( + ", ".join([sco_users.user_info(m["ens_id"])["nomprenom"] for m in M["ens"]]) + ) + H.append("""""") + try: + sco_moduleimpl.can_change_ens(moduleimpl_id) + H.append( + """modifier les enseignants""" + % moduleimpl_id + ) + except: + pass + H.append("""
""") + if sem["semestre_id"] >= 0: + H.append("""Semestre: %s""" % sem["semestre_id"]) + else: + H.append("""""") + if sem_locked: + H.append(scu.icontag("lock32_img", title="verrouillé")) + H.append("""""") + if modimpl.module.is_apc(): + H.append(_ue_coefs_html(modimpl.module.ue_coefs_list())) + else: + H.append( + f"""Coef. dans le semestre: { + "non défini" if modimpl.module.coefficient is None else modimpl.module.coefficient + }""" + ) + H.append("""
Formation: %(titre)s
Inscrits: %d étudiants""" + % len(mod_inscrits) + ) + if current_user.has_permission(Permission.ScoEtudInscrit): + H.append( + """modifier""" + % M["moduleimpl_id"] + ) + H.append("
Règle de calcul: moyenne=%s' + % M["computation_expr"] + ) + H.append("""inutilisée dans cette version de ScoDoc""") + if sco_moduleimpl.can_change_ens(moduleimpl_id, raise_exc=False): + H.append( + f""" supprimer""" + ) + H.append("""""") + H.append("
' # règle de calcul standard' + ) + # if sco_moduleimpl.can_change_ens(moduleimpl_id, raise_exc=False): + # H.append( + # f' (changer)' + # ) + H.append("
Absences dans ce module' + % moduleimpl_id + ) + # Adapté à partir d'une suggestion de DS (Le Havre) + # Liens saisies absences seulement si permission et date courante dans le semestre + if current_user.has_permission( + Permission.ScoAbsChange + ) and sco_formsemestre.sem_est_courant(sem): + datelundi = sco_abs.ddmmyyyy(time.strftime("%d/%m/%Y")).prev_monday() + group_id = sco_groups.get_default_group(formsemestre_id) + H.append( + f""" + + Saisie Absences hebdo. + """ + ) + + H.append("
") + # + if not modimpl.check_apc_conformity(): + H.append( + """
Les poids des évaluations de ce module ne sont + pas encore conformes au PN. + Ses notes ne peuvent pas être prises en compte dans les moyennes d'UE. +
""" + ) + # + if has_expression and nt.expr_diagnostics: + H.append(sco_formsemestre_status.html_expr_diagnostic(nt.expr_diagnostics)) + # + if formsemestre_has_decisions(formsemestre_id): + H.append( + """
  • Décisions de jury saisies: seul le responsable du semestre peut saisir des notes (il devra modifier les décisions de jury).
""" + ) + # + H.append( + """

%d évaluations : + +""" + % (len(mod_evals), moduleimpl_id) + ) + # + # Liste les noms de partitions + partitions = sco_groups.get_partitions_list(sem["formsemestre_id"]) + H.append( + """Afficher les groupes de  +     +Voir toutes les notes + +
+

+""" + % M + ) + + # -------- Tableau des evaluations + top_table_links = "" + if can_edit_evals: + top_table_links = f"""Créer nouvelle évaluation + """ + if mod_evals: + top_table_links += f""" + Trier par date + """ + if mod_evals: + H.append( + '" + ) + H.append("""""") + eval_index = len(mod_evals) - 1 + first_eval = True + for eval_dict in mod_evals: + H.append( + _ligne_evaluation( + modimpl, + eval_dict, + first_eval=first_eval, + partition_id=partition_id, + arrow_down=arrow_down, + arrow_none=arrow_none, + arrow_up=arrow_up, + can_edit_evals=can_edit_evals, + can_edit_notes=can_edit_notes, + eval_index=eval_index, + has_expression=has_expression, + nb_evals=len(mod_evals), + ) + ) + eval_index -= 1 + first_eval = False + # + H.append(""" +
""") + if sem_locked: + H.append(f"""{scu.icontag("lock32_img")} semestre verrouillé""") + elif can_edit_evals: + H.append(top_table_links) + + H.append( + f"""
+ +
+ + +
+

Légende

+
    +
  • {scu.icontag("edit_img")} : modifie description de l'évaluation + (date, heure, coefficient, ...) +
  • +
  • {scu.icontag("notes_img")} : saisie des notes
  • +
  • {scu.icontag("delete_img")} : indique qu'il n'y a aucune note + entrée (cliquer pour supprimer cette évaluation) +
  • +
  • {scu.icontag("status_orange_img")} : indique qu'il manque + quelques notes dans cette évaluation +
  • +
  • {scu.icontag("status_green_img")} : toutes les notes sont + entrées (cliquer pour les afficher) +
  • +
  • {scu.icontag("status_visible_img")} : indique que cette évaluation + sera mentionnée dans les bulletins au format "intermédiaire" +
  • +
+ +

Rappel : seules les notes des évaluations complètement saisies + (affichées en vert) apparaissent dans les bulletins. +

+ """ + ) + H.append(html_sco_header.sco_footer()) + return "".join(H) + + +def _ligne_evaluation( + modimpl: ModuleImpl, + eval_dict: dict, + first_eval: bool = True, + partition_id=None, + arrow_down=None, + arrow_none=None, + arrow_up=None, + can_edit_evals: bool = False, + can_edit_notes: bool = False, + eval_index: int = 0, + has_expression: bool = False, + nb_evals: int = 0, +) -> str: + """Ligne décrivant une évaluation dans le tableau de bord moduleimpl.""" + H = [] + # TODO unifier pour ne plus utiliser eval_dict + evaluation: Evaluation = Evaluation.query.get(eval_dict["evaluation_id"]) + etat = sco_evaluations.do_evaluation_etat( + evaluation.id, + partition_id=partition_id, + select_first_partition=True, + ) + if evaluation.evaluation_type in ( + scu.EVALUATION_RATTRAPAGE, + scu.EVALUATION_SESSION2, + ): + tr_class = "mievr mievr_rattr" + else: + tr_class = "mievr" + tr_class_1 = "mievr" + if not first_eval: + H.append(""" """) + tr_class_1 += " mievr_spaced" + H.append(f"""""") + if evaluation.jour: + H.append( + f"""Le {evaluation.jour.strftime("%d/%m/%Y")} {evaluation.descr_heure()}""" + ) + else: + H.append( + f"""Évaluation sans date""" + ) + H.append(f"    {evaluation.description or ''}") + if evaluation.evaluation_type == scu.EVALUATION_RATTRAPAGE: + H.append( + """rattrapage""" + ) + elif evaluation.evaluation_type == scu.EVALUATION_SESSION2: + H.append( + """session 2""" + ) + # Avertissement si coefs/poids nuls + if ( + evaluation.coefficient * sum(evaluation.get_ue_poids_dict().values()) + < scu.NOTES_PRECISION + ): + H.append("""coef. nul !""") + # + if etat["last_modif"]: + H.append( + f"""(dernière modif le { + etat["last_modif"].strftime("%d/%m/%Y à %Hh%M")})""" + ) + # + H.append('') + if has_expression or True: + H.append( + f"""{ + eval_index:2}""" + ) + # Fleches: + H.append('') + if eval_index != (nb_evals - 1) and can_edit_evals: + H.append( + '%s' + % (evaluation.id, arrow_up) + ) + else: + H.append(arrow_none) + if (eval_index > 0) and can_edit_evals: + H.append( + '%s' + % (evaluation.id, arrow_down) + ) + else: + H.append(arrow_none) + + H.append( + f""" + + +   + Durée + Coef. + Notes + Abs + N + Moyenne """ + ) + + if etat["evalcomplete"]: + etat_txt = """(prise en compte)""" + etat_descr = "notes utilisées dans les moyennes" + elif eval_dict["publish_incomplete"]: + etat_txt = """(prise en compte immédiate)""" + etat_descr = ( + "il manque des notes, mais la prise en compte immédiate a été demandée" + ) + elif etat["nb_notes"] != 0: + etat_txt = "(non prise en compte)" + etat_descr = "il manque des notes" + else: + etat_txt = "" + if can_edit_evals and etat_txt: + etat_txt = f"""{etat_txt}""" + + H.append( + f"""{etat_txt} + + """ + ) + if can_edit_evals: + H.append( + f"""{scu.icontag("edit_img", alt="modifier", title="Modifier informations")}""" + ) + if can_edit_notes: + H.append( + f"""{scu.icontag("notes_img", alt="saisie notes", title="Saisie des notes")}""" + ) + if etat["nb_notes"] == 0: + if can_edit_evals: + H.append( + f"""""" + ) + H.append(scu.icontag("delete_img", alt="supprimer", title="Supprimer")) + if can_edit_evals: + H.append("""""") + elif etat["evalcomplete"]: + H.append( + f"""{scu.icontag("status_green_img", title="ok")}""" + ) + else: + if etat["evalattente"]: + H.append( + f"""{scu.icontag( + "status_greenorange_img", + file_format="gif", + title="notes en attente", + )}""" + ) + else: + H.append( + f"""{scu.icontag("status_orange_img", title="il manque des notes")}""" + ) + # + if eval_dict["visibulletin"]: + H.append( + scu.icontag( + "status_visible_img", title="visible dans bulletins intermédiaires" + ) + ) + else: + H.append(" ") + H.append('') + if can_edit_notes: + H.append( + moduleimpl_evaluation_menu( + evaluation.id, + nbnotes=etat["nb_notes"], + ) + ) + # + H.append( + """ +%s%s""" + % (eval_dict["duree"], "%g" % eval_dict["coefficient"]) + ) + H.append( + """%(nb_notes)s / %(nb_inscrits)s +%(nb_abs)s +%(nb_neutre)s +""" + % etat + ) + if etat["moy"]: + H.append("%s / %g" % (etat["moy"], eval_dict["note_max"])) + H.append( + f"""  (afficher)""" + ) + else: + H.append( + f"""saisir notes + """ + ) + H.append("""""") + # + if etat["nb_notes"] == 0: + H.append(f"""""") + if modimpl.module.is_apc(): + H.append( + f"""{ + evaluation.get_ue_poids_str()}""" + ) + else: + H.append('') + H.append("""""") + else: # il y a deja des notes saisies + gr_moyennes = etat["gr_moyennes"] + first_group = True + for gr_moyenne in gr_moyennes: + H.append(f""" """) + if first_group and modimpl.module.is_apc(): + H.append( + f"""{ + evaluation.get_ue_poids_str()}""" + ) + else: + H.append("""""") + first_group = False + if gr_moyenne["group_name"] is None: + name = "Tous" # tous + else: + name = f"""Groupe {gr_moyenne["group_name"]}""" + H.append(f"""{name}  """) + if gr_moyenne["gr_nb_notes"] > 0: + H.append( + f"""{gr_moyenne["gr_moy"]}  ({gr_moyenne["gr_nb_notes"]} notes""" + ) + if gr_moyenne["gr_nb_att"] > 0: + H.append( + f""", {gr_moyenne["gr_nb_att"]} en attente""" + ) + H.append(""")""") + if gr_moyenne["group_id"] in etat["gr_incomplets"]: + H.append("""[""") + if can_edit_notes: + H.append( + f"""incomplet]""" + ) + else: + H.append("""incomplet]""") + else: + H.append("""  """) + if can_edit_notes: + H.append( + f"""""") + H.append("") + H.append("""""") + return "\n".join(H) diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index ce001b54..0d62e3eb 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -1862,6 +1862,14 @@ a.mievr_evalnodate:hover { text-decoration: underline; } +span.eval_warning_coef { + color: red; + margin: 2px; + padding-left: 3px; + padding-right: 3px; + background-color: rgb(255, 225, 0); +} + span.evalindex_cont { float: right; }