diff --git a/app/__init__.py b/app/__init__.py
index 41875f33..0943a91f 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -357,7 +357,7 @@ def sco_db_insert_constants():
current_app.logger.info("Init Sco db")
# Modalités:
- models.NotesFormModalite.insert_modalites()
+ models.FormationModalite.insert_modalites()
def initialize_scodoc_database(erase=False, create_all=False):
diff --git a/app/comp/moy_ue.py b/app/comp/moy_ue.py
new file mode 100644
index 00000000..b2eb29e0
--- /dev/null
+++ b/app/comp/moy_ue.py
@@ -0,0 +1,54 @@
+# -*- mode: python -*-
+# -*- coding: utf-8 -*-
+
+##############################################################################
+#
+# Gestion scolarite IUT
+#
+# Copyright (c) 1999 - 2021 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
+#
+##############################################################################
+
+"""Fonctions de calcul des moyennes d'UE
+"""
+import numpy as np
+import pandas as pd
+
+from app import db
+from app import models
+
+
+def df_load_ue_coefs(formation_id):
+ """Load coefs of all modules in formation and returns a DataFrame
+ rows = modules, columns = UE, value = coef.
+ Unspecified coefs (not defined in db) are set to zero.
+ """
+ ues = models.UniteEns.query.filter_by(formation_id=formation_id).all()
+ modules = models.Module.query.filter_by(formation_id=formation_id).all()
+ ue_ids = [ue.id for ue in ues]
+ module_ids = [module.id for module in modules]
+ df = pd.DataFrame(columns=ue_ids, index=module_ids, dtype=float)
+ for mod_coef in (
+ db.session.query(models.ModuleUECoef)
+ .filter(models.UniteEns.formation_id == formation_id)
+ .filter(models.ModuleUECoef.ue_id == models.UniteEns.id)
+ ):
+ df[mod_coef.ue_id][mod_coef.module_id] = mod_coef.coef
+ df.fillna(value=0, inplace=True)
+ return df
diff --git a/app/models/__init__.py b/app/models/__init__.py
index 3d164fe4..100b0a93 100644
--- a/app/models/__init__.py
+++ b/app/models/__init__.py
@@ -31,26 +31,27 @@ from app.models.etudiants import (
)
from app.models.events import Scolog, ScolarNews
from app.models.formations import (
- NotesFormation,
- NotesUE,
- NotesMatiere,
- NotesModule,
+ Formation,
+ UniteEns,
+ Matiere,
+ Module,
+ ModuleUECoef,
NotesTag,
notes_modules_tags,
)
from app.models.formsemestre import (
FormSemestre,
- NotesFormsemestreEtape,
- NotesFormModalite,
- NotesFormsemestreUECoef,
- NotesFormsemestreUEComputationExpr,
- NotesFormsemestreCustomMenu,
- NotesFormsemestreInscription,
+ FormsemestreEtape,
+ FormationModalite,
+ FormsemestreUECoef,
+ FormsemestreUEComputationExpr,
+ FormsemestreCustomMenu,
+ FormsemestreInscription,
notes_formsemestre_responsables,
- NotesModuleImpl,
+ ModuleImpl,
notes_modules_enseignants,
- NotesModuleImplInscription,
- NotesEvaluation,
+ ModuleImplInscription,
+ Evaluation,
EvaluationUEPoids,
NotesSemSet,
notes_semset_formsemestre,
@@ -61,7 +62,7 @@ from app.models.notes import (
ScolarEvent,
ScolarFormsemestreValidation,
ScolarAutorisationInscription,
- NotesAppreciations,
+ BulAppreciations,
NotesNotes,
NotesNotesLog,
)
diff --git a/app/models/but_pn.py b/app/models/but_pn.py
index 75f83a35..1e142d23 100644
--- a/app/models/but_pn.py
+++ b/app/models/but_pn.py
@@ -22,7 +22,7 @@ class AppCrit(db.Model):
titre = db.Column(db.Text(), info={"label": "Titre"})
modules = db.relationship(
- "NotesModule", secondary=Modules_ACs, lazy="dynamic", backref="acs"
+ "Module", secondary=Modules_ACs, lazy="dynamic", backref="acs"
)
def to_dict(self):
diff --git a/app/models/departements.py b/app/models/departements.py
index 36aa8d4c..aa9c1006 100644
--- a/app/models/departements.py
+++ b/app/models/departements.py
@@ -21,9 +21,7 @@ class Departement(db.Model):
entreprises = db.relationship("Entreprise", lazy="dynamic", backref="departement")
etudiants = db.relationship("Identite", lazy="dynamic", backref="departement")
- formations = db.relationship(
- "NotesFormation", lazy="dynamic", backref="departement"
- )
+ formations = db.relationship("Formation", lazy="dynamic", backref="departement")
formsemestres = db.relationship(
"FormSemestre", lazy="dynamic", backref="departement"
)
diff --git a/app/models/formations.py b/app/models/formations.py
index 046a3e73..fd1f0189 100644
--- a/app/models/formations.py
+++ b/app/models/formations.py
@@ -5,10 +5,11 @@ from typing import Any
from app import db
from app.models import APO_CODE_STR_LEN
from app.models import SHORT_STR_LEN
+from app.scodoc import sco_utils as scu
from app.scodoc.sco_utils import ModuleType
-class NotesFormation(db.Model):
+class Formation(db.Model):
"""Programme pédagogique d'une formation"""
__tablename__ = "notes_formations"
@@ -31,16 +32,16 @@ class NotesFormation(db.Model):
type_parcours = db.Column(db.Integer, default=0, server_default="0")
code_specialite = db.Column(db.String(SHORT_STR_LEN))
- ues = db.relationship("NotesUE", backref="formation", lazy="dynamic")
+ ues = db.relationship("UniteEns", backref="formation", lazy="dynamic")
formsemestres = db.relationship("FormSemestre", lazy="dynamic", backref="formation")
- ues = db.relationship("NotesUE", lazy="dynamic", backref="formation")
+ ues = db.relationship("UniteEns", lazy="dynamic", backref="formation")
def __repr__(self):
return f"<{self.__class__.__name__}(id={self.id}, dept_id={self.dept_id}, acronyme='{self.acronyme}')>"
-class NotesUE(db.Model):
- """Unité d'Enseignement"""
+class UniteEns(db.Model):
+ """Unité d'Enseignement (UE)"""
__tablename__ = "notes_ue"
@@ -68,14 +69,14 @@ class NotesUE(db.Model):
coefficient = db.Column(db.Float)
# relations
- matieres = db.relationship("NotesMatiere", lazy="dynamic", backref="ue")
- modules = db.relationship("NotesModule", lazy="dynamic", backref="ue")
+ matieres = db.relationship("Matiere", lazy="dynamic", backref="ue")
+ modules = db.relationship("Module", lazy="dynamic", backref="ue")
def __repr__(self):
return f"<{self.__class__.__name__}(id={self.id}, formation_id={self.formation_id}, acronyme='{self.acronyme}')>"
-class NotesMatiere(db.Model):
+class Matiere(db.Model):
"""Matières: regroupe les modules d'une UE
La matière a peu d'utilité en dehors de la présentation des modules
d'une UE.
@@ -90,10 +91,10 @@ class NotesMatiere(db.Model):
titre = db.Column(db.Text())
numero = db.Column(db.Integer) # ordre de présentation
- modules = db.relationship("NotesModule", lazy="dynamic", backref="matiere")
+ modules = db.relationship("Module", lazy="dynamic", backref="matiere")
-class NotesModule(db.Model):
+class Module(db.Model):
"""Module"""
__tablename__ = "notes_modules"
@@ -107,7 +108,7 @@ class NotesModule(db.Model):
heures_cours = db.Column(db.Float)
heures_td = db.Column(db.Float)
heures_tp = db.Column(db.Float)
- coefficient = db.Column(db.Float) # coef PPN
+ coefficient = db.Column(db.Float) # coef PPN (sauf en APC)
ects = db.Column(db.Float) # Crédits ECTS
ue_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), index=True)
formation_id = db.Column(db.Integer, db.ForeignKey("notes_formations.id"))
@@ -120,13 +121,75 @@ class NotesModule(db.Model):
# Type: ModuleType: DEFAULT, MALUS, RESSOURCE, MODULE_SAE (enum)
module_type = db.Column(db.Integer)
# Relations:
- modimpls = db.relationship("NotesModuleImpl", backref="module", lazy="dynamic")
+ modimpls = db.relationship("ModuleImpl", backref="module", lazy="dynamic")
+ ues_apc = db.relationship("UniteEns", secondary="module_ue_coef", viewonly=True)
+
+ def __init__(self, **kwargs):
+ self.ue_coefs = []
+ super(Module, self).__init__(**kwargs)
def __repr__(self):
return (
f"
-Les modules sont décrits dans le programme pédagogique. Un module est pour ce
-logiciel l'unité pédagogique élémentaire. On va lui associer une note
-à travers des évaluations. Un module possède un enseignant responsable
-(typiquement celui qui dispense le cours magistral). On peut associer
-au module une liste d'enseignants (typiquement les chargés de TD).
-Tous ces enseignants, plus le responsable du semestre, pourront
-saisir et modifier les notes de ce module.
- "
+ + "Modification évaluation impossible pour %s"
+ % current_user.get_nomplogin()
+ + " Module : %s UE : %(acronyme)s
-Cette note (moyenne de module) sera utilisée pour calculer la moyenne
-générale (et la moyenne de l'UE à laquelle appartient le module). Pour
-cela, on utilisera le coefficient associé au module.
-Création d'un module dans la matière %(titre)s""" % M,
+ """
Création d'un module dans la matière %(titre)s""" % matiere,
""" (UE %(acronyme)s)
""" % UE,
- _MODULE_HELP,
+ render_template("scodoc/help/modules.html", is_apc=is_apc),
]
- # cherche le numero adequat (pour placer le module en fin de liste)
- Mods = module_list(args={"matiere_id": matiere_id})
- if Mods:
- default_num = max([m["numero"] for m in Mods]) + 10
+ # cherche le numero adéquat (pour placer le module en fin de liste)
+ modules = module_list(args={"matiere_id": matiere_id})
+ if modules:
+ default_num = max([m["numero"] for m in modules]) + 10
else:
default_num = 10
tf = TrivialFormulator(
@@ -153,7 +142,7 @@ def module_create(matiere_id=None):
"size": 10,
"explanation": "code du module (doit être unique dans la formation)",
"allow_null": False,
- "validator": lambda val, field, formation_id=Fo[
+ "validator": lambda val, field, formation_id=formation[
"formation_id"
]: check_module_code_unicity(val, field, formation_id),
},
@@ -166,8 +155,8 @@ def module_create(matiere_id=None):
"input_type": "menu",
"title": "Type",
"explanation": "",
- "labels": ("Standard", "Malus"),
- "allowed_values": (str(scu.MODULE_STANDARD), str(scu.MODULE_MALUS)),
+ "labels": [x.name.capitalize() for x in scu.ModuleType],
+ "allowed_values": [str(int(x)) for x in scu.ModuleType],
},
),
(
@@ -201,8 +190,8 @@ def module_create(matiere_id=None):
),
# ('ects', { 'size' : 4, 'type' : 'float', 'title' : 'ECTS', 'explanation' : 'nombre de crédits ECTS (inutilisés: les crédits sont associés aux UE)' }),
("formation_id", {"default": UE["formation_id"], "input_type": "hidden"}),
- ("ue_id", {"default": M["ue_id"], "input_type": "hidden"}),
- ("matiere_id", {"default": M["matiere_id"], "input_type": "hidden"}),
+ ("ue_id", {"default": matiere["ue_id"], "input_type": "hidden"}),
+ ("matiere_id", {"default": matiere["matiere_id"], "input_type": "hidden"}),
(
"semestre_id",
{
@@ -350,35 +339,38 @@ def module_edit(module_id=None):
if not module_id:
raise ScoValueError("invalid module !")
- Mod = module_list(args={"module_id": module_id})
- if not Mod:
+ modules = module_list(args={"module_id": module_id})
+ if not modules:
raise ScoValueError("invalid module !")
- Mod = Mod[0]
+ module = modules[0]
unlocked = not module_is_locked(module_id)
- Fo = sco_formations.formation_list(args={"formation_id": Mod["formation_id"]})[0]
- parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"])
- M = ndb.SimpleDictFetch(
+ formation = sco_formations.formation_list(
+ args={"formation_id": module["formation_id"]}
+ )[0]
+ parcours = sco_codes_parcours.get_parcours_from_code(formation["type_parcours"])
+ is_apc = parcours.APC_SAE
+ ues_matieres = ndb.SimpleDictFetch(
"""SELECT ue.acronyme, mat.*, mat.id AS matiere_id
FROM notes_matieres mat, notes_ue ue
WHERE mat.ue_id = ue.id
AND ue.formation_id = %(formation_id)s
ORDER BY ue.numero, mat.numero
""",
- {"formation_id": Mod["formation_id"]},
+ {"formation_id": module["formation_id"]},
)
- Mnames = ["%s / %s" % (x["acronyme"], x["titre"]) for x in M]
- Mids = ["%s!%s" % (x["ue_id"], x["matiere_id"]) for x in M]
- Mod["ue_matiere_id"] = "%s!%s" % (Mod["ue_id"], Mod["matiere_id"])
+ mat_names = ["%s / %s" % (x["acronyme"], x["titre"]) for x in ues_matieres]
+ ue_mat_ids = ["%s!%s" % (x["ue_id"], x["matiere_id"]) for x in ues_matieres]
+ module["ue_matiere_id"] = "%s!%s" % (module["ue_id"], module["matiere_id"])
semestres_indices = list(range(1, parcours.NB_SEM + 1))
dest_url = url_for(
"notes.ue_table",
scodoc_dept=g.scodoc_dept,
- formation_id=str(Mod["formation_id"]),
+ formation_id=str(module["formation_id"]),
)
H = [
html_sco_header.sco_header(
- page_title="Modification du module %(titre)s" % Mod,
+ page_title="Modification du module %(titre)s" % module,
cssstyles=["libjs/jQuery-tagEditor/jquery.tag-editor.css"],
javascripts=[
"libjs/jQuery-tagEditor/jquery.tag-editor.min.js",
@@ -386,9 +378,9 @@ def module_edit(module_id=None):
"js/module_tag_editor.js",
],
),
- """Modification du module %(titre)s""" % Mod,
- """ (formation %(acronyme)s, version %(version)s)
""" % Fo,
- _MODULE_HELP,
+ """Modification du module %(titre)s""" % module,
+ """ (formation %(acronyme)s, version %(version)s)
""" % formation,
+ render_template("scodoc/help/modules.html", is_apc=is_apc),
]
if not unlocked:
H.append(
@@ -405,7 +397,7 @@ def module_edit(module_id=None):
"size": 10,
"explanation": "code du module (doit être unique dans la formation)",
"allow_null": False,
- "validator": lambda val, field, formation_id=Mod[
+ "validator": lambda val, field, formation_id=module[
"formation_id"
]: check_module_code_unicity(
val, field, formation_id, module_id=module_id
@@ -465,8 +457,8 @@ def module_edit(module_id=None):
"input_type": "menu",
"title": "Matière",
"explanation": "un module appartient à une seule matière.",
- "labels": Mnames,
- "allowed_values": Mids,
+ "labels": mat_names,
+ "allowed_values": ue_mat_ids,
"enabled": unlocked,
},
),
@@ -503,7 +495,7 @@ def module_edit(module_id=None):
html_foot_markup="""Opération non autorisée
{action} en
+ {scu.MODULE_TYPE_NAMES[mod["module_type"]]} {mod_descr}
+ """
+ ]
+
+ heures = ["%02dh%02d" % (h, m) 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"] = []
+ vals = scu.get_request_args()
+ if vals.get("tf_submitted", False) and "visibulletinlist" not in vals:
+ vals["visibulletinlist"] = []
+ #
+ if is_apc: # BUT: poids vers les UE
+ for ue in sem_ues:
+ if edit:
+ existing_poids = models.EvaluationUEPoids.query.filter_by(
+ ue=ue, evaluation=evaluation
+ ).first()
+ else:
+ existing_poids = None
+ if existing_poids:
+ poids = existing_poids.poids
+ else:
+ poids = 1.0 # par defaut au départ
+ initvalues[f"poids_{ue.id}"] = poids
+ #
+ form = [
+ ("evaluation_id", {"default": evaluation_id, "input_type": "hidden"}),
+ ("formsemestre_id", {"default": formsemestre_id, "input_type": "hidden"}),
+ ("moduleimpl_id", {"default": moduleimpl_id, "input_type": "hidden"}),
+ # ('jour', { 'title' : 'Date (j/m/a)', 'size' : 12, 'explanation' : 'date de l\'examen, devoir ou contrôle' }),
+ (
+ "jour",
+ {
+ "input_type": "date",
+ "title": "Date",
+ "size": 12,
+ "explanation": "date de l'examen, devoir ou contrôle",
+ },
+ ),
+ (
+ "heure_debut",
+ {
+ "title": "Heure de début",
+ "explanation": "heure du début de l'épreuve",
+ "input_type": "menu",
+ "allowed_values": heures,
+ "labels": heures,
+ },
+ ),
+ (
+ "heure_fin",
+ {
+ "title": "Heure de fin",
+ "explanation": "heure de fin de l'épreuve",
+ "input_type": "menu",
+ "allowed_values": heures,
+ "labels": heures,
+ },
+ ),
+ ]
+ if is_malus: # pas de coefficient
+ form.append(("coefficient", {"input_type": "hidden", "default": "1."}))
+ elif not is_apc: # modules standard hors BUT
+ form.append(
+ (
+ "coefficient",
+ {
+ "size": 6,
+ "type": "float",
+ "explanation": "coef. dans le module (choisi librement par l'enseignant)",
+ "allow_null": False,
+ },
+ )
+ )
+ form += [
+ (
+ "note_max",
+ {
+ "size": 4,
+ "type": "float",
+ "title": "Notes de 0 à",
+ "explanation": "barème (note max actuelle: %s)" % min_note_max_str,
+ "allow_null": False,
+ "max_value": scu.NOTES_MAX,
+ "min_value": min_note_max,
+ },
+ ),
+ (
+ "description",
+ {
+ "size": 36,
+ "type": "text",
+ "explanation": 'type d\'évaluation, apparait sur le bulletins longs. Exemples: "contrôle court", "examen de TP", "examen final".',
+ },
+ ),
+ (
+ "visibulletinlist",
+ {
+ "input_type": "checkbox",
+ "allowed_values": ["X"],
+ "labels": [""],
+ "title": "Visible sur bulletins",
+ "explanation": "(pour les bulletins en version intermédiaire)",
+ },
+ ),
+ (
+ "publish_incomplete",
+ {
+ "input_type": "boolcheckbox",
+ "title": "Prise en compte immédiate",
+ "explanation": "notes utilisées même si incomplètes",
+ },
+ ),
+ (
+ "evaluation_type",
+ {
+ "input_type": "menu",
+ "title": "Modalité",
+ "allowed_values": (
+ scu.EVALUATION_NORMALE,
+ scu.EVALUATION_RATTRAPAGE,
+ scu.EVALUATION_SESSION2,
+ ),
+ "type": "int",
+ "labels": (
+ "Normale",
+ "Rattrapage (remplace si meilleure note)",
+ "Deuxième session (remplace toujours)",
+ ),
+ },
+ ),
+ ]
+ if is_apc: # ressources et SAÉs
+ form += [
+ (
+ "coefficient",
+ {
+ "size": 6,
+ "type": "float",
+ "explanation": "importance de l'évaluation (multiplie les poids ci-dessous)",
+ "allow_null": False,
+ },
+ ),
+ ]
+ # Liste des UE utilisées dans des modules de ce semestre:
+ for ue in sem_ues:
+ form.append(
+ (
+ f"poids_{ue.id}",
+ {
+ "title": f"Poids {ue.acronyme}",
+ "size": 2,
+ "type": "float",
+ "explanation": f"{ue.titre}",
+ "allow_null": False,
+ },
+ ),
+ )
+ tf = TrivialFormulator(
+ request.base_url,
+ vals,
+ form,
+ cancelbutton="Annuler",
+ submitlabel=submitlabel,
+ initvalues=initvalues,
+ readonly=False,
+ )
+
+ dest_url = "moduleimpl_status?moduleimpl_id=%s" % modimpl["moduleimpl_id"]
+ if tf[0] == 0:
+ head = html_sco_header.sco_header(page_title=page_title)
+ return (
+ head
+ + "\n".join(H)
+ + "\n"
+ + tf[1]
+ + render_template("scodoc/help/evaluations.html", is_apc=is_apc)
+ + html_sco_header.sco_footer()
+ )
+ elif tf[0] == -1:
+ return flask.redirect(dest_url)
+ else:
+ # form submission
+ if tf[2]["visibulletinlist"]:
+ tf[2]["visibulletin"] = True
+ else:
+ tf[2]["visibulletin"] = False
+ if edit:
+ sco_evaluation_db.do_evaluation_edit(tf[2])
+ else:
+ # creation d'une evaluation
+ evaluation_id = sco_evaluation_db.do_evaluation_create(**tf[2])
+ # Set poids
+ evaluation = models.Evaluation.query.get(evaluation_id)
+ for ue in sem_ues:
+ evaluation.set_ue_poids(ue, tf[2][f"poids_{ue.id}"])
+ db.session.add(evaluation)
+ db.session.commit()
+ return flask.redirect(dest_url)
diff --git a/app/scodoc/sco_evaluations.py b/app/scodoc/sco_evaluations.py
index 0fba0b3c..e650d695 100644
--- a/app/scodoc/sco_evaluations.py
+++ b/app/scodoc/sco_evaluations.py
@@ -29,9 +29,7 @@
"""
import datetime
import operator
-import pprint
import time
-import urllib
import flask
from flask import url_for
@@ -41,12 +39,13 @@ from flask import request
from app import log
import app.scodoc.sco_utils as scu
+from app.scodoc.sco_utils import ModuleType
import app.scodoc.notesdb as ndb
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
import sco_version
from app.scodoc.gen_tables import GenTable
-from app.scodoc.TrivialFormulator import TrivialFormulator
from app.scodoc import html_sco_header
+from app.scodoc import sco_evaluation_db
from app.scodoc import sco_abs
from app.scodoc import sco_cache
from app.scodoc import sco_edit_module
@@ -96,281 +95,6 @@ def ListMedian(L):
# --------------------------------------------------------------------
-_evaluationEditor = ndb.EditableTable(
- "notes_evaluation",
- "evaluation_id",
- (
- "evaluation_id",
- "moduleimpl_id",
- "jour",
- "heure_debut",
- "heure_fin",
- "description",
- "note_max",
- "coefficient",
- "visibulletin",
- "publish_incomplete",
- "evaluation_type",
- "numero",
- ),
- sortkey="numero desc, jour desc, heure_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 do_evaluation_list
- "heure_fin": ndb.TimetoISO8601, # converti par do_evaluation_list
- "visibulletin": bool,
- "publish_incomplete": bool,
- "evaluation_type": int,
- },
-)
-
-
-def do_evaluation_list(args, sortkey=None):
- """List evaluations, sorted by numero (or most recent date first).
-
- Ajoute les champs:
- 'duree' : '2h30'
- 'matin' : 1 (commence avant 12:00) ou 0
- 'apresmidi' : 1 (termine après 12:00) ou 0
- 'descrheure' : ' de 15h00 à 16h30'
- """
- cnx = ndb.GetDBConnexion()
- evals = _evaluationEditor.list(cnx, args, sortkey=sortkey)
- # calcule duree (chaine de car.) de chaque evaluation et ajoute jouriso, matin, apresmidi
- for e in evals:
- 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 heure_debut_dt < datetime.time(12, 00):
- e["matin"] = 1
- else:
- e["matin"] = 0
- if heure_fin_dt > datetime.time(12, 00):
- e["apresmidi"] = 1
- else:
- e["apresmidi"] = 0
-
- return evals
-
-
-def do_evaluation_list_in_formsemestre(formsemestre_id):
- "list evaluations in this formsemestre"
- mods = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
- evals = []
- for mod in mods:
- evals += do_evaluation_list(args={"moduleimpl_id": mod["moduleimpl_id"]})
- return evals
-
-
-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:
- M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
- sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
- d, m, y = [int(x) for x in sem["date_debut"].split("/")]
- date_debut = datetime.date(y, m, d)
- d, m, y = [int(x) for x in sem["date_fin"].split("/")]
- date_fin = datetime.date(y, m, d)
- # passe par ndb.DateDMYtoISO pour avoir date pivot
- y, m, d = [int(x) for x in ndb.DateDMYtoISO(jour).split("-")]
- jour = datetime.date(y, m, d)
- if (jour > date_fin) or (jour < date_debut):
- raise ScoValueError(
- "La date de l'évaluation (%s/%s/%s) n'est pas dans le semestre !"
- % (d, m, y)
- )
- 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 !")
-
-
-def do_evaluation_create(
- moduleimpl_id=None,
- jour=None,
- heure_debut=None,
- heure_fin=None,
- description=None,
- note_max=None,
- coefficient=None,
- visibulletin=None,
- publish_incomplete=None,
- evaluation_type=None,
- numero=None,
- **kw, # ceci pour absorber les arguments excedentaires de tf #sco8
-):
- """Create an evaluation"""
- if not sco_permissions_check.can_edit_evaluation(moduleimpl_id=moduleimpl_id):
- raise AccessDenied(
- "Modification évaluation impossible pour %s" % current_user.get_nomplogin()
- )
- args = locals()
- log("do_evaluation_create: args=" + str(args))
- _check_evaluation_args(args)
- # Check numeros
- module_evaluation_renumber(moduleimpl_id, only_if_unumbered=True)
- if not "numero" in args or args["numero"] is None:
- n = None
- # determine le numero avec la date
- # Liste des eval existantes triees par date, la plus ancienne en tete
- ModEvals = do_evaluation_list(
- args={"moduleimpl_id": moduleimpl_id},
- sortkey="jour asc, heure_debut asc",
- )
- if args["jour"]:
- next_eval = None
- t = (
- ndb.DateDMYtoISO(args["jour"], null_is_empty=True),
- ndb.TimetoISO8601(args["heure_debut"], null_is_empty=True),
- )
- for e in ModEvals:
- if (
- ndb.DateDMYtoISO(e["jour"], null_is_empty=True),
- ndb.TimetoISO8601(e["heure_debut"], null_is_empty=True),
- ) > t:
- next_eval = e
- break
- if next_eval:
- n = module_evaluation_insert_before(ModEvals, next_eval)
- else:
- n = None # a placer en fin
- if n is None: # pas de date ou en fin:
- if ModEvals:
- log(pprint.pformat(ModEvals[-1]))
- n = ModEvals[-1]["numero"] + 1
- else:
- n = 0 # the only one
- # log("creating with numero n=%d" % n)
- args["numero"] = n
-
- #
- cnx = ndb.GetDBConnexion()
- r = _evaluationEditor.create(cnx, args)
-
- # news
- M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
- mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
- mod["moduleimpl_id"] = M["moduleimpl_id"]
- mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
- sco_news.add(
- typ=sco_news.NEWS_NOTE,
- object=moduleimpl_id,
- text='Création d\'une évaluation dans %(titre)s' % mod,
- url=mod["url"],
- )
-
- return r
-
-
-def do_evaluation_edit(args):
- "edit an evaluation"
- evaluation_id = args["evaluation_id"]
- the_evals = do_evaluation_list({"evaluation_id": evaluation_id})
- if not the_evals:
- raise ValueError("evaluation inexistante !")
- moduleimpl_id = the_evals[0]["moduleimpl_id"]
- if not sco_permissions_check.can_edit_evaluation(moduleimpl_id=moduleimpl_id):
- raise AccessDenied(
- "Modification évaluation impossible pour %s" % current_user.get_nomplogin()
- )
- args["moduleimpl_id"] = moduleimpl_id
- _check_evaluation_args(args)
-
- cnx = ndb.GetDBConnexion()
- _evaluationEditor.edit(cnx, args)
- # inval cache pour ce semestre
- M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
- sco_cache.invalidate_formsemestre(formsemestre_id=M["formsemestre_id"])
-
-
-def do_evaluation_delete(evaluation_id):
- "delete evaluation"
- the_evals = do_evaluation_list({"evaluation_id": evaluation_id})
- if not the_evals:
- raise ValueError("evaluation inexistante !")
- moduleimpl_id = the_evals[0]["moduleimpl_id"]
- if not sco_permissions_check.can_edit_evaluation(moduleimpl_id=moduleimpl_id):
- raise AccessDenied(
- "Modification évaluation impossible pour %s" % current_user.get_nomplogin()
- )
- NotesDB = do_evaluation_get_all_notes(evaluation_id) # { etudid : value }
- notes = [x["value"] for x in NotesDB.values()]
- if notes:
- raise ScoValueError(
- "Impossible de supprimer cette évaluation: il reste des notes"
- )
-
- cnx = ndb.GetDBConnexion()
-
- _evaluationEditor.delete(cnx, evaluation_id)
- # inval cache pour ce semestre
- M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
- sco_cache.invalidate_formsemestre(formsemestre_id=M["formsemestre_id"])
- # news
- mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
- mod["moduleimpl_id"] = M["moduleimpl_id"]
- mod["url"] = (
- scu.NotesURL() + "/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
- )
- sco_news.add(
- typ=sco_news.NEWS_NOTE,
- object=moduleimpl_id,
- text='Suppression d\'une évaluation dans %(titre)s' % mod,
- url=mod["url"],
- )
def do_evaluation_etat(evaluation_id, partition_id=None, select_first_partition=False):
@@ -385,7 +109,9 @@ def do_evaluation_etat(evaluation_id, partition_id=None, select_first_partition=
nb_inscrits = len(
sco_groups.do_evaluation_listeetuds_groups(evaluation_id, getallstudents=True)
)
- NotesDB = do_evaluation_get_all_notes(evaluation_id) # { etudid : value }
+ NotesDB = sco_evaluation_db.do_evaluation_get_all_notes(
+ evaluation_id
+ ) # { etudid : value }
notes = [x["value"] for x in NotesDB.values()]
nb_abs = len([x for x in notes if x is None])
nb_neutre = len([x for x in notes if x == scu.NOTES_NEUTRALISE])
@@ -408,10 +134,10 @@ def do_evaluation_etat(evaluation_id, partition_id=None, select_first_partition=
else:
last_modif = None
# ---- Liste des groupes complets et incomplets
- E = do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
+ E = sco_evaluation_db.do_evaluation_list(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"] == scu.MODULE_MALUS # True si module de malus
+ is_malus = Mod["module_type"] == ModuleType.MALUS # True si module de malus
formsemestre_id = M["formsemestre_id"]
# Si partition_id is None, prend 'all' ou bien la premiere:
if partition_id is None:
@@ -611,46 +337,6 @@ def do_evaluation_list_in_sem(formsemestre_id, with_etat=True):
return res
-# ancien _notes_getall
-def do_evaluation_get_all_notes(
- evaluation_id, table="notes_notes", filter_suppressed=True, by_uid=None
-):
- """Toutes les notes pour une evaluation: { etudid : { 'value' : value, 'date' : date ... }}
- Attention: inclut aussi les notes des étudiants qui ne sont plus inscrits au module.
- """
- do_cache = (
- filter_suppressed and table == "notes_notes" and (by_uid is None)
- ) # pas de cache pour (rares) appels via undo_notes ou specifiant un enseignant
- if do_cache:
- r = sco_cache.EvaluationCache.get(evaluation_id)
- if r != None:
- return r
- cnx = ndb.GetDBConnexion()
- cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
- cond = " where evaluation_id=%(evaluation_id)s"
- if by_uid:
- cond += " and uid=%(by_uid)s"
-
- cursor.execute(
- "select * from " + table + cond,
- {"evaluation_id": evaluation_id, "by_uid": by_uid},
- )
- res = cursor.dictfetchall()
- d = {}
- if filter_suppressed:
- for x in res:
- if x["value"] != scu.NOTES_SUPPRESS:
- d[x["etudid"]] = x
- else:
- for x in res:
- d[x["etudid"]] = x
- if do_cache:
- status = sco_cache.EvaluationCache.set(evaluation_id, d)
- if not status:
- log(f"Warning: EvaluationCache.set: {evaluation_id}\t{status}")
- return d
-
-
def _eval_etat(evals):
"""evals: list of mappings (etats)
-> nb_eval_completes, nb_evals_en_cours,
@@ -818,10 +504,12 @@ def evaluation_date_first_completion(evaluation_id):
# ins = [i for i in insem if i["etudid"] in insmodset]
notes = list(
- do_evaluation_get_all_notes(evaluation_id, filter_suppressed=False).values()
+ sco_evaluation_db.do_evaluation_get_all_notes(
+ evaluation_id, filter_suppressed=False
+ ).values()
)
notes_log = list(
- do_evaluation_get_all_notes(
+ sco_evaluation_db.do_evaluation_get_all_notes(
evaluation_id, filter_suppressed=False, table="notes_notes_log"
).values()
)
@@ -854,7 +542,7 @@ def formsemestre_evaluations_delai_correction(formsemestre_id, format="html"):
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"] == scu.MODULE_MALUS
+ Mod["module_type"] == ModuleType.MALUS
):
continue
e["date_first_complete"] = evaluation_date_first_completion(e["evaluation_id"])
@@ -917,112 +605,6 @@ def formsemestre_evaluations_delai_correction(formsemestre_id, format="html"):
return tab.make_page(format=format)
-def module_evaluation_insert_before(ModEvals, next_eval):
- """Renumber evals such that an evaluation with can be inserted before next_eval
- Returns numero suitable for the inserted evaluation
- """
- if next_eval:
- n = next_eval["numero"]
- if not n:
- log("renumbering old evals")
- module_evaluation_renumber(next_eval["moduleimpl_id"])
- next_eval = do_evaluation_list(
- args={"evaluation_id": next_eval["evaluation_id"]}
- )[0]
- n = next_eval["numero"]
- else:
- n = 1
- # log('inserting at position numero %s' % n )
- # all numeros >= n are incremented
- for e in ModEvals:
- if e["numero"] >= n:
- e["numero"] += 1
- # log('incrementing %s to %s' % (e['evaluation_id'], e['numero']))
- do_evaluation_edit(e)
-
- return n
-
-
-def module_evaluation_move(evaluation_id, after=0, redirect=1):
- """Move before/after previous one (decrement/increment numero)
- (published)
- """
- e = do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
- redirect = int(redirect)
- # access: can change eval ?
- if not sco_permissions_check.can_edit_evaluation(moduleimpl_id=e["moduleimpl_id"]):
- raise AccessDenied(
- "Modification évaluation impossible pour %s" % current_user.get_nomplogin()
- )
-
- module_evaluation_renumber(e["moduleimpl_id"], only_if_unumbered=True)
- e = do_evaluation_list(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"')
- ModEvals = do_evaluation_list({"moduleimpl_id": e["moduleimpl_id"]})
- # log('ModEvals=%s' % [ x['evaluation_id'] for x in ModEvals] )
- if len(ModEvals) > 1:
- idx = [p["evaluation_id"] for p in ModEvals].index(evaluation_id)
- neigh = None # object to swap with
- if after == 0 and idx > 0:
- neigh = ModEvals[idx - 1]
- elif after == 1 and idx < len(ModEvals) - 1:
- neigh = ModEvals[idx + 1]
- if neigh: #
- # swap numero with neighbor
- e["numero"], neigh["numero"] = neigh["numero"], e["numero"]
- do_evaluation_edit(e)
- do_evaluation_edit(neigh)
- # redirect to moduleimpl page:
- if redirect:
- return flask.redirect(
- url_for(
- "notes.moduleimpl_status",
- scodoc_dept=g.scodoc_dept,
- moduleimpl_id=e["moduleimpl_id"],
- )
- )
-
-
-def module_evaluation_renumber(moduleimpl_id, only_if_unumbered=False, redirect=0):
- """Renumber evaluations in this module, according to their date. (numero=0: oldest one)
- Needed because previous versions of ScoDoc did not have eval numeros
- Note: existing numeros are ignored
- """
- redirect = int(redirect)
- # log('module_evaluation_renumber( moduleimpl_id=%s )' % moduleimpl_id )
- # List sorted according to date/heure, ignoring numeros:
- # (note that we place evaluations with NULL date at the end)
- ModEvals = do_evaluation_list(
- args={"moduleimpl_id": moduleimpl_id},
- sortkey="jour asc, heure_debut asc",
- )
-
- all_numbered = False not in [x["numero"] > 0 for x in ModEvals]
- if all_numbered and only_if_unumbered:
- return # all ok
-
- # log('module_evaluation_renumber')
- # Reset all numeros:
- i = 1
- for e in ModEvals:
- e["numero"] = i
- do_evaluation_edit(e)
- i += 1
-
- # If requested, redirect to moduleimpl page:
- if redirect:
- return flask.redirect(
- url_for(
- "notes.moduleimpl_status",
- scodoc_dept=g.scodoc_dept,
- moduleimpl_id=moduleimpl_id,
- )
- )
-
-
# -------------- VIEWS
def evaluation_describe(evaluation_id="", edit_in_place=True):
"""HTML description of evaluation, for page headers
@@ -1030,7 +612,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True):
"""
from app.scodoc import sco_saisie_notes
- E = do_evaluation_list({"evaluation_id": evaluation_id})[0]
+ E = sco_evaluation_db.do_evaluation_list({"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]
@@ -1054,13 +636,13 @@ def evaluation_describe(evaluation_id="", edit_in_place=True):
etit = E["description"] or ""
if etit:
etit = ' "' + etit + '"'
- if Mod["module_type"] == scu.MODULE_MALUS:
+ if Mod["module_type"] == ModuleType.MALUS:
etit += ' (points de malus)'
H = [
'Evaluation%s
" - + "Modification évaluation impossible pour %s" - % current_user.get_nomplogin() - + "
" - + '' - % (moduleimpl_id,) - + html_sco_header.sco_footer() - ) - if readonly: - edit = True # montre les donnees existantes - if not edit: - # creation nouvel - if moduleimpl_id is None: - raise ValueError("missing moduleimpl_id parameter") - initvalues = { - "note_max": 20, - "jour": time.strftime("%d/%m/%Y", time.localtime()), - "publish_incomplete": is_malus, - } - submitlabel = "Créer cette évaluation" - action = "Création d'une é" - link = "" - else: - # edition donnees existantes - # setup form init values - if evaluation_id is None: - raise ValueError("missing evaluation_id parameter") - initvalues = the_eval - moduleimpl_id = initvalues["moduleimpl_id"] - submitlabel = "Modifier les données" - if readonly: - action = "E" - link = ( - 'voir toutes les notes du module' - % M["moduleimpl_id"] - ) - else: - action = "Modification d'une é" - link = "" - # Note maximale actuelle dans cette eval ? - etat = do_evaluation_etat(evaluation_id) - if etat["maxi_num"] is not None: - min_note_max = max(scu.NOTES_PRECISION, etat["maxi_num"]) - else: - min_note_max = scu.NOTES_PRECISION - # - if min_note_max > scu.NOTES_PRECISION: - min_note_max_str = scu.fmt_note(min_note_max) - else: - min_note_max_str = "0" - # - Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] - # - help = """- Le coefficient d'une évaluation n'est utilisé que pour pondérer les évaluations au sein d'un module. - Il est fixé librement par l'enseignant pour refléter l'importance de ses différentes notes - (examens, projets, travaux pratiques...). Ce coefficient est utilisé pour calculer la note - moyenne de chaque étudiant dans ce module. -
- Ne pas confondre ce coefficient avec le coefficient du module, qui est lui fixé par le programme - pédagogique (le PPN pour les DUT) et pondère les moyennes de chaque module pour obtenir - les moyennes d'UE et la moyenne générale. -
- L'option Visible sur bulletins indique que la note sera reportée sur les bulletins - en version dite "intermédiaire" (dans cette version, on peut ne faire apparaitre que certaines - notes, en sus des moyennes de modules. Attention, cette option n'empêche pas la publication sur - les bulletins en version "longue" (la note est donc visible par les étudiants sur le portail). -
- Les modalités "rattrapage" et "deuxième session" définissent des évaluations prises en compte de - façon spéciale:
-- Dans ces deux cas, le coefficient est ignoré, et toutes les notes n'ont - pas besoin d'être rentrées. -
-- Par ailleurs, les évaluations des modules de type "malus" sont toujours spéciales: le coefficient n'est pas utilisé. - Les notes de malus sont toujours comprises entre -20 et 20. Les points sont soustraits à la moyenne - de l'UE à laquelle appartient le module malus (si la note est négative, la moyenne est donc augmentée). -
- """ - mod_descr = '%s %s %s' % ( - moduleimpl_id, - Mod["code"], - Mod["titre"], - link, - ) - if not readonly: - H = ["Attention: il y a %d évaluations dans ce semestre (sa suppression entrainera l'effacement définif des notes) !
""" @@ -1311,7 +1312,7 @@ def do_formsemestre_delete(formsemestre_id): mods = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id) for mod in mods: # evaluations - evals = sco_evaluations.do_evaluation_list( + evals = sco_evaluation_db.do_evaluation_list( args={"moduleimpl_id": mod["moduleimpl_id"]} ) for e in evals: diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index 04f17d5b..48af5949 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -36,6 +36,7 @@ from flask_login import current_user from app import log import app.scodoc.sco_utils as scu +from app.scodoc.sco_utils import ModuleType import app.scodoc.notesdb as ndb from app.scodoc.sco_permissions import Permission from app.scodoc.sco_exceptions import ScoValueError, ScoInvalidDateError @@ -50,6 +51,7 @@ from app.scodoc import sco_compute_moy from app.scodoc import sco_cache 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_formations from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre_edit @@ -459,7 +461,9 @@ def retreive_formsemestre_from_request() -> int: modimpl = modimpl[0] formsemestre_id = modimpl["formsemestre_id"] elif "evaluation_id" in args: - E = sco_evaluations.do_evaluation_list({"evaluation_id": args["evaluation_id"]}) + E = sco_evaluation_db.do_evaluation_list( + {"evaluation_id": args["evaluation_id"]} + ) if not E: return None # evaluation suppressed ? E = E[0] @@ -979,13 +983,22 @@ def formsemestre_status(formsemestre_id=None): # porté du DTML cnx = ndb.GetDBConnexion() sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True) - Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) - # inscrits = sco_formsemestre_inscriptions.do_formsemestre_inscription_list( - # args={"formsemestre_id": formsemestre_id} - # ) - prev_ue_id = None + modimpls = sco_moduleimpl.moduleimpl_withmodule_list( + formsemestre_id=formsemestre_id + ) + nt = sco_cache.NotesTableCache.get(formsemestre_id) + # Construit la liste de tous les enseignants de ce semestre: + mails_enseignants = set( + [sco_users.user_info(ens_id)["email"] for ens_id in sem["responsables"]] + ) + for modimpl in modimpls: + mails_enseignants.add(sco_users.user_info(modimpl["responsable_id"])["email"]) + mails_enseignants |= set( + [sco_users.user_info(m["ens_id"])["email"] for m in modimpl["ens"]] + ) can_edit = sco_formsemestre_edit.can_edit_sem(formsemestre_id, sem=sem) + use_ue_coefs = sco_preferences.get_preference("use_ue_coefs", formsemestre_id) H = [ html_sco_header.sco_header(page_title="Semestre %s" % sem["titreannee"]), @@ -995,158 +1008,69 @@ def formsemestre_status(formsemestre_id=None): ), """Tableau de bord: cliquez sur un module pour saisir des notes
""", ] - nt = sco_cache.NotesTableCache.get(formsemestre_id) + if nt.expr_diagnostics: H.append(html_expr_diagnostic(nt.expr_diagnostics)) - H.append( - """ --
Code | -Module | -Inscrits | -Responsable | -Evaluations | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
-%s -%s - | """ - % (acronyme, titre) - ) - expr = sco_compute_moy.get_ue_expression( - formsemestre_id, ue["ue_id"], cnx, html_quote=True - ) - - if can_edit: - H.append( - ' ' - % (formsemestre_id, ue["ue_id"]) - ) - H.append( - scu.icontag( - "formula", - title="Mode calcul moyenne d'UE", - style="vertical-align:middle", - ) - ) - if can_edit: - H.append("") - if expr: - H.append( - """ %s""" - % expr - ) - - H.append(" | |||||||||||||||||||
%s | ' - % (M["moduleimpl_id"], ModDescr, Mod["code"]) - ) - H.append( - '%s | ' - % (M["moduleimpl_id"], ModDescr, Mod["abbrev"] or Mod["titre"]) - ) - H.append('%s | ' % len(ModInscrits)) - H.append( - '%s | ' - % ( - M["moduleimpl_id"], - ModEns, - sco_users.user_info(M["responsable_id"])["prenomnom"], - ) - ) - - if Mod["module_type"] == scu.MODULE_STANDARD: - H.append('') - nb_evals = ( - etat["nb_evals_completes"] - + etat["nb_evals_en_cours"] - + etat["nb_evals_vides"] - ) - if nb_evals != 0: - H.append( - '%s prévues, %s ok' - % (M["moduleimpl_id"], nb_evals, etat["nb_evals_completes"]) - ) - if etat["nb_evals_en_cours"] > 0: - H.append( - ', %s en cours' - % (M["moduleimpl_id"], etat["nb_evals_en_cours"]) - ) - if etat["attente"]: - H.append( - ' [en attente]' - % M["moduleimpl_id"] - ) - elif Mod["module_type"] == scu.MODULE_MALUS: - nb_malus_notes = sum( - [ - e["etat"]["nb_notes"] - for e in nt.get_mod_evaluation_etat_list(M["moduleimpl_id"]) - ] - ) - H.append( - """ |
- malus (%d notes)
+ if nt.parcours.APC_SAE:
+ # BUT: tableau ressources puis SAE
+ ressources = [
+ m for m in modimpls if m["module"]["module_type"] == ModuleType.RESSOURCE
+ ]
+ saes = [m for m in modimpls if m["module"]["module_type"] == ModuleType.SAE]
+ autres = [
+ m
+ for m in modimpls
+ if m["module"]["module_type"] not in (ModuleType.RESSOURCE, ModuleType.SAE)
+ ]
+ H += [
"""
- % (M["moduleimpl_id"], nb_malus_notes)
- )
- else:
- raise ValueError("Invalid module_type") # a bug
+
+ """,
+ _TABLEAU_MODULES_HEAD,
+ f""" "]
+ else:
+ # formations classiques: groupe par UE
+ H += [
+ "
+ Ressources
+ |
+ SAÉs
+ |
+ Autres modules
+ | ", + _TABLEAU_MODULES_HEAD, + formsemestre_tableau_modules( + modimpls, + nt, + formsemestre_id, + can_edit=can_edit, + use_ue_coefs=use_ue_coefs, + ), + _TABLEAU_MODULES_FOOT, + " ", + ] - H.append(" |
utilise les coefficients d'UE pour calculer la moyenne générale.
@@ -1166,3 +1090,156 @@ def formsemestre_status(formsemestre_id=None): % (",".join(adrlist), len(adrlist)) ) return "".join(H) + html_sco_header.sco_footer() + + +_TABLEAU_MODULES_HEAD = """ +Code | +Module | +Inscrits | +Responsable | +Évaluations | +
---|
Responsable: | """,
- module_resp.get_nomcomplet(), # sco_users.user_info(M["responsable_id"])["nomprenom"],
- f"""({module_resp.user_name})""",
+ html_sco_header.sco_header(page_title=f"{mod_type_name} {Mod['titre']}"),
+ f"""{mod_type_name} + {Mod['code']} {Mod['titre']}+
+
|