forked from ScoDoc/ScoDoc
- Coef evaluations (modèles).
- Refactoring. - Changement des noms des classes (modèles) des formations. - Début intégration calculs BUT. - Requiert numpy et pandas.
This commit is contained in:
parent
d71b399c3d
commit
89e7250f4a
@ -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):
|
||||
|
54
app/comp/moy_ue.py
Normal file
54
app/comp/moy_ue.py
Normal file
@ -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
|
@ -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,
|
||||
)
|
||||
|
@ -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):
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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"<Module{ModuleType(self.module_type).name} id={self.id} code={self.code}>"
|
||||
)
|
||||
|
||||
def type_name(self):
|
||||
return scu.MODULE_TYPE_NAMES[self.module_type]
|
||||
|
||||
def set_ue_coef(self, ue, coef: float) -> None:
|
||||
"""Set coef module vers cette UE"""
|
||||
self.update_ue_coef_dict({ue.id: coef})
|
||||
|
||||
def set_ue_coef_dict(self, ue_coef_dict: dict) -> None:
|
||||
"""set coefs vers les UE (remplace existants)
|
||||
ue_coef_dict = { ue_id : coef }
|
||||
"""
|
||||
ue_coefs = []
|
||||
for ue_id, coef in ue_coef_dict.items():
|
||||
ue = UniteEns.query.get(ue_id)
|
||||
ue_coefs.append(ModuleUECoef(module=self, ue=ue, coef=coef))
|
||||
self.ue_coefs = ue_coefs
|
||||
|
||||
def update_ue_coef_dict(self, ue_coef_dict: dict):
|
||||
"""update coefs vers UE (ajoute aux existants)"""
|
||||
current = self.get_ue_coef_dict()
|
||||
current.update(ue_coef_dict)
|
||||
self.set_ue_coef_dict(current)
|
||||
|
||||
def get_ue_coef_dict(self):
|
||||
"""returns { ue_id : coef }"""
|
||||
return {p.ue.id: p.coef for p in self.ue_coefs}
|
||||
|
||||
def delete_ue_coef(self, ue):
|
||||
"""delete coef"""
|
||||
ue_coef = ModuleUECoef.query.get((self.id, ue.id))
|
||||
db.session.delete(ue_coef)
|
||||
|
||||
|
||||
class ModuleUECoef(db.Model):
|
||||
"""Coefficients des modules vers les UE (APC, BUT)
|
||||
En mode APC, ces coefs remplacent le coefficient "PPN" du module.
|
||||
"""
|
||||
|
||||
__tablename__ = "module_ue_coef"
|
||||
|
||||
module_id = db.Column(
|
||||
db.Integer, db.ForeignKey("notes_modules.id"), primary_key=True
|
||||
)
|
||||
ue_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), primary_key=True)
|
||||
coef = db.Column(
|
||||
db.Float,
|
||||
nullable=False,
|
||||
)
|
||||
module = db.relationship(
|
||||
Module,
|
||||
backref=db.backref("ue_coefs", cascade="all, delete-orphan"),
|
||||
)
|
||||
ue = db.relationship(
|
||||
UniteEns,
|
||||
backref=db.backref("module_ue_coefs", cascade="all, delete-orphan"),
|
||||
)
|
||||
|
||||
|
||||
class NotesTag(db.Model):
|
||||
"""Tag sur un module"""
|
||||
|
@ -8,7 +8,10 @@ from app import db
|
||||
from app.models import APO_CODE_STR_LEN
|
||||
from app.models import SHORT_STR_LEN
|
||||
from app.models import CODE_STR_LEN
|
||||
from app.models import NotesUE
|
||||
from app.models import UniteEns
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app.scodoc import sco_evaluation_db
|
||||
|
||||
|
||||
class FormSemestre(db.Model):
|
||||
@ -73,11 +76,9 @@ class FormSemestre(db.Model):
|
||||
|
||||
# Relations:
|
||||
etapes = db.relationship(
|
||||
"NotesFormsemestreEtape", cascade="all,delete", backref="formsemestre"
|
||||
)
|
||||
formsemestres = db.relationship(
|
||||
"NotesModuleImpl", backref="formsemestre", lazy="dynamic"
|
||||
"FormsemestreEtape", cascade="all,delete", backref="formsemestre"
|
||||
)
|
||||
modimpls = db.relationship("ModuleImpl", backref="formsemestre", lazy="dynamic")
|
||||
|
||||
# Ancien id ScoDoc7 pour les migrations de bases anciennes
|
||||
# ne pas utiliser après migrate_scodoc7_dept_archives
|
||||
@ -86,7 +87,7 @@ class FormSemestre(db.Model):
|
||||
def __init__(self, **kwargs):
|
||||
super(FormSemestre, self).__init__(**kwargs)
|
||||
if self.modalite is None:
|
||||
self.modalite = NotesFormModalite.DEFAULT_MODALITE
|
||||
self.modalite = FormationModalite.DEFAULT_MODALITE
|
||||
|
||||
|
||||
# Association id des utilisateurs responsables (aka directeurs des etudes) du semestre
|
||||
@ -101,7 +102,7 @@ notes_formsemestre_responsables = db.Table(
|
||||
)
|
||||
|
||||
|
||||
class NotesFormsemestreEtape(db.Model):
|
||||
class FormsemestreEtape(db.Model):
|
||||
"""Étape Apogée associées au semestre"""
|
||||
|
||||
__tablename__ = "notes_formsemestre_etapes"
|
||||
@ -113,7 +114,7 @@ class NotesFormsemestreEtape(db.Model):
|
||||
etape_apo = db.Column(db.String(APO_CODE_STR_LEN))
|
||||
|
||||
|
||||
class NotesFormModalite(db.Model):
|
||||
class FormationModalite(db.Model):
|
||||
"""Modalités de formation, utilisées pour la présentation
|
||||
(grouper les semestres, générer des codes, etc.)
|
||||
"""
|
||||
@ -140,7 +141,7 @@ class NotesFormModalite(db.Model):
|
||||
numero = 0
|
||||
try:
|
||||
for (code, titre) in (
|
||||
(NotesFormModalite.DEFAULT_MODALITE, "Formation Initiale"),
|
||||
(FormationModalite.DEFAULT_MODALITE, "Formation Initiale"),
|
||||
("FAP", "Apprentissage"),
|
||||
("FC", "Formation Continue"),
|
||||
("DEC", "Formation Décalées"),
|
||||
@ -151,9 +152,9 @@ class NotesFormModalite(db.Model):
|
||||
("EXT", "Extérieur"),
|
||||
("OTHER", "Autres formations"),
|
||||
):
|
||||
modalite = NotesFormModalite.query.filter_by(modalite=code).first()
|
||||
modalite = FormationModalite.query.filter_by(modalite=code).first()
|
||||
if modalite is None:
|
||||
modalite = NotesFormModalite(
|
||||
modalite = FormationModalite(
|
||||
modalite=code, titre=titre, numero=numero
|
||||
)
|
||||
db.session.add(modalite)
|
||||
@ -164,7 +165,7 @@ class NotesFormModalite(db.Model):
|
||||
raise
|
||||
|
||||
|
||||
class NotesFormsemestreUECoef(db.Model):
|
||||
class FormsemestreUECoef(db.Model):
|
||||
"""Coef des UE capitalisees arrivant dans ce semestre"""
|
||||
|
||||
__tablename__ = "notes_formsemestre_uecoef"
|
||||
@ -183,7 +184,7 @@ class NotesFormsemestreUECoef(db.Model):
|
||||
coefficient = db.Column(db.Float, nullable=False)
|
||||
|
||||
|
||||
class NotesFormsemestreUEComputationExpr(db.Model):
|
||||
class FormsemestreUEComputationExpr(db.Model):
|
||||
"""Formules utilisateurs pour calcul moyenne UE"""
|
||||
|
||||
__tablename__ = "notes_formsemestre_ue_computation_expr"
|
||||
@ -203,7 +204,7 @@ class NotesFormsemestreUEComputationExpr(db.Model):
|
||||
computation_expr = db.Column(db.Text())
|
||||
|
||||
|
||||
class NotesFormsemestreCustomMenu(db.Model):
|
||||
class FormsemestreCustomMenu(db.Model):
|
||||
"""Menu custom associe au semestre"""
|
||||
|
||||
__tablename__ = "notes_formsemestre_custommenu"
|
||||
@ -219,7 +220,7 @@ class NotesFormsemestreCustomMenu(db.Model):
|
||||
idx = db.Column(db.Integer, default=0, server_default="0") # rang dans le menu
|
||||
|
||||
|
||||
class NotesFormsemestreInscription(db.Model):
|
||||
class FormsemestreInscription(db.Model):
|
||||
"""Inscription à un semestre de formation"""
|
||||
|
||||
__tablename__ = "notes_formsemestre_inscription"
|
||||
@ -239,7 +240,7 @@ class NotesFormsemestreInscription(db.Model):
|
||||
etape = db.Column(db.String(APO_CODE_STR_LEN))
|
||||
|
||||
|
||||
class NotesModuleImpl(db.Model):
|
||||
class ModuleImpl(db.Model):
|
||||
"""Mise en oeuvre d'un module pour une annee/semestre"""
|
||||
|
||||
__tablename__ = "notes_moduleimpl"
|
||||
@ -275,7 +276,7 @@ notes_modules_enseignants = db.Table(
|
||||
# XXX il manque probablement une relation pour gérer cela
|
||||
|
||||
|
||||
class NotesModuleImplInscription(db.Model):
|
||||
class ModuleImplInscription(db.Model):
|
||||
"""Inscription à un module (etudiants,moduleimpl)"""
|
||||
|
||||
__tablename__ = "notes_moduleimpl_inscription"
|
||||
@ -291,7 +292,7 @@ class NotesModuleImplInscription(db.Model):
|
||||
etudid = db.Column(db.Integer, db.ForeignKey("identite.id"), index=True)
|
||||
|
||||
|
||||
class NotesEvaluation(db.Model):
|
||||
class Evaluation(db.Model):
|
||||
"""Evaluation (contrôle, examen, ...)"""
|
||||
|
||||
__tablename__ = "notes_evaluation"
|
||||
@ -306,7 +307,7 @@ class NotesEvaluation(db.Model):
|
||||
heure_fin = db.Column(db.Time)
|
||||
description = db.Column(db.Text)
|
||||
note_max = db.Column(db.Float)
|
||||
coefficient = db.Column(db.Float) # non BUT
|
||||
coefficient = db.Column(db.Float)
|
||||
visibulletin = db.Column(
|
||||
db.Boolean, nullable=False, default=True, server_default="true"
|
||||
)
|
||||
@ -320,7 +321,34 @@ class NotesEvaluation(db.Model):
|
||||
# ordre de presentation (par défaut, le plus petit numero
|
||||
# est la plus ancienne eval):
|
||||
numero = db.Column(db.Integer)
|
||||
ues = db.relationship("NotesUE", secondary="evaluation_ue_poids", viewonly=True)
|
||||
ues = db.relationship("UniteEns", secondary="evaluation_ue_poids", viewonly=True)
|
||||
|
||||
def to_dict(self):
|
||||
e = dict(self.__dict__)
|
||||
e.pop("_sa_instance_state", None)
|
||||
# ScoDoc7 output_formators
|
||||
e["jour"] = ndb.DateISOtoDMY(e["jour"])
|
||||
e["numero"] = ndb.int_null_is_zero(e["numero"])
|
||||
return sco_evaluation_db.evaluation_enrich_dict(e)
|
||||
|
||||
# def from_dict(self, data):
|
||||
# """Set evaluation attributes from given dict values."""
|
||||
# sco_evaluation_db._check_evaluation_args(data)
|
||||
# for field in [
|
||||
# "moduleimpl_id",
|
||||
# "jour",
|
||||
# "heure_debut",
|
||||
# "heure_fin",
|
||||
# "description",
|
||||
# "note_max",
|
||||
# "coefficient",
|
||||
# "visibulletin",
|
||||
# "publish_incomplete",
|
||||
# "evaluation_type",
|
||||
# "numero",
|
||||
# ]:
|
||||
# if field in data:
|
||||
# setattr(self, field, data[field] or None)
|
||||
|
||||
def set_ue_poids(self, ue, poids: float):
|
||||
"""Set poids évaluation vers cette UE"""
|
||||
@ -332,7 +360,7 @@ class NotesEvaluation(db.Model):
|
||||
"""
|
||||
L = []
|
||||
for ue_id, poids in ue_poids_dict.items():
|
||||
ue = NotesUE.query.get(ue_id)
|
||||
ue = UniteEns.query.get(ue_id)
|
||||
L.append(EvaluationUEPoids(evaluation=self, ue=ue, poids=poids))
|
||||
self.ue_poids = L
|
||||
|
||||
@ -361,11 +389,12 @@ class EvaluationUEPoids(db.Model):
|
||||
nullable=False,
|
||||
)
|
||||
evaluation = db.relationship(
|
||||
NotesEvaluation,
|
||||
Evaluation,
|
||||
backref=db.backref("ue_poids", cascade="all, delete-orphan"),
|
||||
)
|
||||
ue = db.relationship(
|
||||
NotesUE, backref=db.backref("evaluation_ue_poids", cascade="all, delete-orphan")
|
||||
UniteEns,
|
||||
backref=db.backref("evaluation_ue_poids", cascade="all, delete-orphan"),
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -100,9 +100,10 @@ class ScolarAutorisationInscription(db.Model):
|
||||
)
|
||||
|
||||
|
||||
class NotesAppreciations(db.Model):
|
||||
class BulAppreciations(db.Model):
|
||||
"""Appréciations sur bulletins"""
|
||||
|
||||
__tablename__ = "notes_appreciations"
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
||||
etudid = db.Column(
|
||||
|
@ -34,6 +34,7 @@ from flask import g, url_for
|
||||
|
||||
from app.models import ScoDocSiteConfig
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app import log
|
||||
from app.scodoc.sco_formulas import NoteVector
|
||||
@ -607,7 +608,7 @@ class NotesTable(object):
|
||||
# si 'NI', etudiant non inscrit a ce module
|
||||
if val != "NI":
|
||||
est_inscrit = True
|
||||
if modimpl["module"]["module_type"] == scu.MODULE_STANDARD:
|
||||
if modimpl["module"]["module_type"] == ModuleType.STANDARD:
|
||||
coef = modimpl["module"]["coefficient"]
|
||||
if modimpl["ue"]["type"] != UE_SPORT:
|
||||
notes.append(val, name=modimpl["module"]["code"])
|
||||
@ -644,11 +645,17 @@ class NotesTable(object):
|
||||
except:
|
||||
# log('comp_etud_moy_ue: exception: val=%s coef=%s' % (val,coef))
|
||||
pass
|
||||
elif modimpl["module"]["module_type"] == scu.MODULE_MALUS:
|
||||
elif modimpl["module"]["module_type"] == ModuleType.MALUS:
|
||||
try:
|
||||
ue_malus += val
|
||||
except:
|
||||
pass # si non inscrit ou manquant, ignore
|
||||
elif modimpl["module"]["module_type"] in (
|
||||
ModuleType.RESSOURCE,
|
||||
ModuleType.SAE,
|
||||
):
|
||||
# XXX temporaire pour ne pas bloquer durant le dev
|
||||
pass
|
||||
else:
|
||||
raise ValueError(
|
||||
"invalid module type (%s)" % modimpl["module"]["module_type"]
|
||||
@ -672,7 +679,7 @@ class NotesTable(object):
|
||||
|
||||
# Recalcule la moyenne en utilisant une formule utilisateur
|
||||
expr_diag = {}
|
||||
formula = sco_compute_moy.get_ue_expression(self.formsemestre_id, ue_id, cnx)
|
||||
formula = sco_compute_moy.get_ue_expression(self.formsemestre_id, ue_id)
|
||||
if formula:
|
||||
moy = sco_compute_moy.compute_user_formula(
|
||||
self.sem,
|
||||
|
@ -53,7 +53,7 @@ def close_db_connection():
|
||||
del g.db_conn
|
||||
|
||||
|
||||
def GetDBConnexion(autocommit=True): # on n'utilise plus autocommit
|
||||
def GetDBConnexion():
|
||||
return g.db_conn
|
||||
|
||||
|
||||
|
@ -45,6 +45,7 @@ from flask_login import current_user
|
||||
from flask_mail import Message
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app import log
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
@ -59,7 +60,7 @@ from app.scodoc import sco_bulletins_xml
|
||||
from app.scodoc import sco_codes_parcours
|
||||
from app.scodoc import sco_cache
|
||||
from app.scodoc import sco_etud
|
||||
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_groups
|
||||
@ -428,7 +429,7 @@ def _ue_mod_bulletin(etudid, formsemestre_id, ue_id, modimpls, nt, version):
|
||||
mod_moy = nt.get_etud_mod_moy(
|
||||
modimpl["moduleimpl_id"], etudid
|
||||
) # peut etre 'NI'
|
||||
is_malus = mod["module"]["module_type"] == scu.MODULE_MALUS
|
||||
is_malus = mod["module"]["module_type"] == ModuleType.MALUS
|
||||
if bul_show_abs_modules:
|
||||
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
|
||||
mod_abs = [nbabs, nbabsjust]
|
||||
@ -558,7 +559,7 @@ def _ue_mod_bulletin(etudid, formsemestre_id, ue_id, modimpls, nt, version):
|
||||
mod["evaluations_incompletes"] = []
|
||||
if sco_preferences.get_preference("bul_show_all_evals", formsemestre_id):
|
||||
complete_eval_ids = set([e["evaluation_id"] for e in evals])
|
||||
all_evals = sco_evaluations.do_evaluation_list(
|
||||
all_evals = sco_evaluation_db.do_evaluation_list(
|
||||
args={"moduleimpl_id": modimpl["moduleimpl_id"]}
|
||||
)
|
||||
all_evals.reverse() # plus ancienne d'abord
|
||||
|
@ -36,7 +36,7 @@ import app.scodoc.notesdb as ndb
|
||||
from app.scodoc import sco_abs
|
||||
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_formsemestre
|
||||
from app.scodoc import sco_groups
|
||||
from app.scodoc import sco_photos
|
||||
@ -277,7 +277,7 @@ def formsemestre_bulletinetud_published_dict(
|
||||
if sco_preferences.get_preference(
|
||||
"bul_show_all_evals", formsemestre_id
|
||||
):
|
||||
all_evals = sco_evaluations.do_evaluation_list(
|
||||
all_evals = sco_evaluation_db.do_evaluation_list(
|
||||
args={"moduleimpl_id": modimpl["moduleimpl_id"]}
|
||||
)
|
||||
all_evals.reverse() # plus ancienne d'abord
|
||||
|
@ -51,7 +51,7 @@ from app.scodoc import sco_abs
|
||||
from app.scodoc import sco_codes_parcours
|
||||
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_formsemestre
|
||||
from app.scodoc import sco_groups
|
||||
from app.scodoc import sco_photos
|
||||
@ -289,7 +289,7 @@ def make_xml_formsemestre_bulletinetud(
|
||||
if sco_preferences.get_preference(
|
||||
"bul_show_all_evals", formsemestre_id
|
||||
):
|
||||
all_evals = sco_evaluations.do_evaluation_list(
|
||||
all_evals = sco_evaluation_db.do_evaluation_list(
|
||||
args={"moduleimpl_id": modimpl["moduleimpl_id"]}
|
||||
)
|
||||
all_evals.reverse() # plus ancienne d'abord
|
||||
|
@ -34,6 +34,7 @@ from flask import url_for, g
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app.scodoc.sco_utils import (
|
||||
ModuleType,
|
||||
NOTES_ATTENTE,
|
||||
NOTES_NEUTRALISE,
|
||||
EVALUATION_NORMALE,
|
||||
@ -44,7 +45,7 @@ from app.scodoc.sco_exceptions import ScoValueError
|
||||
from app import log
|
||||
from app.scodoc import sco_abs
|
||||
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_formsemestre
|
||||
from app.scodoc import sco_formsemestre_inscriptions
|
||||
from app.scodoc import sco_formulas
|
||||
@ -103,8 +104,9 @@ formsemestre_ue_computation_expr_list = _formsemestre_ue_computation_exprEditor.
|
||||
formsemestre_ue_computation_expr_edit = _formsemestre_ue_computation_exprEditor.edit
|
||||
|
||||
|
||||
def get_ue_expression(formsemestre_id, ue_id, cnx, html_quote=False):
|
||||
def get_ue_expression(formsemestre_id, ue_id, html_quote=False):
|
||||
"""Returns UE expression (formula), or None if no expression has been defined"""
|
||||
cnx = ndb.GetDBConnexion()
|
||||
el = formsemestre_ue_computation_expr_list(
|
||||
cnx, {"formsemestre_id": formsemestre_id, "ue_id": ue_id}
|
||||
)
|
||||
@ -203,7 +205,7 @@ def compute_moduleimpl_moyennes(nt, modimpl):
|
||||
"""
|
||||
diag_info = {} # message d'erreur formule
|
||||
moduleimpl_id = modimpl["moduleimpl_id"]
|
||||
is_malus = modimpl["module"]["module_type"] == scu.MODULE_MALUS
|
||||
is_malus = modimpl["module"]["module_type"] == ModuleType.MALUS
|
||||
sem = sco_formsemestre.get_formsemestre(modimpl["formsemestre_id"])
|
||||
etudids = sco_moduleimpl.moduleimpl_listeetuds(
|
||||
moduleimpl_id
|
||||
@ -230,7 +232,7 @@ def compute_moduleimpl_moyennes(nt, modimpl):
|
||||
eval_rattr = None
|
||||
for e in evals:
|
||||
e["nb_inscrits"] = e["etat"]["nb_inscrits"]
|
||||
NotesDB = sco_evaluations.do_evaluation_get_all_notes(
|
||||
NotesDB = sco_evaluation_db.do_evaluation_get_all_notes(
|
||||
e["evaluation_id"]
|
||||
) # toutes, y compris demissions
|
||||
# restreint aux étudiants encore inscrits à ce module
|
||||
|
@ -29,11 +29,13 @@
|
||||
(portage from DTML)
|
||||
"""
|
||||
import flask
|
||||
from flask import url_for, g, request
|
||||
from flask import url_for, render_template
|
||||
from flask import g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
from app import log
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
@ -44,22 +46,6 @@ from app.scodoc import sco_edit_matiere
|
||||
from app.scodoc import sco_moduleimpl
|
||||
from app.scodoc import sco_news
|
||||
|
||||
_MODULE_HELP = """<p class="help">
|
||||
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 <em>évaluations</em>. <br/>
|
||||
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 <em>coefficient</em> associé au module.
|
||||
</p>
|
||||
|
||||
<p class="help">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.
|
||||
</p> """
|
||||
|
||||
_moduleEditor = ndb.EditableTable(
|
||||
"notes_modules",
|
||||
"module_id",
|
||||
@ -120,27 +106,30 @@ def do_module_create(args) -> int:
|
||||
|
||||
|
||||
def module_create(matiere_id=None):
|
||||
"""Creation d'un module"""
|
||||
"""Création d'un module"""
|
||||
from app.scodoc import sco_formations
|
||||
from app.scodoc import sco_edit_ue
|
||||
|
||||
if matiere_id is None:
|
||||
raise ScoValueError("invalid matiere !")
|
||||
M = sco_edit_matiere.matiere_list(args={"matiere_id": matiere_id})[0]
|
||||
UE = sco_edit_ue.ue_list(args={"ue_id": M["ue_id"]})[0]
|
||||
Fo = sco_formations.formation_list(args={"formation_id": UE["formation_id"]})[0]
|
||||
parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"])
|
||||
matiere = sco_edit_matiere.matiere_list(args={"matiere_id": matiere_id})[0]
|
||||
UE = sco_edit_ue.ue_list(args={"ue_id": matiere["ue_id"]})[0]
|
||||
formation = sco_formations.formation_list(
|
||||
args={"formation_id": UE["formation_id"]}
|
||||
)[0]
|
||||
parcours = sco_codes_parcours.get_parcours_from_code(formation["type_parcours"])
|
||||
is_apc = parcours.APC_SAE
|
||||
semestres_indices = list(range(1, parcours.NB_SEM + 1))
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title="Création d'un module"),
|
||||
"""<h2>Création d'un module dans la matière %(titre)s""" % M,
|
||||
"""<h2>Création d'un module dans la matière %(titre)s""" % matiere,
|
||||
""" (UE %(acronyme)s)</h2>""" % 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",
|
||||
],
|
||||
),
|
||||
"""<h2>Modification du module %(titre)s""" % Mod,
|
||||
""" (formation %(acronyme)s, version %(version)s)</h2>""" % Fo,
|
||||
_MODULE_HELP,
|
||||
"""<h2>Modification du module %(titre)s""" % module,
|
||||
""" (formation %(acronyme)s, version %(version)s)</h2>""" % 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="""<div style="width: 90%;"><span class="sco_tag_edit"><textarea data-module_id="{}" class="module_tag_editor">{}</textarea></span></div>""".format(
|
||||
module_id, ",".join(sco_tag_module.module_tag_list(module_id))
|
||||
),
|
||||
initvalues=Mod,
|
||||
initvalues=module,
|
||||
submitlabel="Modifier ce module",
|
||||
)
|
||||
|
||||
@ -602,7 +594,7 @@ def formation_add_malus_modules(formation_id, titre=None, redirect=True):
|
||||
[
|
||||
mod
|
||||
for mod in module_list(args={"ue_id": ue["ue_id"]})
|
||||
if mod["module_type"] == scu.MODULE_MALUS
|
||||
if mod["module_type"] == ModuleType.MALUS
|
||||
]
|
||||
)
|
||||
if nb_mod_malus == 0:
|
||||
@ -654,7 +646,7 @@ def ue_add_malus_module(ue_id, titre=None, code=None):
|
||||
"matiere_id": matiere_id,
|
||||
"formation_id": ue["formation_id"],
|
||||
"semestre_id": semestre_id,
|
||||
"module_type": scu.MODULE_MALUS,
|
||||
"module_type": ModuleType.MALUS,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -32,9 +32,10 @@ import flask
|
||||
from flask import g, url_for, request
|
||||
from flask_login import current_user
|
||||
|
||||
from app.models.formations import NotesUE
|
||||
from app.models.formations import UniteEns
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
from app import log
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator, TF
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
@ -955,7 +956,7 @@ def _ue_table_modules(
|
||||
mod["module_id"]
|
||||
)
|
||||
klass = "notes_module_list"
|
||||
if mod["module_type"] == scu.MODULE_MALUS:
|
||||
if mod["module_type"] == ModuleType.MALUS:
|
||||
klass += " module_malus"
|
||||
H.append('<li class="%s">' % klass)
|
||||
|
||||
@ -1066,20 +1067,20 @@ def ue_sharing_code(ue_code=None, ue_id=None, hide_ue_id=None):
|
||||
formation_code = F["formation_code"]
|
||||
# UE du même code, code formation et departement:
|
||||
q_ues = (
|
||||
NotesUE.query.filter_by(ue_code=ue_code)
|
||||
.join(NotesUE.formation, aliased=True)
|
||||
UniteEns.query.filter_by(ue_code=ue_code)
|
||||
.join(UniteEns.formation, aliased=True)
|
||||
.filter_by(dept_id=g.scodoc_dept_id, formation_code=formation_code)
|
||||
)
|
||||
else:
|
||||
# Toutes les UE du departement avec ce code:
|
||||
q_ues = (
|
||||
NotesUE.query.filter_by(ue_code=ue_code)
|
||||
.join(NotesUE.formation, aliased=True)
|
||||
UniteEns.query.filter_by(ue_code=ue_code)
|
||||
.join(UniteEns.formation, aliased=True)
|
||||
.filter_by(dept_id=g.scodoc_dept_id)
|
||||
)
|
||||
|
||||
if hide_ue_id: # enlève l'ue de depart
|
||||
q_ues = q_ues.filter(NotesUE.id != hide_ue_id)
|
||||
q_ues = q_ues.filter(UniteEns.id != hide_ue_id)
|
||||
|
||||
ues = q_ues.all()
|
||||
if not ues:
|
||||
|
482
app/scodoc/sco_evaluation_db.py
Normal file
482
app/scodoc/sco_evaluation_db.py
Normal file
@ -0,0 +1,482 @@
|
||||
# -*- 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@gmail.com
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""Gestion evaluations (ScoDoc7, sans SQlAlchemy)
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import pprint
|
||||
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask_login import current_user
|
||||
|
||||
from app import log
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
||||
|
||||
from app.scodoc import sco_cache
|
||||
from app.scodoc import sco_edit_module
|
||||
from app.scodoc import sco_formsemestre
|
||||
from app.scodoc import sco_moduleimpl
|
||||
from app.scodoc import sco_news
|
||||
from app.scodoc import sco_permissions_check
|
||||
|
||||
|
||||
_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 evaluation_enrich_dict
|
||||
"heure_fin": ndb.TimetoISO8601, # converti par evaluation_enrich_dict
|
||||
"visibulletin": bool,
|
||||
"publish_incomplete": bool,
|
||||
"evaluation_type": int,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def evaluation_enrich_dict(e):
|
||||
"""add or convert some fileds 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 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 e
|
||||
|
||||
|
||||
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'
|
||||
"""
|
||||
# Attention: transformation fonction ScoDc7 en SQLAlchemy
|
||||
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:
|
||||
evaluation_enrich_dict(e)
|
||||
|
||||
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 modimpl in mods:
|
||||
evals += do_evaluation_list(args={"moduleimpl_id": modimpl["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
|
||||
mod_evals = 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 mod_evals:
|
||||
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(mod_evals, next_eval)
|
||||
else:
|
||||
n = None # a placer en fin
|
||||
if n is None: # pas de date ou en fin:
|
||||
if mod_evals:
|
||||
log(pprint.pformat(mod_evals[-1]))
|
||||
n = mod_evals[-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 <a href="%(url)s">%(titre)s</a>' % 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 <a href="%(url)s">%(titre)s</a>' % mod,
|
||||
url=mod["url"],
|
||||
)
|
||||
|
||||
|
||||
# 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 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)
|
||||
mod_evals = 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 mod_evals]
|
||||
if all_numbered and only_if_unumbered:
|
||||
return # all ok
|
||||
|
||||
# Reset all numeros:
|
||||
i = 1
|
||||
for e in mod_evals:
|
||||
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,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def module_evaluation_insert_before(mod_evals, 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 mod_evals:
|
||||
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"')
|
||||
mod_evals = do_evaluation_list({"moduleimpl_id": e["moduleimpl_id"]})
|
||||
if len(mod_evals) > 1:
|
||||
idx = [p["evaluation_id"] for p in mod_evals].index(evaluation_id)
|
||||
neigh = None # object to swap with
|
||||
if after == 0 and idx > 0:
|
||||
neigh = mod_evals[idx - 1]
|
||||
elif after == 1 and idx < len(mod_evals) - 1:
|
||||
neigh = mod_evals[idx + 1]
|
||||
if neigh: #
|
||||
if neigh["numero"] == e["numero"]:
|
||||
log("Warning: module_evaluation_move: forcing renumber")
|
||||
module_evaluation_renumber(e["moduleimpl_id"], only_if_unumbered=False)
|
||||
else:
|
||||
# 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"],
|
||||
)
|
||||
)
|
332
app/scodoc/sco_evaluation_edit.py
Normal file
332
app/scodoc/sco_evaluation_edit.py
Normal file
@ -0,0 +1,332 @@
|
||||
# -*- 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@gmail.com
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""Formulaire ajout/édition d'une évaluation
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
import flask
|
||||
from flask import url_for, render_template
|
||||
from flask import g
|
||||
from flask_login import current_user
|
||||
from flask import request
|
||||
|
||||
from app import db
|
||||
from app import log
|
||||
from app import models
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import sco_evaluations
|
||||
from app.scodoc import sco_evaluation_db
|
||||
from app.scodoc import sco_moduleimpl
|
||||
from app.scodoc import sco_permissions_check
|
||||
|
||||
|
||||
def evaluation_create_form(
|
||||
moduleimpl_id=None,
|
||||
evaluation_id=None,
|
||||
edit=False,
|
||||
page_title="Évaluation",
|
||||
):
|
||||
"Formulaire création/édition d'une évaluation (pas de ses notes)"
|
||||
if evaluation_id is not None:
|
||||
evaluation = models.Evaluation.query.get(evaluation_id)
|
||||
moduleimpl_id = evaluation.moduleimpl_id
|
||||
#
|
||||
modimpl = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0]
|
||||
mod = modimpl["module"]
|
||||
formsemestre_id = modimpl["formsemestre_id"]
|
||||
sem_ues = db.session.query(models.UniteEns).filter(
|
||||
models.ModuleImpl.formsemestre_id == formsemestre_id,
|
||||
models.Module.id == models.ModuleImpl.module_id,
|
||||
models.UniteEns.id == models.Module.ue_id,
|
||||
)
|
||||
is_malus = mod["module_type"] == ModuleType.MALUS
|
||||
is_apc = mod["module_type"] in (ModuleType.RESSOURCE, ModuleType.SAE)
|
||||
|
||||
min_note_max = scu.NOTES_PRECISION # le plus petit bareme possible
|
||||
#
|
||||
if not sco_permissions_check.can_edit_evaluation(moduleimpl_id=moduleimpl_id):
|
||||
return (
|
||||
html_sco_header.sco_header()
|
||||
+ "<h2>Opération non autorisée</h2><p>"
|
||||
+ "Modification évaluation impossible pour %s"
|
||||
% current_user.get_nomplogin()
|
||||
+ "</p>"
|
||||
+ '<p><a href="moduleimpl_status?moduleimpl_id=%s">Revenir</a></p>'
|
||||
% (moduleimpl_id,)
|
||||
+ html_sco_header.sco_footer()
|
||||
)
|
||||
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 évaluation"
|
||||
link = ""
|
||||
else:
|
||||
# édition données existantes
|
||||
# setup form init values
|
||||
if evaluation_id is None:
|
||||
raise ValueError("missing evaluation_id parameter")
|
||||
initvalues = evaluation.to_dict()
|
||||
moduleimpl_id = initvalues["moduleimpl_id"]
|
||||
submitlabel = "Modifier les données"
|
||||
action = "Modification d'une évaluation"
|
||||
link = ""
|
||||
# Note maximale actuelle dans cette éval ?
|
||||
etat = sco_evaluations.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_descr = '<a href="moduleimpl_status?moduleimpl_id=%s">%s %s</a> %s' % (
|
||||
moduleimpl_id,
|
||||
mod["code"],
|
||||
mod["titre"],
|
||||
link,
|
||||
)
|
||||
H = [
|
||||
f"""<h3>{action} en
|
||||
{scu.MODULE_TYPE_NAMES[mod["module_type"]]} {mod_descr}</h3>
|
||||
"""
|
||||
]
|
||||
|
||||
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)
|
@ -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 <a href="%(url)s">%(titre)s</a>' % 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 <a href="%(url)s">%(titre)s</a>' % 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 += ' <span class="eval_malus">(points de malus)</span>'
|
||||
H = [
|
||||
'<span class="eval_title">Evaluation%s</span><p><b>Module : %s</b></p>'
|
||||
% (etit, mod_descr)
|
||||
]
|
||||
if Mod["module_type"] == scu.MODULE_MALUS:
|
||||
if Mod["module_type"] == ModuleType.MALUS:
|
||||
# Indique l'UE
|
||||
ue = sco_edit_ue.ue_list(args={"ue_id": Mod["ue_id"]})[0]
|
||||
H.append("<p><b>UE : %(acronyme)s</b></p>" % ue)
|
||||
@ -1099,269 +681,3 @@ def evaluation_describe(evaluation_id="", edit_in_place=True):
|
||||
H.append("</p>")
|
||||
|
||||
return '<div class="eval_description">' + "\n".join(H) + "</div>"
|
||||
|
||||
|
||||
def evaluation_create_form(
|
||||
moduleimpl_id=None,
|
||||
evaluation_id=None,
|
||||
edit=False,
|
||||
readonly=False,
|
||||
page_title="Evaluation",
|
||||
):
|
||||
"formulaire creation/edition des evaluations (pas des notes)"
|
||||
if evaluation_id != None:
|
||||
the_eval = do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
moduleimpl_id = the_eval["moduleimpl_id"]
|
||||
#
|
||||
M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0]
|
||||
is_malus = M["module"]["module_type"] == scu.MODULE_MALUS # True si module de malus
|
||||
formsemestre_id = M["formsemestre_id"]
|
||||
min_note_max = scu.NOTES_PRECISION # le plus petit bareme possible
|
||||
if not readonly:
|
||||
if not sco_permissions_check.can_edit_evaluation(moduleimpl_id=moduleimpl_id):
|
||||
return (
|
||||
html_sco_header.sco_header()
|
||||
+ "<h2>Opération non autorisée</h2><p>"
|
||||
+ "Modification évaluation impossible pour %s"
|
||||
% current_user.get_nomplogin()
|
||||
+ "</p>"
|
||||
+ '<p><a href="moduleimpl_status?moduleimpl_id=%s">Revenir</a></p>'
|
||||
% (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 = (
|
||||
'<span class="evallink"><a class="stdlink" href="evaluation_listenotes?moduleimpl_id=%s">voir toutes les notes du module</a></span>'
|
||||
% 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 = """<div class="help"><p class="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.
|
||||
</p><p class="help">
|
||||
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.
|
||||
</p><p class="help">
|
||||
L'option <em>Visible sur bulletins</em> 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).
|
||||
</p><p class="help">
|
||||
Les modalités "rattrapage" et "deuxième session" définissent des évaluations prises en compte de
|
||||
façon spéciale: </p>
|
||||
<ul>
|
||||
<li>les notes d'une évaluation de "rattrapage" remplaceront les moyennes du module
|
||||
<em>si elles sont meilleures que celles calculées</em>.</li>
|
||||
<li>les notes de "deuxième session" remplacent, lorsqu'elles sont saisies, la moyenne de l'étudiant
|
||||
à ce module, même si la note de deuxième session est plus faible.</li>
|
||||
</ul>
|
||||
<p class="help">
|
||||
Dans ces deux cas, le coefficient est ignoré, et toutes les notes n'ont
|
||||
pas besoin d'être rentrées.
|
||||
</p>
|
||||
<p class="help">
|
||||
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).
|
||||
</p>
|
||||
"""
|
||||
mod_descr = '<a href="moduleimpl_status?moduleimpl_id=%s">%s %s</a> %s' % (
|
||||
moduleimpl_id,
|
||||
Mod["code"],
|
||||
Mod["titre"],
|
||||
link,
|
||||
)
|
||||
if not readonly:
|
||||
H = ["<h3>%svaluation en %s</h3>" % (action, mod_descr)]
|
||||
else:
|
||||
return evaluation_describe(evaluation_id)
|
||||
|
||||
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"] = []
|
||||
#
|
||||
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."}))
|
||||
else:
|
||||
form.append(
|
||||
(
|
||||
"coefficient",
|
||||
{
|
||||
"size": 10,
|
||||
"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)",
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
request.base_url,
|
||||
vals,
|
||||
form,
|
||||
cancelbutton="Annuler",
|
||||
submitlabel=submitlabel,
|
||||
initvalues=initvalues,
|
||||
readonly=readonly,
|
||||
)
|
||||
|
||||
dest_url = "moduleimpl_status?moduleimpl_id=%s" % M["moduleimpl_id"]
|
||||
if tf[0] == 0:
|
||||
head = html_sco_header.sco_header(page_title=page_title)
|
||||
return head + "\n".join(H) + "\n" + tf[1] + help + 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 not edit:
|
||||
# creation d'une evaluation
|
||||
evaluation_id = do_evaluation_create(**tf[2])
|
||||
return flask.redirect(dest_url)
|
||||
else:
|
||||
do_evaluation_edit(tf[2])
|
||||
return flask.redirect(dest_url)
|
||||
|
@ -39,7 +39,6 @@ from app.scodoc import sco_codes_parcours
|
||||
from app.scodoc import sco_cache
|
||||
from app.scodoc import sco_formations
|
||||
from app.scodoc import sco_preferences
|
||||
from app.scodoc import sco_users
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
from app import log
|
||||
from app.scodoc.sco_codes_parcours import NO_SEMESTRE_ID
|
||||
@ -369,7 +368,7 @@ def _write_formsemestre_aux(sem, fieldname, valuename):
|
||||
# uniquify
|
||||
values = set([str(x) for x in sem[fieldname]])
|
||||
|
||||
cnx = ndb.GetDBConnexion(autocommit=False)
|
||||
cnx = ndb.GetDBConnexion()
|
||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
tablename = "notes_formsemestre_" + fieldname
|
||||
try:
|
||||
@ -398,6 +397,8 @@ def _write_formsemestre_aux(sem, fieldname, valuename):
|
||||
|
||||
def sem_set_responsable_name(sem):
|
||||
"ajoute champs responsable_name"
|
||||
from app.scodoc import sco_users
|
||||
|
||||
sem["responsable_name"] = ", ".join(
|
||||
[
|
||||
sco_users.user_info(responsable_id)["nomprenom"]
|
||||
|
@ -49,6 +49,7 @@ from app.scodoc import sco_edit_module
|
||||
from app.scodoc import sco_edit_ue
|
||||
from app.scodoc import sco_etud
|
||||
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_groups_copy
|
||||
@ -851,7 +852,7 @@ def formsemestre_delete_moduleimpls(formsemestre_id, module_ids_to_del):
|
||||
)[0]["moduleimpl_id"]
|
||||
mod = sco_edit_module.module_list({"module_id": module_id})[0]
|
||||
# Evaluations dans ce module ?
|
||||
evals = sco_evaluations.do_evaluation_list({"moduleimpl_id": moduleimpl_id})
|
||||
evals = sco_evaluation_db.do_evaluation_list({"moduleimpl_id": moduleimpl_id})
|
||||
if evals:
|
||||
msg += [
|
||||
'<b>impossible de supprimer %s (%s) car il y a %d évaluations définies (<a href="moduleimpl_status?moduleimpl_id=%s" class="stdlink">supprimer les d\'abord</a>)</b>'
|
||||
@ -1030,14 +1031,14 @@ def do_formsemestre_clone(
|
||||
sco_moduleimpl.do_ens_create(args)
|
||||
# optionally, copy evaluations
|
||||
if clone_evaluations:
|
||||
evals = sco_evaluations.do_evaluation_list(
|
||||
evals = sco_evaluation_db.do_evaluation_list(
|
||||
args={"moduleimpl_id": mod_orig["moduleimpl_id"]}
|
||||
)
|
||||
for e in evals:
|
||||
args = e.copy()
|
||||
del args["jour"] # erase date
|
||||
args["moduleimpl_id"] = mid
|
||||
_ = sco_evaluations.do_evaluation_create(**args)
|
||||
_ = sco_evaluation_db.do_evaluation_create(**args)
|
||||
|
||||
# 3- copy uecoefs
|
||||
objs = sco_formsemestre.formsemestre_uecoef_list(
|
||||
@ -1229,7 +1230,7 @@ def formsemestre_delete(formsemestre_id):
|
||||
</ol></div>""",
|
||||
]
|
||||
|
||||
evals = sco_evaluations.do_evaluation_list_in_formsemestre(formsemestre_id)
|
||||
evals = sco_evaluation_db.do_evaluation_list_in_formsemestre(formsemestre_id)
|
||||
if evals:
|
||||
H.append(
|
||||
"""<p class="warning">Attention: il y a %d évaluations dans ce semestre (sa suppression entrainera l'effacement définif des notes) !</p>"""
|
||||
@ -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:
|
||||
|
@ -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):
|
||||
),
|
||||
"""<p><b style="font-size: 130%">Tableau de bord: </b><span class="help">cliquez sur un module pour saisir des notes</span></p>""",
|
||||
]
|
||||
nt = sco_cache.NotesTableCache.get(formsemestre_id)
|
||||
|
||||
if nt.expr_diagnostics:
|
||||
H.append(html_expr_diagnostic(nt.expr_diagnostics))
|
||||
H.append(
|
||||
"""
|
||||
<p>
|
||||
<table class="formsemestre_status">
|
||||
<tr>
|
||||
<th class="formsemestre_status">Code</th>
|
||||
<th class="formsemestre_status">Module</th>
|
||||
<th class="formsemestre_status">Inscrits</th>
|
||||
<th class="resp">Responsable</th>
|
||||
<th class="evals">Evaluations</th></tr>"""
|
||||
)
|
||||
mails_enseignants = set(
|
||||
[sco_users.user_info(ens_id)["email"] for ens_id in sem["responsables"]]
|
||||
) # adr. mail des enseignants
|
||||
for M in Mlist:
|
||||
Mod = M["module"]
|
||||
ModDescr = (
|
||||
"Module "
|
||||
+ M["module"]["titre"]
|
||||
+ ", coef. "
|
||||
+ str(M["module"]["coefficient"])
|
||||
)
|
||||
ModEns = sco_users.user_info(M["responsable_id"])["nomcomplet"]
|
||||
if M["ens"]:
|
||||
ModEns += " (resp.), " + ", ".join(
|
||||
[sco_users.user_info(e["ens_id"])["nomcomplet"] for e in M["ens"]]
|
||||
)
|
||||
ModInscrits = sco_moduleimpl.do_moduleimpl_inscription_list(
|
||||
moduleimpl_id=M["moduleimpl_id"]
|
||||
)
|
||||
mails_enseignants.add(sco_users.user_info(M["responsable_id"])["email"])
|
||||
mails_enseignants |= set(
|
||||
[sco_users.user_info(m["ens_id"])["email"] for m in M["ens"]]
|
||||
)
|
||||
ue = M["ue"]
|
||||
if prev_ue_id != ue["ue_id"]:
|
||||
prev_ue_id = ue["ue_id"]
|
||||
acronyme = ue["acronyme"]
|
||||
titre = ue["titre"]
|
||||
if sco_preferences.get_preference("use_ue_coefs", formsemestre_id):
|
||||
titre += " <b>(coef. %s)</b>" % (ue["coefficient"] or 0.0)
|
||||
H.append(
|
||||
"""<tr class="formsemestre_status_ue"><td colspan="4">
|
||||
<span class="status_ue_acro">%s</span>
|
||||
<span class="status_ue_title">%s</span>
|
||||
</td><td>"""
|
||||
% (acronyme, titre)
|
||||
)
|
||||
|
||||
expr = sco_compute_moy.get_ue_expression(
|
||||
formsemestre_id, ue["ue_id"], cnx, html_quote=True
|
||||
)
|
||||
|
||||
if can_edit:
|
||||
H.append(
|
||||
' <a href="edit_ue_expr?formsemestre_id=%s&ue_id=%s">'
|
||||
% (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("</a>")
|
||||
if expr:
|
||||
H.append(
|
||||
""" <span class="formula" title="mode de calcul de la moyenne d'UE">%s</span>"""
|
||||
% expr
|
||||
)
|
||||
|
||||
H.append("</td></tr>")
|
||||
|
||||
if M["ue"]["type"] != sco_codes_parcours.UE_STANDARD:
|
||||
fontorange = " fontorange" # style css additionnel
|
||||
else:
|
||||
fontorange = ""
|
||||
|
||||
etat = sco_evaluations.do_evaluation_etat_in_mod(nt, M["moduleimpl_id"])
|
||||
if (
|
||||
etat["nb_evals_completes"] > 0
|
||||
and etat["nb_evals_en_cours"] == 0
|
||||
and etat["nb_evals_vides"] == 0
|
||||
):
|
||||
H.append('<tr class="formsemestre_status_green%s">' % fontorange)
|
||||
else:
|
||||
H.append('<tr class="formsemestre_status%s">' % fontorange)
|
||||
|
||||
H.append(
|
||||
'<td class="formsemestre_status_code"><a href="moduleimpl_status?moduleimpl_id=%s" title="%s" class="stdlink">%s</a></td>'
|
||||
% (M["moduleimpl_id"], ModDescr, Mod["code"])
|
||||
)
|
||||
H.append(
|
||||
'<td class="scotext"><a href="moduleimpl_status?moduleimpl_id=%s" title="%s" class="formsemestre_status_link">%s</a></td>'
|
||||
% (M["moduleimpl_id"], ModDescr, Mod["abbrev"] or Mod["titre"])
|
||||
)
|
||||
H.append('<td class="formsemestre_status_inscrits">%s</td>' % len(ModInscrits))
|
||||
H.append(
|
||||
'<td class="resp scotext"><a class="discretelink" href="moduleimpl_status?moduleimpl_id=%s" title="%s">%s</a></td>'
|
||||
% (
|
||||
M["moduleimpl_id"],
|
||||
ModEns,
|
||||
sco_users.user_info(M["responsable_id"])["prenomnom"],
|
||||
)
|
||||
)
|
||||
|
||||
if Mod["module_type"] == scu.MODULE_STANDARD:
|
||||
H.append('<td class="evals">')
|
||||
nb_evals = (
|
||||
etat["nb_evals_completes"]
|
||||
+ etat["nb_evals_en_cours"]
|
||||
+ etat["nb_evals_vides"]
|
||||
)
|
||||
if nb_evals != 0:
|
||||
H.append(
|
||||
'<a href="moduleimpl_status?moduleimpl_id=%s" class="formsemestre_status_link">%s prévues, %s ok</a>'
|
||||
% (M["moduleimpl_id"], nb_evals, etat["nb_evals_completes"])
|
||||
)
|
||||
if etat["nb_evals_en_cours"] > 0:
|
||||
H.append(
|
||||
', <span><a class="redlink" href="moduleimpl_status?moduleimpl_id=%s" title="Il manque des notes">%s en cours</a></span>'
|
||||
% (M["moduleimpl_id"], etat["nb_evals_en_cours"])
|
||||
)
|
||||
if etat["attente"]:
|
||||
H.append(
|
||||
' <span><a class="redlink" href="moduleimpl_status?moduleimpl_id=%s" title="Il y a des notes en attente">[en attente]</a></span>'
|
||||
% 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(
|
||||
"""<td class="malus">
|
||||
<a href="moduleimpl_status?moduleimpl_id=%s" class="formsemestre_status_link">malus (%d notes)</a>
|
||||
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
|
||||
<div class="tableau_modules">
|
||||
""",
|
||||
_TABLEAU_MODULES_HEAD,
|
||||
f"""<tr class="formsemestre_status_cat">
|
||||
<td colspan="5">
|
||||
<span class="status_module_cat">Ressources</span>
|
||||
</td></tr>""",
|
||||
formsemestre_tableau_modules(
|
||||
ressources, nt, formsemestre_id, can_edit=can_edit, show_ues=False
|
||||
),
|
||||
f"""<tr class="formsemestre_status_cat">
|
||||
<td colspan="5">
|
||||
<span class="status_module_cat">SAÉs</span>
|
||||
</td></tr>""",
|
||||
formsemestre_tableau_modules(
|
||||
saes, nt, formsemestre_id, can_edit=can_edit, show_ues=False
|
||||
),
|
||||
]
|
||||
if autres:
|
||||
H += [
|
||||
f"""<tr class="formsemestre_status_cat">
|
||||
<td colspan="5">
|
||||
<span class="status_module_cat">Autres modules</span>
|
||||
</td></tr>""",
|
||||
formsemestre_tableau_modules(
|
||||
autres, nt, formsemestre_id, can_edit=can_edit, show_ues=False
|
||||
),
|
||||
]
|
||||
H += [_TABLEAU_MODULES_FOOT, "</div>"]
|
||||
else:
|
||||
# formations classiques: groupe par UE
|
||||
H += [
|
||||
"<p>",
|
||||
_TABLEAU_MODULES_HEAD,
|
||||
formsemestre_tableau_modules(
|
||||
modimpls,
|
||||
nt,
|
||||
formsemestre_id,
|
||||
can_edit=can_edit,
|
||||
use_ue_coefs=use_ue_coefs,
|
||||
),
|
||||
_TABLEAU_MODULES_FOOT,
|
||||
"</p>",
|
||||
]
|
||||
|
||||
H.append("</td></tr>")
|
||||
H.append("</table></p>")
|
||||
if sco_preferences.get_preference("use_ue_coefs", formsemestre_id):
|
||||
if use_ue_coefs:
|
||||
H.append(
|
||||
"""
|
||||
<p class="infop">utilise les coefficients d'UE pour calculer la moyenne générale.</p>
|
||||
@ -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 = """
|
||||
<table class="formsemestre_status">
|
||||
<tr>
|
||||
<th class="formsemestre_status">Code</th>
|
||||
<th class="formsemestre_status">Module</th>
|
||||
<th class="formsemestre_status">Inscrits</th>
|
||||
<th class="resp">Responsable</th>
|
||||
<th class="evals">Évaluations</th>
|
||||
</tr>
|
||||
"""
|
||||
_TABLEAU_MODULES_FOOT = """</table>"""
|
||||
|
||||
|
||||
def formsemestre_tableau_modules(
|
||||
modimpls, nt, formsemestre_id, can_edit=True, show_ues=True, use_ue_coefs=False
|
||||
) -> str:
|
||||
"Lignes table HTML avec modules du semestre"
|
||||
H = []
|
||||
prev_ue_id = None
|
||||
for modimpl in modimpls:
|
||||
mod = modimpl["module"]
|
||||
mod_descr = (
|
||||
"Module "
|
||||
+ modimpl["module"]["titre"]
|
||||
+ ", coef. "
|
||||
+ str(modimpl["module"]["coefficient"])
|
||||
)
|
||||
mod_ens = sco_users.user_info(modimpl["responsable_id"])["nomcomplet"]
|
||||
if modimpl["ens"]:
|
||||
mod_ens += " (resp.), " + ", ".join(
|
||||
[sco_users.user_info(e["ens_id"])["nomcomplet"] for e in modimpl["ens"]]
|
||||
)
|
||||
mod_inscrits = sco_moduleimpl.do_moduleimpl_inscription_list(
|
||||
moduleimpl_id=modimpl["moduleimpl_id"]
|
||||
)
|
||||
|
||||
ue = modimpl["ue"]
|
||||
if show_ues and (prev_ue_id != ue["ue_id"]):
|
||||
prev_ue_id = ue["ue_id"]
|
||||
titre = ue["titre"]
|
||||
if use_ue_coefs:
|
||||
titre += " <b>(coef. %s)</b>" % (ue["coefficient"] or 0.0)
|
||||
H.append(
|
||||
f"""<tr class="formsemestre_status_ue"><td colspan="4">
|
||||
<span class="status_ue_acro">{ue["acronyme"]}</span>
|
||||
<span class="status_ue_title">{titre}</span>
|
||||
</td><td>"""
|
||||
)
|
||||
if can_edit:
|
||||
H.append(
|
||||
' <a href="edit_ue_expr?formsemestre_id=%s&ue_id=%s">'
|
||||
% (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("</a>")
|
||||
|
||||
expr = sco_compute_moy.get_ue_expression(
|
||||
formsemestre_id, ue["ue_id"], html_quote=True
|
||||
)
|
||||
if expr:
|
||||
H.append(
|
||||
""" <span class="formula" title="mode de calcul de la moyenne d'UE">%s</span>"""
|
||||
% expr
|
||||
)
|
||||
|
||||
H.append("</td></tr>")
|
||||
|
||||
if modimpl["ue"]["type"] != sco_codes_parcours.UE_STANDARD:
|
||||
fontorange = " fontorange" # style css additionnel
|
||||
else:
|
||||
fontorange = ""
|
||||
|
||||
etat = sco_evaluations.do_evaluation_etat_in_mod(nt, modimpl["moduleimpl_id"])
|
||||
if (
|
||||
etat["nb_evals_completes"] > 0
|
||||
and etat["nb_evals_en_cours"] == 0
|
||||
and etat["nb_evals_vides"] == 0
|
||||
):
|
||||
H.append('<tr class="formsemestre_status_green%s">' % fontorange)
|
||||
else:
|
||||
H.append('<tr class="formsemestre_status%s">' % fontorange)
|
||||
|
||||
H.append(
|
||||
'<td class="formsemestre_status_code"><a href="moduleimpl_status?moduleimpl_id=%s" title="%s" class="stdlink">%s</a></td>'
|
||||
% (modimpl["moduleimpl_id"], mod_descr, mod["code"])
|
||||
)
|
||||
H.append(
|
||||
'<td class="scotext"><a href="moduleimpl_status?moduleimpl_id=%s" title="%s" class="formsemestre_status_link">%s</a></td>'
|
||||
% (modimpl["moduleimpl_id"], mod_descr, mod["abbrev"] or mod["titre"])
|
||||
)
|
||||
H.append('<td class="formsemestre_status_inscrits">%s</td>' % len(mod_inscrits))
|
||||
H.append(
|
||||
'<td class="resp scotext"><a class="discretelink" href="moduleimpl_status?moduleimpl_id=%s" title="%s">%s</a></td>'
|
||||
% (
|
||||
modimpl["moduleimpl_id"],
|
||||
mod_ens,
|
||||
sco_users.user_info(modimpl["responsable_id"])["prenomnom"],
|
||||
)
|
||||
)
|
||||
|
||||
if mod["module_type"] in (
|
||||
ModuleType.STANDARD,
|
||||
ModuleType.RESSOURCE,
|
||||
ModuleType.SAE,
|
||||
):
|
||||
H.append('<td class="evals">')
|
||||
nb_evals = (
|
||||
etat["nb_evals_completes"]
|
||||
+ etat["nb_evals_en_cours"]
|
||||
+ etat["nb_evals_vides"]
|
||||
)
|
||||
if nb_evals != 0:
|
||||
H.append(
|
||||
'<a href="moduleimpl_status?moduleimpl_id=%s" class="formsemestre_status_link">%s prévues, %s ok</a>'
|
||||
% (modimpl["moduleimpl_id"], nb_evals, etat["nb_evals_completes"])
|
||||
)
|
||||
if etat["nb_evals_en_cours"] > 0:
|
||||
H.append(
|
||||
', <span><a class="redlink" href="moduleimpl_status?moduleimpl_id=%s" title="Il manque des notes">%s en cours</a></span>'
|
||||
% (modimpl["moduleimpl_id"], etat["nb_evals_en_cours"])
|
||||
)
|
||||
if etat["attente"]:
|
||||
H.append(
|
||||
' <span><a class="redlink" href="moduleimpl_status?moduleimpl_id=%s" title="Il y a des notes en attente">[en attente]</a></span>'
|
||||
% modimpl["moduleimpl_id"]
|
||||
)
|
||||
elif mod["module_type"] == ModuleType.MALUS:
|
||||
nb_malus_notes = sum(
|
||||
[
|
||||
e["etat"]["nb_notes"]
|
||||
for e in nt.get_mod_evaluation_etat_list(modimpl["moduleimpl_id"])
|
||||
]
|
||||
)
|
||||
H.append(
|
||||
"""<td class="malus">
|
||||
<a href="moduleimpl_status?moduleimpl_id=%s" class="formsemestre_status_link">malus (%d notes)</a>
|
||||
"""
|
||||
% (modimpl["moduleimpl_id"], nb_malus_notes)
|
||||
)
|
||||
else:
|
||||
raise ValueError("Invalid module_type") # a bug
|
||||
|
||||
H.append("</td></tr>")
|
||||
return "\n".join(H)
|
||||
|
@ -956,7 +956,7 @@ def do_formsemestre_validation_auto(formsemestre_id):
|
||||
def formsemestre_validation_suppress_etud(formsemestre_id, etudid):
|
||||
"""Suppression des decisions de jury pour un etudiant."""
|
||||
log("formsemestre_validation_suppress_etud( %s, %s)" % (formsemestre_id, etudid))
|
||||
cnx = ndb.GetDBConnexion(autocommit=False)
|
||||
cnx = ndb.GetDBConnexion()
|
||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
args = {"formsemestre_id": formsemestre_id, "etudid": etudid}
|
||||
try:
|
||||
@ -1123,7 +1123,7 @@ def do_formsemestre_validate_previous_ue(
|
||||
cette UE (utile seulement pour les semestres extérieurs).
|
||||
"""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
cnx = ndb.GetDBConnexion(autocommit=False)
|
||||
cnx = ndb.GetDBConnexion()
|
||||
nt = sco_cache.NotesTableCache.get(formsemestre_id) # > get_etud_ue_status
|
||||
if ue_coefficient != None:
|
||||
sco_formsemestre.do_formsemestre_uecoef_edit_or_create(
|
||||
|
@ -262,7 +262,7 @@ def scolars_import_excel_file(
|
||||
et les inscrit dans le semestre indiqué (et à TOUS ses modules)
|
||||
"""
|
||||
log("scolars_import_excel_file: formsemestre_id=%s" % formsemestre_id)
|
||||
cnx = ndb.GetDBConnexion(autocommit=False)
|
||||
cnx = ndb.GetDBConnexion()
|
||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
annee_courante = time.localtime()[0]
|
||||
always_require_ine = sco_preferences.get_preference("always_require_ine")
|
||||
|
@ -37,13 +37,12 @@ import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app import log
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
from app.scodoc import htmlutils
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import sco_abs
|
||||
from app.scodoc import sco_cache
|
||||
from app.scodoc import sco_edit_module
|
||||
from app.scodoc import sco_evaluations
|
||||
from app.scodoc import sco_excel
|
||||
from app.scodoc import sco_evaluation_db
|
||||
from app.scodoc import sco_formsemestre
|
||||
from app.scodoc import sco_formsemestre_inscriptions
|
||||
from app.scodoc import sco_groups
|
||||
@ -68,11 +67,11 @@ def do_evaluation_listenotes():
|
||||
if "evaluation_id" in vals:
|
||||
evaluation_id = int(vals["evaluation_id"])
|
||||
mode = "eval"
|
||||
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
evals = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
if "moduleimpl_id" in vals and vals["moduleimpl_id"]:
|
||||
moduleimpl_id = int(vals["moduleimpl_id"])
|
||||
mode = "module"
|
||||
evals = sco_evaluations.do_evaluation_list({"moduleimpl_id": moduleimpl_id})
|
||||
evals = sco_evaluation_db.do_evaluation_list({"moduleimpl_id": moduleimpl_id})
|
||||
if not mode:
|
||||
raise ValueError("missing argument: evaluation or module")
|
||||
if not evals:
|
||||
@ -545,7 +544,7 @@ def _add_eval_columns(
|
||||
sum_notes = 0
|
||||
notes = [] # liste des notes numeriques, pour calcul histogramme uniquement
|
||||
evaluation_id = e["evaluation_id"]
|
||||
NotesDB = sco_evaluations.do_evaluation_get_all_notes(evaluation_id)
|
||||
NotesDB = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
|
||||
for row in rows:
|
||||
etudid = row["etudid"]
|
||||
if etudid in NotesDB:
|
||||
@ -710,7 +709,7 @@ def evaluation_check_absences(evaluation_id):
|
||||
EXC et pas justifie
|
||||
Ramene 3 listes d'etudid
|
||||
"""
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
if not E["jour"]:
|
||||
return [], [], [], [], [] # evaluation sans date
|
||||
|
||||
@ -731,7 +730,7 @@ def evaluation_check_absences(evaluation_id):
|
||||
Justs = set([x["etudid"] for x in Just]) # ensemble des etudiants avec justif
|
||||
|
||||
# Les notes:
|
||||
NotesDB = sco_evaluations.do_evaluation_get_all_notes(evaluation_id)
|
||||
NotesDB = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
|
||||
ValButAbs = [] # une note mais noté absent
|
||||
AbsNonSignalee = [] # note ABS mais pas noté absent
|
||||
ExcNonSignalee = [] # note EXC mais pas noté absent
|
||||
@ -764,7 +763,7 @@ def evaluation_check_absences(evaluation_id):
|
||||
def evaluation_check_absences_html(evaluation_id, with_header=True, show_ok=True):
|
||||
"""Affiche etat verification absences d'une evaluation"""
|
||||
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
am, pm, demijournee = _eval_demijournee(E)
|
||||
|
||||
(
|
||||
@ -876,7 +875,7 @@ def formsemestre_check_absences_html(formsemestre_id):
|
||||
# Modules, dans l'ordre
|
||||
Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
|
||||
for M in Mlist:
|
||||
evals = sco_evaluations.do_evaluation_list(
|
||||
evals = sco_evaluation_db.do_evaluation_list(
|
||||
{"moduleimpl_id": M["moduleimpl_id"]}
|
||||
)
|
||||
if evals:
|
||||
|
@ -28,7 +28,6 @@
|
||||
"""Tableau de bord module
|
||||
"""
|
||||
import time
|
||||
import urllib
|
||||
|
||||
from flask import g, url_for
|
||||
from flask_login import current_user
|
||||
@ -44,6 +43,7 @@ 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
|
||||
@ -57,7 +57,7 @@ from app.scodoc import sco_users
|
||||
# menu evaluation dans moduleimpl
|
||||
def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0):
|
||||
"Menu avec actions sur une evaluation"
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
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"])
|
||||
@ -161,13 +161,13 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0]
|
||||
ModInscrits = sco_moduleimpl.do_moduleimpl_inscription_list(
|
||||
mod_inscrits = sco_moduleimpl.do_moduleimpl_inscription_list(
|
||||
moduleimpl_id=M["moduleimpl_id"]
|
||||
)
|
||||
|
||||
nt = sco_cache.NotesTableCache.get(formsemestre_id)
|
||||
ModEvals = sco_evaluations.do_evaluation_list({"moduleimpl_id": moduleimpl_id})
|
||||
ModEvals.sort(
|
||||
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
|
||||
|
||||
@ -179,15 +179,19 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
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="Module %(titre)s" % Mod),
|
||||
"""<h2 class="formsemestre">Module <tt>%(code)s</tt> %(titre)s</h2>""" % Mod,
|
||||
"""<div class="moduleimpl_tableaubord">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="fichetitre2">Responsable: </td><td class="redboldtext">""",
|
||||
module_resp.get_nomcomplet(), # sco_users.user_info(M["responsable_id"])["nomprenom"],
|
||||
f"""<span class="blacktt">({module_resp.user_name})</span>""",
|
||||
html_sco_header.sco_header(page_title=f"{mod_type_name} {Mod['titre']}"),
|
||||
f"""<h2 class="formsemestre">{mod_type_name}
|
||||
<tt>{Mod['code']}</tt> {Mod['titre']}</h2>
|
||||
<div class="moduleimpl_tableaubord moduleimpl_type_{
|
||||
scu.ModuleType(Mod['module_type']).name.lower()}">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="fichetitre2">Responsable: </td><td class="redboldtext">
|
||||
{module_resp.get_nomcomplet()}
|
||||
<span class="blacktt">({module_resp.user_name})</span>
|
||||
""",
|
||||
]
|
||||
try:
|
||||
sco_moduleimpl.can_change_module_resp(moduleimpl_id)
|
||||
@ -231,7 +235,7 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
# Ligne: Inscrits
|
||||
H.append(
|
||||
"""<tr><td class="fichetitre2">Inscrits: </td><td> %d étudiants"""
|
||||
% len(ModInscrits)
|
||||
% len(mod_inscrits)
|
||||
)
|
||||
if current_user.has_permission(Permission.ScoEtudInscrit):
|
||||
H.append(
|
||||
@ -297,7 +301,7 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
"""<p><form name="f"><span style="font-size:120%%; font-weight: bold;">%d évaluations :</span>
|
||||
<span style="padding-left: 30px;">
|
||||
<input type="hidden" name="moduleimpl_id" value="%s"/>"""
|
||||
% (len(ModEvals), moduleimpl_id)
|
||||
% (len(mod_evals), moduleimpl_id)
|
||||
)
|
||||
#
|
||||
# Liste les noms de partitions
|
||||
@ -341,16 +345,16 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
"""
|
||||
% M
|
||||
)
|
||||
if ModEvals:
|
||||
if mod_evals:
|
||||
H.append(
|
||||
'<div class="moduleimpl_evaluations_top_links">'
|
||||
+ top_table_links
|
||||
+ "</div>"
|
||||
)
|
||||
H.append("""<table class="moduleimpl_evaluations">""")
|
||||
eval_index = len(ModEvals) - 1
|
||||
eval_index = len(mod_evals) - 1
|
||||
first = True
|
||||
for eval in ModEvals:
|
||||
for eval in mod_evals:
|
||||
etat = sco_evaluations.do_evaluation_etat(
|
||||
eval["evaluation_id"],
|
||||
partition_id=partition_id,
|
||||
@ -399,7 +403,7 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
)
|
||||
# Fleches:
|
||||
H.append('<span class="eval_arrows_chld">')
|
||||
if eval_index != (len(ModEvals) - 1) and caneditevals:
|
||||
if eval_index != (len(mod_evals) - 1) and caneditevals:
|
||||
H.append(
|
||||
'<a href="module_evaluation_move?evaluation_id=%s&after=0" class="aud">%s</a>'
|
||||
% (eval["evaluation_id"], arrow_up)
|
||||
|
@ -42,7 +42,6 @@ from app import log
|
||||
from app.scodoc import sco_formsemestre
|
||||
from app.scodoc import sco_moduleimpl
|
||||
from app.scodoc import sco_preferences
|
||||
from app.scodoc import sco_users
|
||||
from app import email
|
||||
|
||||
|
||||
@ -82,6 +81,8 @@ def add(typ, object=None, text="", url=None, max_frequency=False):
|
||||
Si max_frequency, ne genere pas 2 nouvelles identiques à moins de max_frequency
|
||||
secondes d'intervalle.
|
||||
"""
|
||||
from app.scodoc import sco_users
|
||||
|
||||
authuser_name = current_user.user_name
|
||||
cnx = ndb.GetDBConnexion()
|
||||
args = {
|
||||
@ -112,6 +113,7 @@ def scolar_news_summary(n=5):
|
||||
News are "compressed", ie redondant events are joined.
|
||||
"""
|
||||
from app.scodoc import sco_etud
|
||||
from app.scodoc import sco_users
|
||||
|
||||
cnx = ndb.GetDBConnexion()
|
||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
|
@ -539,7 +539,7 @@ class SituationEtudParcoursGeneric(object):
|
||||
"""Enregistre la decision (instance de DecisionSem)
|
||||
Enregistre codes semestre et UE, et autorisations inscription.
|
||||
"""
|
||||
cnx = ndb.GetDBConnexion(autocommit=False)
|
||||
cnx = ndb.GetDBConnexion()
|
||||
# -- check
|
||||
if decision.code_etat in self.parcours.UNUSED_CODES:
|
||||
raise ScoValueError("code decision invalide dans ce parcours")
|
||||
@ -902,7 +902,7 @@ def formsemestre_validate_ues(formsemestre_id, etudid, code_etat_sem, assiduite)
|
||||
Les UE des semestres NON ASSIDUS ne sont jamais validées (code AJ).
|
||||
"""
|
||||
valid_semestre = CODES_SEM_VALIDES.get(code_etat_sem, False)
|
||||
cnx = ndb.GetDBConnexion(autocommit=False)
|
||||
cnx = ndb.GetDBConnexion()
|
||||
nt = sco_cache.NotesTableCache.get(formsemestre_id) # > get_ues, get_etud_ue_status
|
||||
ue_ids = [x["ue_id"] for x in nt.get_ues(etudid=etudid, filter_sport=True)]
|
||||
for ue_id in ue_ids:
|
||||
|
@ -54,6 +54,7 @@ from app import ScoValueError
|
||||
from app.scodoc import html_sco_header, sco_preferences
|
||||
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_excel
|
||||
from app.scodoc.sco_excel import ScoExcelBook, COLORS
|
||||
from app.scodoc import sco_formsemestre
|
||||
@ -137,7 +138,9 @@ class PlacementForm(FlaskForm):
|
||||
|
||||
def set_evaluation_infos(self, evaluation_id):
|
||||
"""Initialise les données du formulaire avec les données de l'évaluation."""
|
||||
eval_data = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
eval_data = sco_evaluation_db.do_evaluation_list(
|
||||
{"evaluation_id": evaluation_id}
|
||||
)
|
||||
if not eval_data:
|
||||
raise ScoValueError("invalid evaluation_id")
|
||||
self.groups_tree, self.has_groups, self.nb_groups = _get_group_info(
|
||||
@ -236,7 +239,7 @@ class PlacementRunner:
|
||||
self.groups_ids = [
|
||||
gid if gid != TOUS else form.tous_id for gid in form["groups"].data
|
||||
]
|
||||
self.eval_data = sco_evaluations.do_evaluation_list(
|
||||
self.eval_data = sco_evaluation_db.do_evaluation_list(
|
||||
{"evaluation_id": self.evaluation_id}
|
||||
)[0]
|
||||
self.groups = sco_groups.listgroups(self.groups_ids)
|
||||
|
@ -45,11 +45,11 @@ from app.scodoc import sco_bulletins, sco_excel
|
||||
from app.scodoc import sco_codes_parcours
|
||||
from app.scodoc import sco_cache
|
||||
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_permissions
|
||||
from app.scodoc import sco_permissions_check
|
||||
from app.scodoc import sco_preferences
|
||||
from app.scodoc import sco_etud
|
||||
@ -818,9 +818,9 @@ def _list_notes_evals(evals, etudid):
|
||||
or e["etat"]["evalattente"]
|
||||
or e["publish_incomplete"]
|
||||
):
|
||||
NotesDB = sco_evaluations.do_evaluation_get_all_notes(e["evaluation_id"])
|
||||
if etudid in NotesDB:
|
||||
val = NotesDB[etudid]["value"]
|
||||
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(e["evaluation_id"])
|
||||
if etudid in notes_db:
|
||||
val = notes_db[etudid]["value"]
|
||||
else:
|
||||
# Note manquante mais prise en compte immédiate: affiche ATT
|
||||
val = scu.NOTES_ATTENTE
|
||||
|
@ -40,7 +40,7 @@ from flask import url_for, g, request
|
||||
import pydot
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.models import NotesFormModalite
|
||||
from app.models import FormationModalite
|
||||
from app.scodoc import notesdb as ndb
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import sco_codes_parcours
|
||||
@ -1340,7 +1340,7 @@ def graph_parcours(
|
||||
log("n=%s" % n)
|
||||
log("get=%s" % g.get_node(sem_node_name(s)))
|
||||
log("nodes names = %s" % [x.get_name() for x in g.get_node_list()])
|
||||
if s["modalite"] and s["modalite"] != NotesFormModalite.DEFAULT_MODALITE:
|
||||
if s["modalite"] and s["modalite"] != FormationModalite.DEFAULT_MODALITE:
|
||||
modalite = " " + s["modalite"]
|
||||
else:
|
||||
modalite = ""
|
||||
|
@ -39,6 +39,7 @@ from flask import g, url_for, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app import log
|
||||
from app.scodoc.sco_exceptions import (
|
||||
@ -56,6 +57,7 @@ from app.scodoc import sco_abs
|
||||
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_excel
|
||||
from app.scodoc import sco_formsemestre
|
||||
from app.scodoc import sco_formsemestre_inscriptions
|
||||
@ -133,9 +135,13 @@ def _check_notes(notes, evaluation, mod):
|
||||
and 4 lists of etudid: invalids, withoutnotes, absents, tosuppress, existingjury
|
||||
"""
|
||||
note_max = evaluation["note_max"]
|
||||
if mod["module_type"] == scu.MODULE_STANDARD:
|
||||
if mod["module_type"] in (
|
||||
scu.ModuleType.STANDARD,
|
||||
scu.ModuleType.RESSOURCE,
|
||||
scu.ModuleType.SAE,
|
||||
):
|
||||
note_min = scu.NOTES_MIN
|
||||
elif mod["module_type"] == scu.MODULE_MALUS:
|
||||
elif mod["module_type"] == ModuleType.MALUS:
|
||||
note_min = -20.0
|
||||
else:
|
||||
raise ValueError("Invalid module type") # bug
|
||||
@ -176,7 +182,7 @@ def do_evaluation_upload_xls():
|
||||
vals = scu.get_request_args()
|
||||
evaluation_id = int(vals["evaluation_id"])
|
||||
comment = vals["comment"]
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
||||
# Check access
|
||||
# (admin, respformation, and responsable_id)
|
||||
@ -253,7 +259,9 @@ def do_evaluation_upload_xls():
|
||||
authuser, evaluation_id, L, comment
|
||||
)
|
||||
# news
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
E = sco_evaluation_db.do_evaluation_list({"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]
|
||||
mod["moduleimpl_id"] = M["moduleimpl_id"]
|
||||
@ -292,7 +300,7 @@ def do_evaluation_upload_xls():
|
||||
|
||||
def do_evaluation_set_missing(evaluation_id, value, dialog_confirmed=False):
|
||||
"""Initialisation des notes manquantes"""
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
||||
# Check access
|
||||
# (admin, respformation, and responsable_id)
|
||||
@ -300,7 +308,7 @@ def do_evaluation_set_missing(evaluation_id, value, dialog_confirmed=False):
|
||||
# XXX imaginer un redirect + msg erreur
|
||||
raise AccessDenied("Modification des notes impossible pour %s" % current_user)
|
||||
#
|
||||
NotesDB = sco_evaluations.do_evaluation_get_all_notes(evaluation_id)
|
||||
NotesDB = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
|
||||
etudids = sco_groups.do_evaluation_listeetuds_groups(
|
||||
evaluation_id, getallstudents=True, include_dems=False
|
||||
)
|
||||
@ -378,19 +386,19 @@ def do_evaluation_set_missing(evaluation_id, value, dialog_confirmed=False):
|
||||
|
||||
def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
|
||||
"suppress all notes in this eval"
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
|
||||
if sco_permissions_check.can_edit_notes(
|
||||
current_user, E["moduleimpl_id"], allow_ens=False
|
||||
):
|
||||
# On a le droit de modifier toutes les notes
|
||||
# recupere les etuds ayant une note
|
||||
NotesDB = sco_evaluations.do_evaluation_get_all_notes(evaluation_id)
|
||||
NotesDB = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
|
||||
elif sco_permissions_check.can_edit_notes(
|
||||
current_user, E["moduleimpl_id"], allow_ens=True
|
||||
):
|
||||
# Enseignant associé au module: ne peut supprimer que les notes qu'il a saisi
|
||||
NotesDB = sco_evaluations.do_evaluation_get_all_notes(
|
||||
NotesDB = sco_evaluation_db.do_evaluation_get_all_notes(
|
||||
evaluation_id, by_uid=current_user.id
|
||||
)
|
||||
else:
|
||||
@ -471,13 +479,13 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
|
||||
"etudiant %s: valeur de note invalide (%s)" % (etudid, value)
|
||||
)
|
||||
# Recherche notes existantes
|
||||
NotesDB = sco_evaluations.do_evaluation_get_all_notes(evaluation_id)
|
||||
NotesDB = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
|
||||
# Met a jour la base
|
||||
cnx = ndb.GetDBConnexion(autocommit=False)
|
||||
cnx = ndb.GetDBConnexion()
|
||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
nb_changed = 0
|
||||
nb_suppress = 0
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
||||
existing_decisions = (
|
||||
[]
|
||||
@ -596,7 +604,7 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
|
||||
|
||||
def saisie_notes_tableur(evaluation_id, group_ids=()):
|
||||
"""Saisie des notes via un fichier Excel"""
|
||||
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
evals = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
if not evals:
|
||||
raise ScoValueError("invalid evaluation_id")
|
||||
E = evals[0]
|
||||
@ -767,7 +775,7 @@ def saisie_notes_tableur(evaluation_id, group_ids=()):
|
||||
|
||||
def feuille_saisie_notes(evaluation_id, group_ids=[]):
|
||||
"""Document Excel pour saisie notes dans l'évaluation et les groupes indiqués"""
|
||||
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
evals = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
if not evals:
|
||||
raise ScoValueError("invalid evaluation_id")
|
||||
E = evals[0]
|
||||
@ -866,7 +874,7 @@ def has_existing_decision(M, E, etudid):
|
||||
def saisie_notes(evaluation_id, group_ids=[]):
|
||||
"""Formulaire saisie notes d'une évaluation pour un groupe"""
|
||||
group_ids = [int(group_id) for group_id in group_ids]
|
||||
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
evals = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
if not evals:
|
||||
raise ScoValueError("invalid evaluation_id")
|
||||
E = evals[0]
|
||||
@ -977,7 +985,7 @@ def saisie_notes(evaluation_id, group_ids=[]):
|
||||
|
||||
def _get_sorted_etuds(E, etudids, formsemestre_id):
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
NotesDB = sco_evaluations.do_evaluation_get_all_notes(
|
||||
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(
|
||||
E["evaluation_id"]
|
||||
) # Notes existantes
|
||||
cnx = ndb.GetDBConnexion()
|
||||
@ -1017,14 +1025,14 @@ def _get_sorted_etuds(E, etudids, formsemestre_id):
|
||||
e["absinfo"] = '<span class="sn_abs">' + " ".join(warn_abs_lst) + "</span> "
|
||||
|
||||
# Note actuelle de l'étudiant:
|
||||
if etudid in NotesDB:
|
||||
e["val"] = _displayNote(NotesDB[etudid]["value"])
|
||||
comment = NotesDB[etudid]["comment"]
|
||||
if etudid in notes_db:
|
||||
e["val"] = _displayNote(notes_db[etudid]["value"])
|
||||
comment = notes_db[etudid]["comment"]
|
||||
if comment is None:
|
||||
comment = ""
|
||||
e["explanation"] = "%s (%s) %s" % (
|
||||
NotesDB[etudid]["date"].strftime("%d/%m/%y %Hh%M"),
|
||||
NotesDB[etudid]["uid"],
|
||||
notes_db[etudid]["date"].strftime("%d/%m/%y %Hh%M"),
|
||||
notes_db[etudid]["uid"],
|
||||
comment,
|
||||
)
|
||||
else:
|
||||
@ -1073,7 +1081,11 @@ def _form_saisie_notes(E, M, group_ids, destination=""):
|
||||
("comment", {"size": 44, "title": "Commentaire", "return_focus_next": True}),
|
||||
("changed", {"default": "0", "input_type": "hidden"}), # changed in JS
|
||||
]
|
||||
if M["module"]["module_type"] == scu.MODULE_STANDARD:
|
||||
if M["module"]["module_type"] in (
|
||||
ModuleType.STANDARD,
|
||||
ModuleType.RESSOURCE,
|
||||
ModuleType.SAE,
|
||||
):
|
||||
descr.append(
|
||||
(
|
||||
"s3",
|
||||
@ -1086,7 +1098,7 @@ def _form_saisie_notes(E, M, group_ids, destination=""):
|
||||
},
|
||||
)
|
||||
)
|
||||
elif M["module"]["module_type"] == scu.MODULE_MALUS:
|
||||
elif M["module"]["module_type"] == ModuleType.MALUS:
|
||||
descr.append(
|
||||
(
|
||||
"s3",
|
||||
@ -1229,7 +1241,7 @@ def save_note(etudid=None, evaluation_id=None, value=None, comment=""):
|
||||
"save_note: evaluation_id=%s etudid=%s uid=%s value=%s"
|
||||
% (evaluation_id, etudid, authuser, value)
|
||||
)
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
E = sco_evaluation_db.do_evaluation_list({"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]
|
||||
Mod["url"] = url_for(
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
"""Fonction de gestion des UE "externes" (effectuees dans un cursus exterieur)
|
||||
|
||||
On rapatrie (saisit) les notes (et crédits ECTS).
|
||||
On rapatrie (saisie) les notes (et crédits ECTS).
|
||||
|
||||
Cas d'usage: les étudiants d'une formation gérée par ScoDoc peuvent
|
||||
suivre un certain nombre d'UE à l'extérieur. L'établissement a reconnu
|
||||
@ -66,6 +66,7 @@ from app.scodoc import sco_edit_matiere
|
||||
from app.scodoc import sco_edit_module
|
||||
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_moduleimpl
|
||||
@ -148,13 +149,15 @@ def external_ue_inscrit_et_note(moduleimpl_id, formsemestre_id, notes_etuds):
|
||||
)
|
||||
|
||||
# Création d'une évaluation si il n'y en a pas déjà:
|
||||
ModEvals = sco_evaluations.do_evaluation_list(args={"moduleimpl_id": moduleimpl_id})
|
||||
if len(ModEvals):
|
||||
mod_evals = sco_evaluation_db.do_evaluation_list(
|
||||
args={"moduleimpl_id": moduleimpl_id}
|
||||
)
|
||||
if len(mod_evals):
|
||||
# met la note dans le première évaluation existante:
|
||||
evaluation_id = ModEvals[0]["evaluation_id"]
|
||||
evaluation_id = mod_evals[0]["evaluation_id"]
|
||||
else:
|
||||
# crée une évaluation:
|
||||
evaluation_id = sco_evaluations.do_evaluation_create(
|
||||
evaluation_id = sco_evaluation_db.do_evaluation_create(
|
||||
moduleimpl_id=moduleimpl_id,
|
||||
note_max=20.0,
|
||||
coefficient=1.0,
|
||||
|
@ -53,6 +53,7 @@ from app.scodoc.intervals import intervalmap
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app.scodoc import sco_evaluations
|
||||
from app.scodoc import sco_evaluation_db
|
||||
from app.scodoc import sco_formsemestre
|
||||
from app.scodoc import sco_moduleimpl
|
||||
from app.scodoc import sco_preferences
|
||||
@ -105,12 +106,12 @@ class NotesOperation(dict):
|
||||
def list_operations(evaluation_id):
|
||||
"""returns list of NotesOperation for this evaluation"""
|
||||
notes = list(
|
||||
sco_evaluations.do_evaluation_get_all_notes(
|
||||
sco_evaluation_db.do_evaluation_get_all_notes(
|
||||
evaluation_id, filter_suppressed=False
|
||||
).values()
|
||||
)
|
||||
notes_log = list(
|
||||
sco_evaluations.do_evaluation_get_all_notes(
|
||||
sco_evaluation_db.do_evaluation_get_all_notes(
|
||||
evaluation_id, filter_suppressed=False, table="notes_notes_log"
|
||||
).values()
|
||||
)
|
||||
@ -148,7 +149,7 @@ def list_operations(evaluation_id):
|
||||
|
||||
def evaluation_list_operations(evaluation_id):
|
||||
"""Page listing operations on evaluation"""
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
||||
|
||||
Ops = list_operations(evaluation_id)
|
||||
|
@ -49,7 +49,7 @@ import pydot
|
||||
import requests
|
||||
|
||||
from flask import g, request
|
||||
from flask import url_for, make_response
|
||||
from flask import url_for, make_response, jsonify
|
||||
|
||||
from config import Config
|
||||
from app import log
|
||||
@ -71,20 +71,23 @@ NOTES_ATTENTE = -1002.0 # note "en attente" (se calcule comme une note neutrali
|
||||
|
||||
|
||||
# Types de modules
|
||||
MODULE_STANDARD = 0
|
||||
MODULE_MALUS = 1
|
||||
|
||||
|
||||
class ModuleType(IntEnum):
|
||||
"""Code des types de module."""
|
||||
|
||||
# Stockés en BD dans NotesModule.module_type: ne pas modifier ces valeurs
|
||||
# Stockés en BD dans Module.module_type: ne pas modifier ces valeurs
|
||||
STANDARD = 0
|
||||
MALUS = 1
|
||||
RESSOURCE = 2 # BUT
|
||||
SAE = 3 # BUT
|
||||
|
||||
|
||||
MODULE_TYPE_NAMES = {
|
||||
ModuleType.STANDARD: "Module",
|
||||
ModuleType.MALUS: "Malus",
|
||||
ModuleType.RESSOURCE: "Ressource",
|
||||
ModuleType.SAE: "SAÉ",
|
||||
}
|
||||
|
||||
MALUS_MAX = 20.0
|
||||
MALUS_MIN = -20.0
|
||||
|
||||
@ -667,6 +670,17 @@ def get_request_args():
|
||||
return vals
|
||||
|
||||
|
||||
def json_error(message, success=False, status=404):
|
||||
"""Simple JSON response, for errors"""
|
||||
response = {
|
||||
"success": success,
|
||||
"status": status,
|
||||
"message": message,
|
||||
}
|
||||
log(f"Error: {response}")
|
||||
return jsonify(response), status
|
||||
|
||||
|
||||
def get_scodoc_version():
|
||||
"return a string identifying ScoDoc version"
|
||||
return sco_version.SCOVERSION
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* # -*- mode: css -*-
|
||||
ScoDoc, (c) Emmanuel Viennet 1998 - 2020
|
||||
ScoDoc, (c) Emmanuel Viennet 1998 - 2021
|
||||
*/
|
||||
|
||||
html,body {
|
||||
@ -1192,6 +1192,7 @@ table.formsemestre_status {
|
||||
tr.formsemestre_status { background-color: rgb(90%,90%,90%); }
|
||||
tr.formsemestre_status_green { background-color: #EFF7F2; }
|
||||
tr.formsemestre_status_ue { background-color: rgb(90%,90%,90%); }
|
||||
tr.formsemestre_status_cat td { padding-top: 2ex;}
|
||||
table.formsemestre_status td {
|
||||
border-top: 1px solid rgb(80%,80%,80%);
|
||||
border-bottom: 1px solid rgb(80%,80%,80%);
|
||||
@ -1236,6 +1237,7 @@ td.formsemestre_status_cell {
|
||||
|
||||
span.status_ue_acro { font-weight: bold; }
|
||||
span.status_ue_title { font-style: italic; padding-left: 1cm;}
|
||||
span.status_module_cat { font-weight: bold; }
|
||||
|
||||
table.formsemestre_inscr td {
|
||||
padding-right: 1.25em;
|
||||
@ -1268,11 +1270,18 @@ ul.ue_inscr_list li.etud {
|
||||
border-spacing: 1px;
|
||||
}
|
||||
|
||||
/* Modules */
|
||||
/* Tableau de bord module */
|
||||
div.moduleimpl_tableaubord {
|
||||
padding: 7px;
|
||||
border: 2px solid gray;
|
||||
}
|
||||
div.moduleimpl_type_sae {
|
||||
background-color:#cfeccf;
|
||||
}
|
||||
div.moduleimpl_type_ressource {
|
||||
background-color:#f5e9d2;
|
||||
}
|
||||
|
||||
span.moduleimpl_abs_link {
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
93
app/static/css/table_editor.css
Normal file
93
app/static/css/table_editor.css
Normal file
@ -0,0 +1,93 @@
|
||||
/* table_editor, par Sébastien L.
|
||||
*/
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
/***************************/
|
||||
/* Le tableau */
|
||||
/***************************/
|
||||
.tableau {
|
||||
display: grid;
|
||||
grid-auto-rows: minmax(24px, auto);
|
||||
}
|
||||
|
||||
.entete {
|
||||
background: #09c;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tableau>div {
|
||||
padding: 4px;
|
||||
border: 1px solid #999;
|
||||
grid-column: var(--x) / span var(--nbX);
|
||||
grid-row: var(--y) / span var(--nbY);
|
||||
}
|
||||
|
||||
/***************************/
|
||||
/* Attente */
|
||||
/***************************/
|
||||
.wait {
|
||||
position: fixed;
|
||||
top: 32px;
|
||||
left: 50%;
|
||||
height: 4px;
|
||||
width: 32px;
|
||||
margin-left: -16px;
|
||||
background: #424242;
|
||||
animation: attente 0.4s infinite alternate;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.go {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes attente {
|
||||
100% {
|
||||
transform: translateY(-16px) rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/***************************/
|
||||
/* Système de modification */
|
||||
/***************************/
|
||||
.modifOnOff {
|
||||
position: relative;
|
||||
display: table;
|
||||
margin: 16px;
|
||||
padding-right: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modifOnOff::before {
|
||||
content: '';
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
background: #c90;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 100%;
|
||||
border-radius: 20px;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.modifOnOff::after {
|
||||
content: '';
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: #FFF;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: calc(100% + 2px);
|
||||
border-radius: 100%;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.modifOnOff.active::before {
|
||||
background: #9c0;
|
||||
}
|
||||
|
||||
.modifOnOff.active::after {
|
||||
transform: translateX(20px);
|
||||
}
|
56
app/static/js/table_editor.js
Normal file
56
app/static/js/table_editor.js
Normal file
@ -0,0 +1,56 @@
|
||||
/* table_editor, par Sébastien L.
|
||||
*/
|
||||
|
||||
/******************************/
|
||||
/* Gestion de la modification */
|
||||
/******************************/
|
||||
|
||||
function editableOnOff() {
|
||||
if (this.classList.toggle("active")) {
|
||||
document.querySelectorAll("[data-editable=true]").forEach(cellule => {
|
||||
cellule.contentEditable = true;
|
||||
cellule.addEventListener("input", delayBeforeSave);
|
||||
cellule.addEventListener("blur", save);
|
||||
})
|
||||
} else {
|
||||
document.querySelectorAll("[data-editable=true]").forEach(cellule => {
|
||||
cellule.removeAttribute("contentEditable");
|
||||
cellule.removeEventListener("input", delayBeforeSave);
|
||||
cellule.removeEventListener("blur", save);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let timeout = 0;
|
||||
|
||||
function delayBeforeSave() {
|
||||
clearTimeout(timeout);
|
||||
document.querySelector(".wait").classList.add("go");
|
||||
timeout = setTimeout(() => { save(this) }, 2000);
|
||||
}
|
||||
|
||||
/*****************************/
|
||||
/* Mise en place des données */
|
||||
/*****************************/
|
||||
function build_table(data) {
|
||||
let output = "";
|
||||
|
||||
data.forEach((cellule) => {
|
||||
output += `
|
||||
<div
|
||||
class="${cellule.style}"
|
||||
data-editable="${cellule.editable || "false"}"
|
||||
data-module_id="${cellule.module_id}"
|
||||
data-ue_id="${cellule.ue_id}"
|
||||
style="
|
||||
--x:${cellule.x};
|
||||
--y:${cellule.y};
|
||||
--nbX:${cellule.nbX};
|
||||
--nbY: ${cellule.nbY};
|
||||
">
|
||||
${cellule.data}
|
||||
</div>`;
|
||||
})
|
||||
document.querySelector(".tableau").innerHTML = output;
|
||||
document.querySelector(".modifOnOff").addEventListener("click", editableOnOff);
|
||||
}
|
55
app/templates/pn/form_modules_ue_coefs.html
Normal file
55
app/templates/pn/form_modules_ue_coefs.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="/ScoDoc/static/jQuery/jquery.js"></script>
|
||||
<script src="/ScoDoc/static/js/table_editor.js"></script>
|
||||
<link href="/ScoDoc/static/css/table_editor.css" rel="stylesheet" type="text/css" />
|
||||
<title>Édition coef. formation</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Formation {{formation.titre}} ({{formation.acronyme}})
|
||||
[version {{formation.version}}] code {{formation.code}}</h2>
|
||||
|
||||
<div class=wait></div>
|
||||
<div class=modifOnOff>Modifier</div>
|
||||
<div class="tableau"></div>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
$.getJSON("{{data_source}}", function (data) {
|
||||
build_table(data);
|
||||
});
|
||||
});
|
||||
function save(obj) {
|
||||
if (event?.currentTarget) {
|
||||
obj = event.currentTarget;
|
||||
}
|
||||
document.querySelector(".wait").classList.remove("go");
|
||||
// XXX DEBUG
|
||||
console.log(`
|
||||
x : ${getComputedStyle(obj).getPropertyValue("--x")}
|
||||
y : ${getComputedStyle(obj).getPropertyValue("--y")}
|
||||
data : ${obj.innerText}
|
||||
ue_id: ${obj.dataset.ue_id}
|
||||
module_id : ${obj.dataset.module_id}
|
||||
`);
|
||||
$.post("{{data_save}}",
|
||||
{
|
||||
module_id: `${obj.dataset.module_id}`,
|
||||
ue_id: `${obj.dataset.ue_id}`,
|
||||
coef: `${obj.innerText}`
|
||||
},
|
||||
function (result) {
|
||||
console.log("enregistré");
|
||||
}
|
||||
);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
53
app/templates/scodoc/help/evaluations.html
Normal file
53
app/templates/scodoc/help/evaluations.html
Normal file
@ -0,0 +1,53 @@
|
||||
<div class="help">
|
||||
<p class="help">
|
||||
Le coefficient d'une évaluation est utilisé 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.
|
||||
</p>
|
||||
{%if is_apc%}
|
||||
<p class="help help_but">
|
||||
Dans le BUT, une évaluation peut évaluer différents apprentissages critiques... (à compléter)
|
||||
Le coefficient est multiplié par les poids vers chaque UE.
|
||||
</p>
|
||||
{%endif%}
|
||||
<p class="help">
|
||||
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.
|
||||
</p>
|
||||
<p class="help">
|
||||
L'option <em>Visible sur bulletins</em> 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).
|
||||
</p>
|
||||
<p class="help">
|
||||
Les modalités "rattrapage" et "deuxième session" définissent des
|
||||
évaluations prises en compte de façon spéciale:
|
||||
</p>
|
||||
<ul>
|
||||
<li>les notes d'une évaluation de "rattrapage" remplaceront les moyennes
|
||||
du module <em>si elles sont meilleures que celles calculées</em>.
|
||||
</li>
|
||||
<li>les notes de "deuxième session" remplacent, lorsqu'elles sont
|
||||
saisies, la moyenne de l'étudiant à ce module, même si la note de
|
||||
deuxième session est plus faible.
|
||||
</li>
|
||||
</ul>
|
||||
<p class="help">
|
||||
Dans ces deux cas, le coefficient est ignoré, et toutes les notes n'ont
|
||||
pas besoin d'être rentrées.
|
||||
</p>
|
||||
<p class="help">
|
||||
Par ailleurs, les évaluations des modules de type "<b>malus</b>" 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).
|
||||
</p>
|
||||
</div>
|
27
app/templates/scodoc/help/modules.html
Normal file
27
app/templates/scodoc/help/modules.html
Normal file
@ -0,0 +1,27 @@
|
||||
<div class="help">
|
||||
<p class="help">
|
||||
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 <em>évaluations</em>. <br />
|
||||
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 <em>coefficient</em> associé au module.
|
||||
</p>
|
||||
|
||||
<p class="help">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, et le responsable du semestre, pourront
|
||||
saisir et modifier les notes de ce module.
|
||||
</p>
|
||||
{%if is_apc%}
|
||||
<p class="help help_but">
|
||||
Dans le BUT, les modules peuvent être de type "ressource" ou "Situation
|
||||
d'apprentissage et d'évaluation" (SAÉ). Ne pas oublier de préciser le
|
||||
type, et de saisir les coefficients pondérant l'influence de la
|
||||
ressource ou SAÉ vers les Unités d'Enseignement (UE).
|
||||
Voir les détails sur
|
||||
<a href="https://scodoc.org/BUT" target="_blank">la documentation</a>.
|
||||
</p>
|
||||
{%endif%}
|
||||
</div>
|
@ -16,7 +16,7 @@ notes_bp = Blueprint("notes", __name__)
|
||||
users_bp = Blueprint("users", __name__)
|
||||
absences_bp = Blueprint("absences", __name__)
|
||||
|
||||
from app.views import scodoc, notes, scolar, absences, users
|
||||
from app.views import scodoc, notes, scolar, absences, users, pn_modules
|
||||
|
||||
|
||||
# Cette fonction est bien appelée avant toutes les requêtes
|
||||
|
@ -83,7 +83,6 @@ from app.scodoc import sco_archives
|
||||
from app.scodoc import sco_bulletins
|
||||
from app.scodoc import sco_bulletins_pdf
|
||||
from app.scodoc import sco_cache
|
||||
from app.scodoc import sco_codes_parcours
|
||||
from app.scodoc import sco_compute_moy
|
||||
from app.scodoc import sco_cost_formation
|
||||
from app.scodoc import sco_debouche
|
||||
@ -94,7 +93,8 @@ from app.scodoc import sco_edit_ue
|
||||
from app.scodoc import sco_etape_apogee_view
|
||||
from app.scodoc import sco_etud
|
||||
from app.scodoc import sco_evaluations
|
||||
from app.scodoc import sco_excel
|
||||
from app.scodoc import sco_evaluation_db
|
||||
from app.scodoc import sco_evaluation_edit
|
||||
from app.scodoc import sco_export_results
|
||||
from app.scodoc import sco_formations
|
||||
from app.scodoc import sco_formsemestre
|
||||
@ -306,12 +306,12 @@ sco_publish(
|
||||
)
|
||||
sco_publish(
|
||||
"/module_evaluation_renumber",
|
||||
sco_evaluations.module_evaluation_renumber,
|
||||
sco_evaluation_db.module_evaluation_renumber,
|
||||
Permission.ScoView,
|
||||
)
|
||||
sco_publish(
|
||||
"/module_evaluation_move",
|
||||
sco_evaluations.module_evaluation_move,
|
||||
sco_evaluation_db.module_evaluation_move,
|
||||
Permission.ScoView,
|
||||
)
|
||||
sco_publish(
|
||||
@ -354,7 +354,7 @@ def ue_table(formation_id=None, msg=""):
|
||||
@scodoc7func
|
||||
def ue_set_internal(ue_id):
|
||||
""""""
|
||||
ue = models.formations.NotesUE.query.get(ue_id)
|
||||
ue = models.formations.UniteEns.query.get(ue_id)
|
||||
if not ue:
|
||||
raise ScoValueError("invalid ue_id")
|
||||
ue.is_external = False
|
||||
@ -1557,7 +1557,7 @@ sco_publish(
|
||||
@scodoc7func
|
||||
def evaluation_delete(evaluation_id):
|
||||
"""Form delete evaluation"""
|
||||
El = sco_evaluations.do_evaluation_list(args={"evaluation_id": evaluation_id})
|
||||
El = sco_evaluation_db.do_evaluation_list(args={"evaluation_id": evaluation_id})
|
||||
if not El:
|
||||
raise ValueError("Evalution inexistante ! (%s)" % evaluation_id)
|
||||
E = El[0]
|
||||
@ -1630,7 +1630,7 @@ def evaluation_delete(evaluation_id):
|
||||
|
||||
sco_publish(
|
||||
"/do_evaluation_list",
|
||||
sco_evaluations.do_evaluation_list,
|
||||
sco_evaluation_db.do_evaluation_list,
|
||||
Permission.ScoView,
|
||||
)
|
||||
|
||||
@ -1641,7 +1641,7 @@ sco_publish(
|
||||
@scodoc7func
|
||||
def evaluation_edit(evaluation_id):
|
||||
"form edit evaluation"
|
||||
return sco_evaluations.evaluation_create_form(
|
||||
return sco_evaluation_edit.evaluation_create_form(
|
||||
evaluation_id=evaluation_id, edit=True
|
||||
)
|
||||
|
||||
@ -1652,7 +1652,7 @@ def evaluation_edit(evaluation_id):
|
||||
@scodoc7func
|
||||
def evaluation_create(moduleimpl_id):
|
||||
"form create evaluation"
|
||||
return sco_evaluations.evaluation_create_form(
|
||||
return sco_evaluation_edit.evaluation_create_form(
|
||||
moduleimpl_id=moduleimpl_id, edit=False
|
||||
)
|
||||
|
||||
|
171
app/views/pn_modules.py
Normal file
171
app/views/pn_modules.py
Normal file
@ -0,0 +1,171 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# ScoDoc
|
||||
#
|
||||
# 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
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""
|
||||
PN / Edition des coefs
|
||||
|
||||
Emmanuel Viennet, 2021
|
||||
"""
|
||||
|
||||
from flask import url_for
|
||||
from flask import jsonify
|
||||
from flask import current_app, g, request
|
||||
from flask.templating import render_template
|
||||
from flask_login import current_user
|
||||
from werkzeug.utils import redirect
|
||||
|
||||
from config import Config
|
||||
|
||||
from app import db
|
||||
from app import models
|
||||
from app.auth.models import User
|
||||
|
||||
from app.comp import moy_ue
|
||||
from app.decorators import scodoc, permission_required
|
||||
from app.scodoc import sco_edit_formation
|
||||
from app.views import notes_bp as bp
|
||||
|
||||
# ---------------
|
||||
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc import notesdb as ndb
|
||||
from app import log
|
||||
|
||||
from app.models.formations import Formation, UniteEns, Module
|
||||
from app.scodoc.sco_exceptions import (
|
||||
ScoValueError,
|
||||
ScoLockedFormError,
|
||||
ScoGenError,
|
||||
AccessDenied,
|
||||
)
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
|
||||
|
||||
@bp.route("/table_modules_ue_coefs/<formation_id>")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def table_modules_ue_coefs(formation_id):
|
||||
"""Description JSON de la table des coefs modules/UE dans une formation"""
|
||||
_ = models.Formation.query.get_or_404(formation_id) # check
|
||||
|
||||
df = moy_ue.df_load_ue_coefs(formation_id)
|
||||
ues = models.UniteEns.query.filter_by(formation_id=formation_id).all()
|
||||
modules = models.Module.query.filter_by(formation_id=formation_id).all()
|
||||
# Titre des modules, en ligne
|
||||
col_titres_mods = [
|
||||
{
|
||||
"x": 1, # 1ere colonne
|
||||
"y": row,
|
||||
# "nbX": 1,
|
||||
# "nbY": 1,
|
||||
"style": "title_mod " + scu.ModuleType(mod.module_type).name,
|
||||
"data": mod.code,
|
||||
"title": mod.titre,
|
||||
}
|
||||
for (row, mod) in enumerate(modules, start=2)
|
||||
]
|
||||
row_titres_ue = [
|
||||
{
|
||||
"x": col,
|
||||
"y": 1, # 1ere ligne
|
||||
"nbX": 1,
|
||||
"nbY": 1,
|
||||
"style": "title_ue",
|
||||
"data": ue.acronyme,
|
||||
"title": ue.titre,
|
||||
}
|
||||
for (col, ue) in enumerate(ues, start=2)
|
||||
]
|
||||
# Les champs de saisie
|
||||
cells = []
|
||||
for (row, mod) in enumerate(modules, start=2):
|
||||
for (col, ue) in enumerate(ues, start=2):
|
||||
cells.append(
|
||||
{
|
||||
"x": col,
|
||||
"y": row,
|
||||
"style": "champs",
|
||||
"data": df[ue.id][mod.id],
|
||||
"editable": True,
|
||||
"module_id": mod.id,
|
||||
"ue_id": ue.id,
|
||||
}
|
||||
)
|
||||
return jsonify(col_titres_mods + row_titres_ue + cells)
|
||||
|
||||
|
||||
@bp.route("/set_module_ue_coef", methods=["POST"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoChangeFormation)
|
||||
def set_module_ue_coef():
|
||||
"""Set coef from module to UE"""
|
||||
try:
|
||||
module_id = int(request.form["module_id"])
|
||||
except ValueError:
|
||||
return scu.json_error("invalid module_id", 400)
|
||||
try:
|
||||
ue_id = int(request.form["ue_id"])
|
||||
except ValueError:
|
||||
return scu.json_error("invalid ue_id", 400)
|
||||
try:
|
||||
coef = float(request.form["coef"].replace(",", "."))
|
||||
except ValueError:
|
||||
return scu.json_error("invalid coef", 400)
|
||||
module = models.Module.query.get(module_id)
|
||||
if module is None:
|
||||
return scu.json_error(f"module not found ({module_id})", 404)
|
||||
ue = models.UniteEns.query.get(ue_id)
|
||||
if not ue:
|
||||
return scu.json_error(f"UE not found ({ue_id})", 404)
|
||||
module.set_ue_coef(ue, coef)
|
||||
db.session.commit()
|
||||
sco_edit_formation.invalidate_sems_in_formation(module.formation_id)
|
||||
return scu.json_error("ok", success=True, status=201)
|
||||
|
||||
|
||||
@bp.route("/edit_modules_ue_coefs/<formation_id>")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoChangeFormation)
|
||||
def edit_modules_ue_coefs(formation_id):
|
||||
"""Formulaire édition grille coefs EU/modules"""
|
||||
formation = models.Formation.query.filter_by(
|
||||
formation_id=formation_id
|
||||
).first_or_404()
|
||||
return render_template(
|
||||
"pn/form_modules_ue_coefs.html",
|
||||
formation=formation,
|
||||
data_source=url_for(
|
||||
"notes.table_modules_ue_coefs",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formation_id=formation_id,
|
||||
),
|
||||
data_save=url_for(
|
||||
"notes.set_module_ue_coef",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
),
|
||||
)
|
@ -49,7 +49,7 @@ from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
|
||||
|
||||
import app
|
||||
from app.models import Departement, Identite
|
||||
from app.models import FormSemestre, NotesFormsemestreInscription
|
||||
from app.models import FormSemestre, FormsemestreInscription
|
||||
from app.models import ScoDocSiteConfig
|
||||
import sco_version
|
||||
from app.scodoc import sco_logos
|
||||
@ -124,9 +124,7 @@ def get_etud_dept():
|
||||
last_etud = None
|
||||
last_date = None
|
||||
for etud in etuds:
|
||||
inscriptions = NotesFormsemestreInscription.query.filter_by(
|
||||
etudid=etud.id
|
||||
).all()
|
||||
inscriptions = FormsemestreInscription.query.filter_by(etudid=etud.id).all()
|
||||
for ins in inscriptions:
|
||||
date_fin = FormSemestre.query.get(ins.formsemestre_id).date_fin
|
||||
if (last_date is None) or date_fin > last_date:
|
||||
|
35
migrations/versions/6cfc21a7ae1b_coefs_modules_but.py
Normal file
35
migrations/versions/6cfc21a7ae1b_coefs_modules_but.py
Normal file
@ -0,0 +1,35 @@
|
||||
"""coefs modules but
|
||||
|
||||
Revision ID: 6cfc21a7ae1b
|
||||
Revises: ada0d1f3d84f
|
||||
Create Date: 2021-11-11 21:04:05.573172
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '6cfc21a7ae1b'
|
||||
down_revision = 'ada0d1f3d84f'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('module_ue_coef',
|
||||
sa.Column('module_id', sa.Integer(), nullable=False),
|
||||
sa.Column('ue_id', sa.Integer(), nullable=False),
|
||||
sa.Column('coef', sa.Float(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['module_id'], ['notes_modules.id'], ),
|
||||
sa.ForeignKeyConstraint(['ue_id'], ['notes_ue.id'], ),
|
||||
sa.PrimaryKeyConstraint('module_id', 'ue_id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('module_ue_coef')
|
||||
# ### end Alembic commands ###
|
@ -37,8 +37,10 @@ lazy-object-proxy==1.6.0
|
||||
Mako==1.1.4
|
||||
MarkupSafe==2.0.1
|
||||
mccabe==0.6.1
|
||||
numpy==1.21.4
|
||||
openpyxl==3.0.7
|
||||
packaging==21.0
|
||||
pandas==1.3.4
|
||||
Pillow==8.3.1
|
||||
pluggy==0.13.1
|
||||
psycopg2==2.9.1
|
||||
@ -46,7 +48,6 @@ py==1.10.0
|
||||
pycparser==2.20
|
||||
pydot==1.4.2
|
||||
PyJWT==2.1.0
|
||||
pylint==2.9.6
|
||||
pyOpenSSL==20.0.1
|
||||
pyparsing==2.4.7
|
||||
pytest==6.2.4
|
||||
@ -58,8 +59,10 @@ redis==3.5.3
|
||||
reportlab==3.6.1
|
||||
requests==2.26.0
|
||||
rq==1.9.0
|
||||
six==1.16.0
|
||||
SQLAlchemy==1.4.22
|
||||
toml==0.10.2
|
||||
tornado==6.1
|
||||
urllib3==1.26.6
|
||||
visitor==0.1.3
|
||||
Werkzeug==2.0.1
|
||||
|
@ -17,7 +17,7 @@ import typing
|
||||
|
||||
from config import Config
|
||||
from app.auth.models import User
|
||||
from app.models import NotesFormModalite
|
||||
from app.models import FormationModalite
|
||||
from app.scodoc import notesdb as ndb
|
||||
from app.scodoc import sco_codes_parcours
|
||||
from app.scodoc import sco_edit_formation
|
||||
@ -26,6 +26,7 @@ from app.scodoc import sco_edit_module
|
||||
from app.scodoc import sco_edit_ue
|
||||
from app.scodoc import sco_etud
|
||||
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_inscriptions
|
||||
@ -222,7 +223,7 @@ class ScoFake(object):
|
||||
block_moyennes=None,
|
||||
gestion_semestrielle=None,
|
||||
bul_bgcolor=None,
|
||||
modalite=NotesFormModalite.DEFAULT_MODALITE,
|
||||
modalite=FormationModalite.DEFAULT_MODALITE,
|
||||
resp_can_edit=None,
|
||||
resp_can_change_ens=None,
|
||||
ens_can_edit_eval=None,
|
||||
@ -283,8 +284,8 @@ class ScoFake(object):
|
||||
):
|
||||
args = locals()
|
||||
del args["self"]
|
||||
oid = sco_evaluations.do_evaluation_create(**args)
|
||||
oids = sco_evaluations.do_evaluation_list(args={"evaluation_id": oid})
|
||||
oid = sco_evaluation_db.do_evaluation_create(**args)
|
||||
oids = sco_evaluation_db.do_evaluation_list(args={"evaluation_id": oid})
|
||||
if not oids:
|
||||
raise ScoValueError("evaluation not created !")
|
||||
return oids[0]
|
||||
|
@ -15,8 +15,7 @@ login_user(admin_user)
|
||||
"""
|
||||
|
||||
|
||||
def test_evaluation_poids(test_client):
|
||||
"""Association de poids vers les UE"""
|
||||
def setup_formation_test():
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
_f = G.create_formation(
|
||||
acronyme="F3", titre="Formation 2", titre_officiel="Titre officiel 2"
|
||||
@ -33,14 +32,20 @@ def test_evaluation_poids(test_client):
|
||||
ue_id=_ue1["ue_id"],
|
||||
formation_id=_f["formation_id"],
|
||||
)
|
||||
return G, _f["id"], _ue1["id"], _ue2["id"], _ue3["id"], _mod["id"]
|
||||
|
||||
|
||||
def test_evaluation_poids(test_client):
|
||||
"""Association de poids vers les UE"""
|
||||
G, formation_id, ue1_id, ue2_id, ue3_id, module_id = setup_formation_test()
|
||||
sem = G.create_formsemestre(
|
||||
formation_id=_f["formation_id"],
|
||||
formation_id=formation_id,
|
||||
semestre_id=1,
|
||||
date_debut="01/01/2021",
|
||||
date_fin="30/06/2021",
|
||||
) # formsemestre_id=716
|
||||
mi = G.create_moduleimpl(
|
||||
module_id=_mod["module_id"],
|
||||
module_id=module_id,
|
||||
formsemestre_id=sem["formsemestre_id"],
|
||||
)
|
||||
moduleimpl_id = mi["id"]
|
||||
@ -51,17 +56,17 @@ def test_evaluation_poids(test_client):
|
||||
coefficient=0,
|
||||
)
|
||||
evaluation_id = _e1["evaluation_id"] # evaluation_id=25246
|
||||
ue1_id = _ue1["id"] # ue1_id=1684
|
||||
formation_id = _f["id"] # formation_id=199
|
||||
# ue1_id=1684
|
||||
# formation_id=199
|
||||
#
|
||||
e1 = models.NotesEvaluation.query.get(evaluation_id)
|
||||
ue1 = models.NotesUE.query.get(ue1_id)
|
||||
e1 = models.Evaluation.query.get(evaluation_id)
|
||||
ue1 = models.UniteEns.query.get(ue1_id)
|
||||
assert e1.ue_poids == []
|
||||
p1 = 3.14
|
||||
e1.set_ue_poids(ue1, p1)
|
||||
db.session.commit()
|
||||
assert e1.get_ue_poids_dict()[ue1_id] == p1
|
||||
ues = models.NotesUE.query.filter_by(formation_id=formation_id).all()
|
||||
ues = models.UniteEns.query.filter_by(formation_id=formation_id).all()
|
||||
poids = [1.0, 2.0, 3.0]
|
||||
for (ue, p) in zip(ues, poids):
|
||||
e1.set_ue_poids(ue, p)
|
||||
@ -81,3 +86,22 @@ def test_evaluation_poids(test_client):
|
||||
db.session.delete(e1)
|
||||
db.session.commit()
|
||||
assert len(models.EvaluationUEPoids.query.all()) == 0
|
||||
|
||||
|
||||
def test_modules_coefs(test_client):
|
||||
"""Coefs vers les UE (BUT)"""
|
||||
G, formation_id, ue1_id, ue2_id, ue3_id, module_id = setup_formation_test()
|
||||
ue1 = models.UniteEns.query.get(ue1_id)
|
||||
ue2 = models.UniteEns.query.get(ue2_id)
|
||||
mod = models.Module.query.get(module_id)
|
||||
coef = 2.5
|
||||
mod.set_ue_coef(ue1, coef)
|
||||
db.session.commit()
|
||||
assert mod.ue_coefs[0].coef == coef
|
||||
mod.set_ue_coef(ue2, 2 * coef)
|
||||
db.session.commit()
|
||||
assert set(mod.get_ue_coef_dict().values()) == {coef, 2 * coef}
|
||||
assert set(mod.get_ue_coef_dict().keys()) == {ue1_id, ue2_id}
|
||||
mod.delete_ue_coef(ue1)
|
||||
db.session.commit()
|
||||
assert len(mod.ue_coefs) == 1
|
||||
|
@ -15,6 +15,7 @@ import app
|
||||
from app import db
|
||||
from app.scodoc import sco_cache
|
||||
from app.scodoc import sco_evaluations
|
||||
from app.scodoc import sco_evaluation_db
|
||||
from app.scodoc import sco_formsemestre
|
||||
from app.scodoc import notesdb as ndb
|
||||
from config import TestConfig
|
||||
@ -66,7 +67,7 @@ def test_cache_evaluations(test_client):
|
||||
raise Exception("no evaluations")
|
||||
#
|
||||
evaluation_id = sem_evals[0]["evaluation_id"]
|
||||
eval_notes = sco_evaluations.do_evaluation_get_all_notes(evaluation_id)
|
||||
eval_notes = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
|
||||
# should have been be cached, except if empty
|
||||
if eval_notes:
|
||||
assert sco_cache.EvaluationCache.get(evaluation_id)
|
||||
|
@ -25,6 +25,7 @@ from app.scodoc import sco_abs_views
|
||||
from app.scodoc import sco_bulletins
|
||||
from app.scodoc import sco_codes_parcours
|
||||
from app.scodoc import sco_evaluations
|
||||
from app.scodoc import sco_evaluation_db
|
||||
from app.scodoc import sco_formsemestre_validation
|
||||
from app.scodoc import sco_parcours_dut
|
||||
from app.scodoc import sco_cache
|
||||
@ -134,7 +135,7 @@ def run_sco_basic(verbose=False):
|
||||
|
||||
# Modifie l'évaluation 2 pour "prise en compte immédiate"
|
||||
e2["publish_incomplete"] = True
|
||||
sco_evaluations.do_evaluation_edit(e2)
|
||||
sco_evaluation_db.do_evaluation_edit(e2)
|
||||
etat = sco_evaluations.do_evaluation_etat(e2["evaluation_id"])
|
||||
assert etat["evalcomplete"] == False
|
||||
assert etat["nb_att"] == 0 # il n'y a pas de notes (explicitement) en attente
|
||||
|
Loading…
Reference in New Issue
Block a user