forked from ScoDoc/ScoDoc
Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
14ab816bee | |||
4182fd494c | |||
f6b2297bd3 | |||
1488689bfb | |||
459e75db89 | |||
477c2efac9 | |||
c0dd83fadb | |||
89e7250f4a | |||
d71b399c3d | |||
0e6513e339 | |||
139eb8171a | |||
94325cef2c | |||
3bb9a5cb76 |
@ -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,35 +31,42 @@ 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,
|
||||
notes_modules_enseignants,
|
||||
NotesModuleImplInscription,
|
||||
NotesEvaluation,
|
||||
NotesSemSet,
|
||||
notes_semset_formsemestre,
|
||||
)
|
||||
from app.models.moduleimpls import (
|
||||
ModuleImpl,
|
||||
notes_modules_enseignants,
|
||||
ModuleImplInscription,
|
||||
)
|
||||
from app.models.evaluations import (
|
||||
Evaluation,
|
||||
EvaluationUEPoids,
|
||||
)
|
||||
from app.models.but_pn import AppCrit
|
||||
from app.models.groups import Partition, GroupDescr, group_membership
|
||||
from app.models.notes import (
|
||||
ScolarEvent,
|
||||
ScolarFormsemestreValidation,
|
||||
ScolarAutorisationInscription,
|
||||
NotesAppreciations,
|
||||
BulAppreciations,
|
||||
NotesNotes,
|
||||
NotesNotesLog,
|
||||
)
|
||||
|
@ -1,4 +1,41 @@
|
||||
"""ScoDoc 9 models : Formation BUT 2021
|
||||
"""
|
||||
from enum import unique
|
||||
from typing import Any
|
||||
|
||||
# insérer ici idk
|
||||
from app import db
|
||||
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
|
||||
Modules_ACs = db.Table(
|
||||
"modules_acs",
|
||||
db.Column("module_id", db.ForeignKey("notes_modules.id")),
|
||||
db.Column("ac_id", db.ForeignKey("app_crit.id")),
|
||||
)
|
||||
|
||||
|
||||
class AppCrit(db.Model):
|
||||
"Apprentissage Critique BUT"
|
||||
__tablename__ = "app_crit"
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
code = db.Column(db.Text(), nullable=False, info={"label": "Code"})
|
||||
titre = db.Column(db.Text(), info={"label": "Titre"})
|
||||
|
||||
modules = db.relationship(
|
||||
"Module", secondary=Modules_ACs, lazy="dynamic", backref="acs"
|
||||
)
|
||||
|
||||
def to_dict(self):
|
||||
result = dict(self.__dict__)
|
||||
result.pop("_sa_instance_state", None)
|
||||
return result
|
||||
|
||||
def get_label(self):
|
||||
return self.code + " - " + self.titre
|
||||
|
||||
def __repr__(self):
|
||||
return "<AC {}>".format(self.code)
|
||||
|
||||
def get_saes(self):
|
||||
"""Liste des SAE associées"""
|
||||
return [m for m in self.modules if m.module_type == ModuleType.SAE]
|
||||
|
@ -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"
|
||||
)
|
||||
|
123
app/models/evaluations.py
Normal file
123
app/models/evaluations.py
Normal file
@ -0,0 +1,123 @@
|
||||
# -*- coding: UTF-8 -*
|
||||
|
||||
"""ScoDoc models: evaluations
|
||||
"""
|
||||
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.models import CODE_STR_LEN
|
||||
from app.models import UniteEns
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app.scodoc import sco_evaluation_db
|
||||
|
||||
|
||||
class Evaluation(db.Model):
|
||||
"""Evaluation (contrôle, examen, ...)"""
|
||||
|
||||
__tablename__ = "notes_evaluation"
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
evaluation_id = db.synonym("id")
|
||||
moduleimpl_id = db.Column(
|
||||
db.Integer, db.ForeignKey("notes_moduleimpl.id"), index=True
|
||||
)
|
||||
jour = db.Column(db.Date)
|
||||
heure_debut = db.Column(db.Time)
|
||||
heure_fin = db.Column(db.Time)
|
||||
description = db.Column(db.Text)
|
||||
note_max = db.Column(db.Float)
|
||||
coefficient = db.Column(db.Float)
|
||||
visibulletin = db.Column(
|
||||
db.Boolean, nullable=False, default=True, server_default="true"
|
||||
)
|
||||
publish_incomplete = db.Column(
|
||||
db.Boolean, nullable=False, default=False, server_default="false"
|
||||
)
|
||||
# type d'evaluation: 0 normale, 1 rattrapage, 2 "2eme session"
|
||||
evaluation_type = db.Column(
|
||||
db.Integer, nullable=False, default=0, server_default="0"
|
||||
)
|
||||
# ordre de presentation (par défaut, le plus petit numero
|
||||
# est la plus ancienne eval):
|
||||
numero = db.Column(db.Integer)
|
||||
ues = db.relationship("UniteEns", secondary="evaluation_ue_poids", viewonly=True)
|
||||
|
||||
def 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"""
|
||||
self.update_ue_poids_dict({ue.id: poids})
|
||||
|
||||
def set_ue_poids_dict(self, ue_poids_dict: dict):
|
||||
"""set poids vers les UE (remplace existants)
|
||||
ue_poids_dict = { ue_id : poids }
|
||||
"""
|
||||
L = []
|
||||
for ue_id, poids in ue_poids_dict.items():
|
||||
ue = UniteEns.query.get(ue_id)
|
||||
L.append(EvaluationUEPoids(evaluation=self, ue=ue, poids=poids))
|
||||
self.ue_poids = L
|
||||
|
||||
def update_ue_poids_dict(self, ue_poids_dict: dict):
|
||||
"""update poids vers UE (ajoute aux existants)"""
|
||||
current = self.get_ue_poids_dict()
|
||||
current.update(ue_poids_dict)
|
||||
self.set_ue_poids_dict(current)
|
||||
|
||||
def get_ue_poids_dict(self):
|
||||
"""returns { ue_id : poids }"""
|
||||
return {p.ue.id: p.poids for p in self.ue_poids}
|
||||
|
||||
|
||||
class EvaluationUEPoids(db.Model):
|
||||
"""Poids des évaluations (BUT)
|
||||
association many to many
|
||||
"""
|
||||
|
||||
evaluation_id = db.Column(
|
||||
db.Integer, db.ForeignKey("notes_evaluation.id"), primary_key=True
|
||||
)
|
||||
ue_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), primary_key=True)
|
||||
poids = db.Column(
|
||||
db.Float,
|
||||
nullable=False,
|
||||
)
|
||||
evaluation = db.relationship(
|
||||
Evaluation,
|
||||
backref=db.backref("ue_poids", cascade="all, delete-orphan"),
|
||||
)
|
||||
ue = db.relationship(
|
||||
UniteEns,
|
||||
backref=db.backref("evaluation_ue_poids", cascade="all, delete-orphan"),
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<EvaluationUEPoids {self.evaluation} {self.ue} poids={self.poids}>"
|
@ -5,9 +5,12 @@ 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
|
||||
from app.scodoc import sco_codes_parcours
|
||||
|
||||
|
||||
class NotesFormation(db.Model):
|
||||
class Formation(db.Model):
|
||||
"""Programme pédagogique d'une formation"""
|
||||
|
||||
__tablename__ = "notes_formations"
|
||||
@ -30,16 +33,20 @@ 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}')>"
|
||||
|
||||
def get_parcours(self):
|
||||
"""get l'instance de TypeParcours de cette formation"""
|
||||
return sco_codes_parcours.get_parcours_from_code(self.type_parcours)
|
||||
|
||||
class NotesUE(db.Model):
|
||||
"""Unité d'Enseignement"""
|
||||
|
||||
class UniteEns(db.Model):
|
||||
"""Unité d'Enseignement (UE)"""
|
||||
|
||||
__tablename__ = "notes_ue"
|
||||
|
||||
@ -49,6 +56,11 @@ class NotesUE(db.Model):
|
||||
acronyme = db.Column(db.Text(), nullable=False)
|
||||
numero = db.Column(db.Integer) # ordre de présentation
|
||||
titre = db.Column(db.Text())
|
||||
# Le semestre_idx n'est pas un id mais le numéro du semestre: 1, 2, ...
|
||||
# En ScoDoc7 et pour les formations classiques, il est NULL
|
||||
# (le numéro du semestre étant alors déterminé par celui des modules de l'UE)
|
||||
# Pour les formations APC, il est obligatoire (de 1 à 6 pour le BUT):
|
||||
semestre_idx = db.Column(db.Integer, nullable=True, index=True)
|
||||
# Type d'UE: 0 normal ("fondamentale"), 1 "sport", 2 "projet et stage (LP)",
|
||||
# 4 "élective"
|
||||
type = db.Column(db.Integer, default=0, server_default="0")
|
||||
@ -67,14 +79,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.
|
||||
@ -89,10 +101,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"
|
||||
@ -106,7 +118,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"))
|
||||
@ -116,9 +128,86 @@ class NotesModule(db.Model):
|
||||
numero = db.Column(db.Integer) # ordre de présentation
|
||||
# id de l'element pedagogique Apogee correspondant:
|
||||
code_apogee = db.Column(db.String(APO_CODE_STR_LEN))
|
||||
module_type = db.Column(db.Integer) # NULL ou 0:defaut, 1: malus (NOTES_MALUS)
|
||||
# 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 }
|
||||
Les coefs nuls (zéro) ne sont pas stockés: la relation est supprimée.
|
||||
"""
|
||||
ue_coefs = []
|
||||
for ue_id, coef in ue_coef_dict.items():
|
||||
ue = UniteEns.query.get(ue_id)
|
||||
if coef == 0.0:
|
||||
self.delete_ue_coef(ue)
|
||||
else:
|
||||
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))
|
||||
if ue_coef:
|
||||
db.session.delete(ue_coef)
|
||||
|
||||
def ue_coefs_descr(self):
|
||||
"""List of tuples [ (ue_acronyme, coef) ]"""
|
||||
return [(c.ue.acronyme, c.coef) for c in self.ue_coefs]
|
||||
|
||||
|
||||
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):
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: UTF-8 -*
|
||||
|
||||
"""ScoDoc models
|
||||
"""ScoDoc models: formsemestre
|
||||
"""
|
||||
from typing import Any
|
||||
|
||||
@ -8,12 +8,14 @@ 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 UniteEns
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app.scodoc import sco_evaluation_db
|
||||
|
||||
|
||||
class FormSemestre(db.Model):
|
||||
"""Mise en oeuvre d'un semestre de formation
|
||||
was notes_formsemestre
|
||||
"""
|
||||
"""Mise en oeuvre d'un semestre de formation"""
|
||||
|
||||
__tablename__ = "notes_formsemestre"
|
||||
|
||||
@ -72,11 +74,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
|
||||
@ -85,7 +85,10 @@ 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
|
||||
|
||||
def get_ues(self):
|
||||
"UE des modules de ce semestre"
|
||||
|
||||
|
||||
# Association id des utilisateurs responsables (aka directeurs des etudes) du semestre
|
||||
@ -100,7 +103,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"
|
||||
@ -112,7 +115,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.)
|
||||
"""
|
||||
@ -139,7 +142,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"),
|
||||
@ -150,9 +153,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)
|
||||
@ -163,7 +166,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"
|
||||
@ -182,7 +185,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"
|
||||
@ -202,7 +205,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"
|
||||
@ -218,7 +221,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"
|
||||
@ -238,89 +241,6 @@ class NotesFormsemestreInscription(db.Model):
|
||||
etape = db.Column(db.String(APO_CODE_STR_LEN))
|
||||
|
||||
|
||||
class NotesModuleImpl(db.Model):
|
||||
"""Mise en oeuvre d'un module pour une annee/semestre"""
|
||||
|
||||
__tablename__ = "notes_moduleimpl"
|
||||
__table_args__ = (db.UniqueConstraint("formsemestre_id", "module_id"),)
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
moduleimpl_id = db.synonym("id")
|
||||
module_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey("notes_modules.id"),
|
||||
)
|
||||
formsemestre_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey("notes_formsemestre.id"),
|
||||
index=True,
|
||||
)
|
||||
responsable_id = db.Column("responsable_id", db.Integer, db.ForeignKey("user.id"))
|
||||
# formule de calcul moyenne:
|
||||
computation_expr = db.Column(db.Text())
|
||||
|
||||
|
||||
# Enseignants (chargés de TD ou TP) d'un moduleimpl
|
||||
notes_modules_enseignants = db.Table(
|
||||
"notes_modules_enseignants",
|
||||
db.Column(
|
||||
"moduleimpl_id",
|
||||
db.Integer,
|
||||
db.ForeignKey("notes_moduleimpl.id"),
|
||||
),
|
||||
db.Column("ens_id", db.Integer, db.ForeignKey("user.id")),
|
||||
# ? db.UniqueConstraint("moduleimpl_id", "ens_id"),
|
||||
)
|
||||
# XXX il manque probablement une relation pour gérer cela
|
||||
|
||||
|
||||
class NotesModuleImplInscription(db.Model):
|
||||
"""Inscription à un module (etudiants,moduleimpl)"""
|
||||
|
||||
__tablename__ = "notes_moduleimpl_inscription"
|
||||
__table_args__ = (db.UniqueConstraint("moduleimpl_id", "etudid"),)
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
moduleimpl_inscription_id = db.synonym("id")
|
||||
moduleimpl_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey("notes_moduleimpl.id"),
|
||||
index=True,
|
||||
)
|
||||
etudid = db.Column(db.Integer, db.ForeignKey("identite.id"), index=True)
|
||||
|
||||
|
||||
class NotesEvaluation(db.Model):
|
||||
"""Evaluation (contrôle, examen, ...)"""
|
||||
|
||||
__tablename__ = "notes_evaluation"
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
evaluation_id = db.synonym("id")
|
||||
moduleimpl_id = db.Column(
|
||||
db.Integer, db.ForeignKey("notes_moduleimpl.id"), index=True
|
||||
)
|
||||
jour = db.Column(db.Date)
|
||||
heure_debut = db.Column(db.Time)
|
||||
heure_fin = db.Column(db.Time)
|
||||
description = db.Column(db.Text)
|
||||
note_max = db.Column(db.Float)
|
||||
coefficient = db.Column(db.Float)
|
||||
visibulletin = db.Column(
|
||||
db.Boolean, nullable=False, default=True, server_default="true"
|
||||
)
|
||||
publish_incomplete = db.Column(
|
||||
db.Boolean, nullable=False, default=False, server_default="false"
|
||||
)
|
||||
# type d'evaluation: 0 normale, 1 rattrapage, 2 "2eme session"
|
||||
evaluation_type = db.Column(
|
||||
db.Integer, nullable=False, default=0, server_default="0"
|
||||
)
|
||||
# ordre de presentation (par défaut, le plus petit numero
|
||||
# est la plus ancienne eval):
|
||||
numero = db.Column(db.Integer)
|
||||
|
||||
|
||||
class NotesSemSet(db.Model):
|
||||
"""semsets: ensemble de formsemestres pour exports Apogée"""
|
||||
|
||||
|
66
app/models/moduleimpls.py
Normal file
66
app/models/moduleimpls.py
Normal file
@ -0,0 +1,66 @@
|
||||
# -*- coding: UTF-8 -*
|
||||
|
||||
"""ScoDoc models: moduleimpls
|
||||
"""
|
||||
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.models import CODE_STR_LEN
|
||||
from app.models import UniteEns
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app.scodoc import sco_evaluation_db
|
||||
|
||||
|
||||
class ModuleImpl(db.Model):
|
||||
"""Mise en oeuvre d'un module pour une annee/semestre"""
|
||||
|
||||
__tablename__ = "notes_moduleimpl"
|
||||
__table_args__ = (db.UniqueConstraint("formsemestre_id", "module_id"),)
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
moduleimpl_id = db.synonym("id")
|
||||
module_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey("notes_modules.id"),
|
||||
)
|
||||
formsemestre_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey("notes_formsemestre.id"),
|
||||
index=True,
|
||||
)
|
||||
responsable_id = db.Column("responsable_id", db.Integer, db.ForeignKey("user.id"))
|
||||
# formule de calcul moyenne:
|
||||
computation_expr = db.Column(db.Text())
|
||||
|
||||
|
||||
# Enseignants (chargés de TD ou TP) d'un moduleimpl
|
||||
notes_modules_enseignants = db.Table(
|
||||
"notes_modules_enseignants",
|
||||
db.Column(
|
||||
"moduleimpl_id",
|
||||
db.Integer,
|
||||
db.ForeignKey("notes_moduleimpl.id"),
|
||||
),
|
||||
db.Column("ens_id", db.Integer, db.ForeignKey("user.id")),
|
||||
# ? db.UniqueConstraint("moduleimpl_id", "ens_id"),
|
||||
)
|
||||
# XXX il manque probablement une relation pour gérer cela
|
||||
|
||||
|
||||
class ModuleImplInscription(db.Model):
|
||||
"""Inscription à un module (etudiants,moduleimpl)"""
|
||||
|
||||
__tablename__ = "notes_moduleimpl_inscription"
|
||||
__table_args__ = (db.UniqueConstraint("moduleimpl_id", "etudid"),)
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
moduleimpl_inscription_id = db.synonym("id")
|
||||
moduleimpl_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey("notes_moduleimpl.id"),
|
||||
index=True,
|
||||
)
|
||||
etudid = db.Column(db.Integer, db.ForeignKey("identite.id"), index=True)
|
@ -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(
|
||||
|
@ -51,7 +51,7 @@ def TrivialFormulator(
|
||||
allow_null : if true, field can be left empty (default true)
|
||||
type : 'string', 'int', 'float' (default to string), 'list' (only for hidden)
|
||||
readonly : default False. if True, no form element, display current value.
|
||||
convert_numbers: covert int and float values (from string)
|
||||
convert_numbers: convert int and float values (from string)
|
||||
allowed_values : list of possible values (default: any value)
|
||||
validator : function validating the field (called with (value,field)).
|
||||
min_value : minimum value (for floats and ints)
|
||||
|
@ -40,7 +40,7 @@ from app.scodoc.sco_permissions import Permission
|
||||
def sidebar_common():
|
||||
"partie commune à toutes les sidebar"
|
||||
H = [
|
||||
f"""<a class="scodoc_title" href="{url_for("scodoc.index", scodoc_dept=g.scodoc_dept)}">ScoDoc 9</a>
|
||||
f"""<a class="scodoc_title" href="{url_for("scodoc.index", scodoc_dept=g.scodoc_dept)}">ScoDoc 9.1</a>
|
||||
<div id="authuser"><a id="authuserlink" href="{
|
||||
url_for("users.user_info_page",
|
||||
scodoc_dept=g.scodoc_dept, user_name=current_user.user_name)
|
||||
|
@ -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
|
||||
|
@ -28,8 +28,42 @@
|
||||
"""Semestres: Codes gestion parcours (constantes)
|
||||
"""
|
||||
import collections
|
||||
import enum
|
||||
|
||||
from app import log
|
||||
|
||||
|
||||
@enum.unique
|
||||
class CodesParcours(enum.IntEnum):
|
||||
"""Codes numériques de sparcours, enregistrés en base
|
||||
dans notes_formations.type_parcours
|
||||
Ne pas modifier.
|
||||
"""
|
||||
|
||||
Legacy = 0
|
||||
DUT = 100
|
||||
DUT4 = 110
|
||||
DUTMono = 120
|
||||
DUT2 = 130
|
||||
LP = 200
|
||||
LP2sem = 210
|
||||
LP2semEvry = 220
|
||||
LP2014 = 230
|
||||
LP2sem2014 = 240
|
||||
M2 = 250
|
||||
M2noncomp = 251
|
||||
Mono = 300
|
||||
MasterLMD = 402
|
||||
MasterIG = 403
|
||||
LicenceUCAC3 = 501
|
||||
MasterUCAC2 = 502
|
||||
MonoUCAC = 503
|
||||
GEN_6_SEM = 600
|
||||
BUT = 700
|
||||
ISCID6 = 1001
|
||||
ISCID4 = 1002
|
||||
|
||||
|
||||
NOTES_TOLERANCE = 0.00499999999999 # si note >= (BARRE-TOLERANCE), considere ok
|
||||
# (permet d'eviter d'afficher 10.00 sous barre alors que la moyenne vaut 9.999)
|
||||
|
||||
@ -217,6 +251,7 @@ class TypeParcours(object):
|
||||
ALLOWED_UE_TYPES = list(
|
||||
UE_TYPE_NAME.keys()
|
||||
) # par defaut, autorise tous les types d'UE
|
||||
APC_SAE = False # Approche par compétences avec ressources et SAÉs
|
||||
|
||||
def check(self, formation=None):
|
||||
return True, "" # status, diagnostic_message
|
||||
@ -265,6 +300,20 @@ def register_parcours(Parcours):
|
||||
TYPES_PARCOURS[Parcours.TYPE_PARCOURS] = Parcours
|
||||
|
||||
|
||||
class ParcoursBUT(TypeParcours):
|
||||
"""BUT Bachelor Universitaire de Technologie"""
|
||||
|
||||
TYPE_PARCOURS = 700
|
||||
NAME = "BUT"
|
||||
NB_SEM = 6
|
||||
COMPENSATION_UE = False
|
||||
APC_SAE = True
|
||||
ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT]
|
||||
|
||||
|
||||
register_parcours(ParcoursBUT())
|
||||
|
||||
|
||||
class ParcoursDUT(TypeParcours):
|
||||
"""DUT selon l'arrêté d'août 2005"""
|
||||
|
||||
@ -305,7 +354,7 @@ register_parcours(ParcoursDUTMono())
|
||||
class ParcoursDUT2(ParcoursDUT):
|
||||
"""DUT en deux semestres (par ex.: années spéciales semestrialisées)"""
|
||||
|
||||
TYPE_PARCOURS = 130
|
||||
TYPE_PARCOURS = CodesParcours.DUT2
|
||||
NAME = "DUT2"
|
||||
NB_SEM = 2
|
||||
|
||||
@ -318,7 +367,7 @@ class ParcoursLP(TypeParcours):
|
||||
(pour anciennes LP. Après 2014, préférer ParcoursLP2014)
|
||||
"""
|
||||
|
||||
TYPE_PARCOURS = 200
|
||||
TYPE_PARCOURS = CodesParcours.LP
|
||||
NAME = "LP"
|
||||
NB_SEM = 1
|
||||
COMPENSATION_UE = False
|
||||
@ -335,7 +384,7 @@ register_parcours(ParcoursLP())
|
||||
class ParcoursLP2sem(ParcoursLP):
|
||||
"""Licence Pro (en deux "semestres")"""
|
||||
|
||||
TYPE_PARCOURS = 210
|
||||
TYPE_PARCOURS = CodesParcours.LP2sem
|
||||
NAME = "LP2sem"
|
||||
NB_SEM = 2
|
||||
COMPENSATION_UE = True
|
||||
@ -348,7 +397,7 @@ register_parcours(ParcoursLP2sem())
|
||||
class ParcoursLP2semEvry(ParcoursLP):
|
||||
"""Licence Pro (en deux "semestres", U. Evry)"""
|
||||
|
||||
TYPE_PARCOURS = 220
|
||||
TYPE_PARCOURS = CodesParcours.LP2semEvry
|
||||
NAME = "LP2semEvry"
|
||||
NB_SEM = 2
|
||||
COMPENSATION_UE = True
|
||||
@ -374,7 +423,7 @@ class ParcoursLP2014(TypeParcours):
|
||||
# l'établissement d'un coefficient qui peut varier dans un rapport de 1 à 3. ", etc ne sont _pas_
|
||||
# vérifiés par ScoDoc)
|
||||
|
||||
TYPE_PARCOURS = 230
|
||||
TYPE_PARCOURS = CodesParcours.LP2014
|
||||
NAME = "LP2014"
|
||||
NB_SEM = 1
|
||||
ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT, UE_STAGE_LP]
|
||||
@ -418,7 +467,7 @@ register_parcours(ParcoursLP2014())
|
||||
class ParcoursLP2sem2014(ParcoursLP):
|
||||
"""Licence Pro (en deux "semestres", selon arrêté du 22/01/2014)"""
|
||||
|
||||
TYPE_PARCOURS = 240
|
||||
TYPE_PARCOURS = CodesParcours.LP2sem2014
|
||||
NAME = "LP2014_2sem"
|
||||
NB_SEM = 2
|
||||
|
||||
@ -430,7 +479,7 @@ register_parcours(ParcoursLP2sem2014())
|
||||
class ParcoursM2(TypeParcours):
|
||||
"""Master 2 (en deux "semestres")"""
|
||||
|
||||
TYPE_PARCOURS = 250
|
||||
TYPE_PARCOURS = CodesParcours.M2
|
||||
NAME = "M2sem"
|
||||
NB_SEM = 2
|
||||
COMPENSATION_UE = True
|
||||
@ -443,7 +492,7 @@ register_parcours(ParcoursM2())
|
||||
class ParcoursM2noncomp(ParcoursM2):
|
||||
"""Master 2 (en deux "semestres") sans compensation"""
|
||||
|
||||
TYPE_PARCOURS = 251
|
||||
TYPE_PARCOURS = CodesParcours.M2noncomp
|
||||
NAME = "M2noncomp"
|
||||
COMPENSATION_UE = False
|
||||
UNUSED_CODES = set((ADC, ATT, ATB))
|
||||
@ -455,7 +504,7 @@ register_parcours(ParcoursM2noncomp())
|
||||
class ParcoursMono(TypeParcours):
|
||||
"""Formation générique en une session"""
|
||||
|
||||
TYPE_PARCOURS = 300
|
||||
TYPE_PARCOURS = CodesParcours.Mono
|
||||
NAME = "Mono"
|
||||
NB_SEM = 1
|
||||
COMPENSATION_UE = False
|
||||
@ -468,7 +517,7 @@ register_parcours(ParcoursMono())
|
||||
class ParcoursLegacy(TypeParcours):
|
||||
"""DUT (ancien ScoDoc, ne plus utiliser)"""
|
||||
|
||||
TYPE_PARCOURS = 0
|
||||
TYPE_PARCOURS = CodesParcours.Legacy
|
||||
NAME = "DUT"
|
||||
NB_SEM = 4
|
||||
COMPENSATION_UE = None # backward compat: defini dans formsemestre
|
||||
@ -502,7 +551,7 @@ class ParcoursBachelorISCID6(ParcoursISCID):
|
||||
"""ISCID: Bachelor en 3 ans (6 sem.)"""
|
||||
|
||||
NAME = "ParcoursBachelorISCID6"
|
||||
TYPE_PARCOURS = 1001
|
||||
TYPE_PARCOURS = CodesParcours.ISCID6
|
||||
NAME = ""
|
||||
NB_SEM = 6
|
||||
ECTS_PROF_DIPL = 8 # crédits professionnels requis pour obtenir le diplôme
|
||||
@ -513,7 +562,7 @@ register_parcours(ParcoursBachelorISCID6())
|
||||
|
||||
class ParcoursMasterISCID4(ParcoursISCID):
|
||||
"ISCID: Master en 2 ans (4 sem.)"
|
||||
TYPE_PARCOURS = 1002
|
||||
TYPE_PARCOURS = CodesParcours.ISCID4
|
||||
NAME = "ParcoursMasterISCID4"
|
||||
NB_SEM = 4
|
||||
ECTS_PROF_DIPL = 15 # crédits professionnels requis pour obtenir le diplôme
|
||||
@ -567,7 +616,7 @@ class ParcoursUCAC(TypeParcours):
|
||||
class ParcoursLicenceUCAC3(ParcoursUCAC):
|
||||
"""UCAC: Licence en 3 sessions d'un an"""
|
||||
|
||||
TYPE_PARCOURS = 501
|
||||
TYPE_PARCOURS = CodesParcours.LicenceUCAC3
|
||||
NAME = "Licence UCAC en 3 sessions d'un an"
|
||||
NB_SEM = 3
|
||||
|
||||
@ -578,7 +627,7 @@ register_parcours(ParcoursLicenceUCAC3())
|
||||
class ParcoursMasterUCAC2(ParcoursUCAC):
|
||||
"""UCAC: Master en 2 sessions d'un an"""
|
||||
|
||||
TYPE_PARCOURS = 502
|
||||
TYPE_PARCOURS = CodesParcours.MasterUCAC2
|
||||
NAME = "Master UCAC en 2 sessions d'un an"
|
||||
NB_SEM = 2
|
||||
|
||||
@ -589,7 +638,7 @@ register_parcours(ParcoursMasterUCAC2())
|
||||
class ParcoursMonoUCAC(ParcoursUCAC):
|
||||
"""UCAC: Formation en 1 session de durée variable"""
|
||||
|
||||
TYPE_PARCOURS = 503
|
||||
TYPE_PARCOURS = CodesParcours.MonoUCAC
|
||||
NAME = "Formation UCAC en 1 session de durée variable"
|
||||
NB_SEM = 1
|
||||
UNUSED_CODES = set((ADC, ATT, ATB))
|
||||
@ -601,7 +650,7 @@ register_parcours(ParcoursMonoUCAC())
|
||||
class Parcours6Sem(TypeParcours):
|
||||
"""Parcours générique en 6 semestres"""
|
||||
|
||||
TYPE_PARCOURS = 600
|
||||
TYPE_PARCOURS = CodesParcours.GEN_6_SEM
|
||||
NAME = "Formation en 6 semestres"
|
||||
NB_SEM = 6
|
||||
COMPENSATION_UE = True
|
||||
@ -623,7 +672,7 @@ register_parcours(Parcours6Sem())
|
||||
class ParcoursMasterLMD(TypeParcours):
|
||||
"""Master générique en 4 semestres dans le LMD"""
|
||||
|
||||
TYPE_PARCOURS = 402
|
||||
TYPE_PARCOURS = CodesParcours.MasterLMD
|
||||
NAME = "Master LMD"
|
||||
NB_SEM = 4
|
||||
COMPENSATION_UE = True # variabale inutilisée
|
||||
@ -636,7 +685,7 @@ register_parcours(ParcoursMasterLMD())
|
||||
class ParcoursMasterIG(ParcoursMasterLMD):
|
||||
"""Master de l'Institut Galilée (U. Paris 13) en 4 semestres (LMD)"""
|
||||
|
||||
TYPE_PARCOURS = 403
|
||||
TYPE_PARCOURS = CodesParcours.MasterIG
|
||||
NAME = "Master IG P13"
|
||||
BARRE_MOY = 10.0
|
||||
NOTES_BARRE_VALID_UE_TH = 10.0 # seuil pour valider UE
|
||||
|
@ -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,12 +29,15 @@
|
||||
(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 import models
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.scodoc.sco_exceptions import ScoValueError, ScoLockedFormError, ScoGenError
|
||||
@ -44,22 +47,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 +107,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 +143,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 +156,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 +191,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 +340,37 @@ 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_id = module["formation_id"]
|
||||
formation = sco_formations.formation_list(args={"formation_id": 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": 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(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,28 +378,23 @@ 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(
|
||||
"""<div class="ue_warning"><span>Formation verrouillée, seuls certains éléments peuvent être modifiés</span></div>"""
|
||||
)
|
||||
|
||||
tf = TrivialFormulator(
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
descr = [
|
||||
(
|
||||
"code",
|
||||
{
|
||||
"size": 10,
|
||||
"explanation": "code du module (doit être unique dans la formation)",
|
||||
"allow_null": False,
|
||||
"validator": lambda val, field, formation_id=Mod[
|
||||
"formation_id"
|
||||
]: check_module_code_unicity(
|
||||
"validator": lambda val, field, formation_id=formation_id: check_module_code_unicity(
|
||||
val, field, formation_id, module_id=module_id
|
||||
),
|
||||
},
|
||||
@ -420,8 +407,8 @@ def module_edit(module_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],
|
||||
"enabled": unlocked,
|
||||
},
|
||||
),
|
||||
@ -445,6 +432,27 @@ def module_edit(module_id=None):
|
||||
"explanation": "nombre d'heures de Travaux Pratiques",
|
||||
},
|
||||
),
|
||||
]
|
||||
if is_apc:
|
||||
a_module = models.Module.query.get(module_id)
|
||||
coefs_descr = a_module.ue_coefs_descr()
|
||||
if coefs_descr:
|
||||
coefs_descr_txt = ", ".join(["%s: %s" % x for x in coefs_descr])
|
||||
else:
|
||||
coefs_descr_txt = """<span class="missing_value">non définis</span>"""
|
||||
descr += [
|
||||
(
|
||||
"ue_coefs",
|
||||
{
|
||||
"readonly": True,
|
||||
"title": "Coefficients vers les UE",
|
||||
"default": coefs_descr_txt,
|
||||
"explanation": "passer par la page d'édition de la formation pour modifier les coefficients",
|
||||
},
|
||||
)
|
||||
]
|
||||
else: # Module classique avec coef scalaire:
|
||||
descr += [
|
||||
(
|
||||
"coefficient",
|
||||
{
|
||||
@ -455,7 +463,8 @@ def module_edit(module_id=None):
|
||||
"enabled": unlocked,
|
||||
},
|
||||
),
|
||||
# ('ects', { 'size' : 4, 'type' : 'float', 'title' : 'ECTS', 'explanation' : 'nombre de crédits ECTS', 'enabled' : unlocked }),
|
||||
]
|
||||
descr += [
|
||||
("formation_id", {"input_type": "hidden"}),
|
||||
("ue_id", {"input_type": "hidden"}),
|
||||
("module_id", {"input_type": "hidden"}),
|
||||
@ -465,8 +474,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,
|
||||
},
|
||||
),
|
||||
@ -499,11 +508,16 @@ def module_edit(module_id=None):
|
||||
"type": "int",
|
||||
},
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
tf = TrivialFormulator(
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
descr,
|
||||
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 +616,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 +668,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 Formation, 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
|
||||
@ -65,6 +66,7 @@ _ueEditor = ndb.EditableTable(
|
||||
"acronyme",
|
||||
"numero",
|
||||
"titre",
|
||||
"semestre_idx",
|
||||
"type",
|
||||
"ue_code",
|
||||
"ects",
|
||||
@ -81,6 +83,7 @@ _ueEditor = ndb.EditableTable(
|
||||
"numero": ndb.int_null_is_zero,
|
||||
"ects": ndb.float_null_is_null,
|
||||
"coefficient": ndb.float_null_is_zero,
|
||||
"semestre_idx": ndb.int_null_is_null,
|
||||
},
|
||||
)
|
||||
|
||||
@ -194,7 +197,7 @@ def ue_create(formation_id=None):
|
||||
|
||||
|
||||
def ue_edit(ue_id=None, create=False, formation_id=None):
|
||||
"""Modification ou creation d'une UE"""
|
||||
"""Modification ou création d'une UE"""
|
||||
from app.scodoc import sco_formations
|
||||
|
||||
create = int(create)
|
||||
@ -211,25 +214,25 @@ def ue_edit(ue_id=None, create=False, formation_id=None):
|
||||
title = "Création d'une UE"
|
||||
initvalues = {}
|
||||
submitlabel = "Créer cette UE"
|
||||
Fol = sco_formations.formation_list(args={"formation_id": formation_id})
|
||||
if not Fol:
|
||||
raise ScoValueError(
|
||||
"Formation %s inexistante ! (si vous avez suivi un lien valide, merci de signaler le problème)"
|
||||
% formation_id
|
||||
)
|
||||
Fo = Fol[0]
|
||||
parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"])
|
||||
|
||||
formation = Formation.query.get(formation_id)
|
||||
if not formation:
|
||||
raise ScoValueError(f"Formation inexistante ! (id={formation_id})")
|
||||
parcours = formation.get_parcours()
|
||||
is_apc = parcours.APC_SAE
|
||||
semestres_indices = list(range(1, parcours.NB_SEM + 1))
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title=title, javascripts=["js/edit_ue.js"]),
|
||||
"<h2>" + title,
|
||||
" (formation %(acronyme)s, version %(version)s)</h2>" % Fo,
|
||||
f" (formation {formation.acronyme}, version {formation.version})</h2>",
|
||||
"""
|
||||
<p class="help">Les UE sont des groupes de modules dans une formation donnée, utilisés pour l'évaluation (on calcule des moyennes par UE et applique des seuils ("barres")).
|
||||
</p>
|
||||
<p class="help">Les UE sont des groupes de modules dans une formation donnée,
|
||||
utilisés pour la validation (on calcule des moyennes par UE et applique des
|
||||
seuils ("barres")).
|
||||
</p>
|
||||
|
||||
<p class="help">Note: L'UE n'a pas de coefficient associé. Seuls les <em>modules</em> ont des coefficients.
|
||||
</p>""",
|
||||
<p class="help">Note: sauf exception, l'UE n'a pas de coefficient associé.
|
||||
Seuls les <em>modules</em> ont des coefficients.
|
||||
</p>""",
|
||||
]
|
||||
|
||||
ue_types = parcours.ALLOWED_UE_TYPES
|
||||
@ -251,6 +254,18 @@ def ue_edit(ue_id=None, create=False, formation_id=None):
|
||||
"type": "int",
|
||||
},
|
||||
),
|
||||
(
|
||||
"semestre_idx",
|
||||
{
|
||||
"input_type": "menu",
|
||||
"type": "int",
|
||||
"allow_null": True,
|
||||
"title": parcours.SESSION_NAME.capitalize(),
|
||||
"explanation": "%s de l'UE dans la formation" % parcours.SESSION_NAME,
|
||||
"labels": ["non spécifié"] + [str(x) for x in semestres_indices],
|
||||
"allowed_values": [""] + semestres_indices,
|
||||
},
|
||||
),
|
||||
(
|
||||
"type",
|
||||
{
|
||||
@ -275,10 +290,11 @@ def ue_edit(ue_id=None, create=False, formation_id=None):
|
||||
"size": 4,
|
||||
"type": "float",
|
||||
"title": "Coefficient",
|
||||
"explanation": """les coefficients d'UE ne sont utilisés que lorsque
|
||||
l'option <em>Utiliser les coefficients d'UE pour calculer la moyenne générale</em>
|
||||
est activée. Par défaut, le coefficient d'une UE est simplement la somme des
|
||||
coefficients des modules dans lesquels l'étudiant a des notes.
|
||||
"explanation": """les coefficients d'UE ne sont utilisés que
|
||||
lorsque l'option <em>Utiliser les coefficients d'UE pour calculer
|
||||
la moyenne générale</em> est activée. Par défaut, le coefficient
|
||||
d'une UE est simplement la somme des coefficients des modules dans
|
||||
lesquels l'étudiant a des notes.
|
||||
""",
|
||||
},
|
||||
),
|
||||
@ -307,30 +323,13 @@ def ue_edit(ue_id=None, create=False, formation_id=None):
|
||||
},
|
||||
),
|
||||
]
|
||||
if parcours.UE_IS_MODULE:
|
||||
# demande le semestre pour creer le module immediatement:
|
||||
semestres_indices = list(range(1, parcours.NB_SEM + 1))
|
||||
fw.append(
|
||||
(
|
||||
"semestre_id",
|
||||
{
|
||||
"input_type": "menu",
|
||||
"type": "int",
|
||||
"title": parcours.SESSION_NAME.capitalize(),
|
||||
"explanation": "%s de début du module dans la formation"
|
||||
% parcours.SESSION_NAME,
|
||||
"labels": [str(x) for x in semestres_indices],
|
||||
"allowed_values": semestres_indices,
|
||||
},
|
||||
)
|
||||
)
|
||||
if create and not parcours.UE_IS_MODULE:
|
||||
fw.append(
|
||||
(
|
||||
"create_matiere",
|
||||
{
|
||||
"input_type": "boolcheckbox",
|
||||
"default": False,
|
||||
"default": True,
|
||||
"title": "Créer matière identique",
|
||||
"explanation": "créer immédiatement une matière dans cette UE (utile si on n'utilise pas de matières)",
|
||||
},
|
||||
@ -352,13 +351,10 @@ def ue_edit(ue_id=None, create=False, formation_id=None):
|
||||
if not tf[2]["ue_code"]:
|
||||
del tf[2]["ue_code"]
|
||||
if not tf[2]["numero"]:
|
||||
if not "semestre_id" in tf[2]:
|
||||
tf[2]["semestre_id"] = 0
|
||||
# numero regroupant par semestre ou année:
|
||||
tf[2]["numero"] = next_ue_numero(
|
||||
formation_id, int(tf[2]["semestre_id"] or 0)
|
||||
formation_id, int(tf[2]["semestre_idx"])
|
||||
)
|
||||
|
||||
ue_id = do_ue_create(tf[2])
|
||||
if parcours.UE_IS_MODULE or tf[2]["create_matiere"]:
|
||||
matiere_id = sco_edit_matiere.do_matiere_create(
|
||||
@ -374,7 +370,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None):
|
||||
"ue_id": ue_id,
|
||||
"matiere_id": matiere_id,
|
||||
"formation_id": formation_id,
|
||||
"semestre_id": tf[2]["semestre_id"],
|
||||
"semestre_id": tf[2]["semestre_idx"],
|
||||
},
|
||||
)
|
||||
else:
|
||||
@ -387,14 +383,18 @@ def ue_edit(ue_id=None, create=False, formation_id=None):
|
||||
|
||||
|
||||
def _add_ue_semestre_id(ues):
|
||||
"""ajoute semestre_id dans les ue, en regardant le premier module de chacune.
|
||||
"""ajoute semestre_id dans les ue, en regardant
|
||||
semestre_idx ou à défaut le premier module de chacune.
|
||||
Les UE sans modules se voient attribuer le numero UE_SEM_DEFAULT (1000000),
|
||||
qui les place à la fin de la liste.
|
||||
"""
|
||||
for ue in ues:
|
||||
Modlist = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]})
|
||||
if Modlist:
|
||||
ue["semestre_id"] = Modlist[0]["semestre_id"]
|
||||
if ue["semestre_idx"] is not None:
|
||||
ue["semestre_id"] = ue["semestre_idx"]
|
||||
else:
|
||||
modules = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]})
|
||||
if modules:
|
||||
ue["semestre_id"] = modules[0]["semestre_id"]
|
||||
else:
|
||||
ue["semestre_id"] = 1000000
|
||||
|
||||
@ -452,6 +452,7 @@ def ue_table(formation_id=None, msg=""): # was ue_list
|
||||
raise ScoValueError("invalid formation_id")
|
||||
F = F[0]
|
||||
parcours = sco_codes_parcours.get_parcours_from_code(F["type_parcours"])
|
||||
is_apc = parcours.APC_SAE
|
||||
locked = sco_formations.formation_has_locked_sems(formation_id)
|
||||
|
||||
ues = ue_list(args={"formation_id": formation_id, "is_external": False})
|
||||
@ -567,7 +568,18 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
|
||||
)
|
||||
|
||||
H.append("</div>")
|
||||
|
||||
# Formation APC (BUT) ?
|
||||
if is_apc:
|
||||
H.append(
|
||||
f"""<div class="formation_apc_infos">
|
||||
<div class="ue_list_tit">Formation par compétences (BUT)</div>
|
||||
<ul>
|
||||
<li><a class="stdlink" href="{
|
||||
url_for('notes.edit_modules_ue_coefs', scodoc_dept=g.scodoc_dept, formation_id=formation_id)
|
||||
}">éditer les coefficients des ressources et SAÉs</a></li>
|
||||
</ul>
|
||||
</div>"""
|
||||
)
|
||||
# Description des UE/matières/modules
|
||||
H.append('<div class="formation_ue_list">')
|
||||
H.append('<div class="ue_list_tit">Programme pédagogique:</div>')
|
||||
@ -775,8 +787,12 @@ def _ue_table_ues(
|
||||
)
|
||||
else:
|
||||
H.append('<span class="locked">[verrouillé]</span>')
|
||||
if parcours.APC_SAE:
|
||||
func_html_list = _ue_table_ressources_saes
|
||||
else:
|
||||
func_html_list = _ue_table_matieres
|
||||
H.append(
|
||||
_ue_table_matieres(
|
||||
func_html_list(
|
||||
parcours,
|
||||
ue,
|
||||
editable,
|
||||
@ -837,6 +853,8 @@ def _ue_table_matieres(
|
||||
delete_disabled_icon,
|
||||
)
|
||||
)
|
||||
if not parcours.UE_IS_MODULE:
|
||||
H.append("</li>")
|
||||
if not matieres:
|
||||
H.append("<li>Aucune matière dans cette UE ! ")
|
||||
if editable:
|
||||
@ -855,6 +873,76 @@ def _ue_table_matieres(
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def _ue_table_ressources_saes(
|
||||
parcours,
|
||||
ue,
|
||||
editable,
|
||||
tag_editable,
|
||||
arrow_up,
|
||||
arrow_down,
|
||||
arrow_none,
|
||||
delete_icon,
|
||||
delete_disabled_icon,
|
||||
):
|
||||
"""Édition de programme: liste des ressources et SAÉs d'une UE.
|
||||
(pour les parcours APC_SAE)
|
||||
"""
|
||||
matieres = sco_edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]})
|
||||
if not matieres:
|
||||
# Les formations APC (BUT) n'utilisent pas de matières
|
||||
# mais il doit y en avoir une par UE
|
||||
# silently fix this on-the-fly to ease migration
|
||||
_ = sco_edit_matiere.do_matiere_create(
|
||||
{"ue_id": ue["ue_id"], "titre": "APC", "numero": 1},
|
||||
)
|
||||
matieres = sco_edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]})
|
||||
assert matieres
|
||||
mat = matieres[0]
|
||||
|
||||
H = [
|
||||
"""
|
||||
<ul class="notes_matiere_list but_matiere_list">
|
||||
"""
|
||||
]
|
||||
modules = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]})
|
||||
for titre, element_name, module_type in (
|
||||
("Ressources", "ressource", scu.ModuleType.RESSOURCE),
|
||||
("SAÉs", "SAÉ", scu.ModuleType.SAE),
|
||||
("Autres modules", "xxx", None),
|
||||
):
|
||||
H.append(f'<li class="notes_matiere_list">{titre}')
|
||||
elements = [
|
||||
m
|
||||
for m in modules
|
||||
if module_type == m["module_type"]
|
||||
or (
|
||||
(module_type is None)
|
||||
and m["module_type"]
|
||||
not in (scu.ModuleType.RESSOURCE, scu.ModuleType.SAE)
|
||||
)
|
||||
]
|
||||
H.append(
|
||||
_ue_table_modules(
|
||||
parcours,
|
||||
mat,
|
||||
elements,
|
||||
editable,
|
||||
tag_editable,
|
||||
arrow_up,
|
||||
arrow_down,
|
||||
arrow_none,
|
||||
delete_icon,
|
||||
delete_disabled_icon,
|
||||
empty_list_msg="Aucune " + element_name,
|
||||
create_element_msg="créer une " + element_name,
|
||||
add_suppress_link=False,
|
||||
)
|
||||
)
|
||||
|
||||
H.append("</li></ul>")
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def _ue_table_modules(
|
||||
parcours,
|
||||
mat,
|
||||
@ -866,6 +954,10 @@ def _ue_table_modules(
|
||||
arrow_none,
|
||||
delete_icon,
|
||||
delete_disabled_icon,
|
||||
unit_name="matière",
|
||||
add_suppress_link=True, # lien "supprimer cette matière"
|
||||
empty_list_msg="Aucun élément dans cette matière",
|
||||
create_element_msg="créer un module",
|
||||
):
|
||||
"""Édition de programme: liste des modules d'une matière d'une UE"""
|
||||
H = ['<ul class="notes_module_list">']
|
||||
@ -875,7 +967,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)
|
||||
|
||||
@ -948,8 +1040,8 @@ def _ue_table_modules(
|
||||
)
|
||||
H.append("</li>")
|
||||
if not modules:
|
||||
H.append("<li>Aucun module dans cette matière ! ")
|
||||
if editable:
|
||||
H.append(f"<li>{empty_list_msg} ! ")
|
||||
if editable and add_suppress_link:
|
||||
H.append(
|
||||
f"""<a class="stdlink" href="{
|
||||
url_for("notes.matiere_delete",
|
||||
@ -963,11 +1055,10 @@ def _ue_table_modules(
|
||||
f"""<li> <a class="stdlink" href="{
|
||||
url_for("notes.module_create",
|
||||
scodoc_dept=g.scodoc_dept, matiere_id=mat["matiere_id"])}"
|
||||
>créer un module</a></li>
|
||||
>{create_element_msg}</a></li>
|
||||
"""
|
||||
)
|
||||
H.append("</ul>")
|
||||
H.append("</li>")
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
@ -987,20 +1078,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"])
|
||||
if nt.parcours.APC_SAE:
|
||||
# BUT: tableau ressources puis SAE
|
||||
ressources = [
|
||||
m for m in modimpls if m["module"]["module_type"] == ModuleType.RESSOURCE
|
||||
]
|
||||
)
|
||||
H.append(
|
||||
"""<td class="malus">
|
||||
<a href="moduleimpl_status?moduleimpl_id=%s" class="formsemestre_status_link">malus (%d notes)</a>
|
||||
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)
|
||||
)
|
||||
<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:
|
||||
raise ValueError("Invalid module_type") # a bug
|
||||
# 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">
|
||||
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(), # sco_users.user_info(M["responsable_id"])["nomprenom"],
|
||||
f"""<span class="blacktt">({module_resp.user_name})</span>""",
|
||||
<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)
|
||||
@ -178,7 +179,7 @@ def formsemestre_list_saisies_notes(formsemestre_id, format="html"):
|
||||
"""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
r = ndb.SimpleDictFetch(
|
||||
"""SELECT i.nom, code_nip, n.*, mod.titre, e.description, e.jour
|
||||
"""SELECT i.nom, i.id as etudid, n.*, mod.titre, e.description, e.jour
|
||||
FROM notes_notes n, notes_evaluation e, notes_moduleimpl mi,
|
||||
notes_modules mod, identite i
|
||||
WHERE mi.id = e.moduleimpl_id
|
||||
|
@ -32,13 +32,12 @@ import base64
|
||||
import bisect
|
||||
import copy
|
||||
import datetime
|
||||
from enum import IntEnum
|
||||
import json
|
||||
from hashlib import md5
|
||||
import numbers
|
||||
import os
|
||||
import pydot
|
||||
import re
|
||||
import requests
|
||||
import _thread
|
||||
import time
|
||||
import unicodedata
|
||||
@ -46,9 +45,11 @@ import urllib
|
||||
from urllib.parse import urlparse, parse_qsl, urlunparse, urlencode
|
||||
|
||||
from PIL import Image as PILImage
|
||||
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
|
||||
@ -70,8 +71,22 @@ 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 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
|
||||
@ -655,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;
|
||||
}
|
||||
@ -1597,6 +1606,11 @@ div.ue_warning span {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
span.missing_value {
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* tableau recap notes */
|
||||
table.notes_recapcomplet {
|
||||
border: 2px solid blue;
|
||||
|
59
app/static/css/table_editor.css
Normal file
59
app/static/css/table_editor.css
Normal file
@ -0,0 +1,59 @@
|
||||
/* table_editor, par Sébastien L.
|
||||
*/
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
/***************************/
|
||||
/* Le tableau */
|
||||
/***************************/
|
||||
.tableau{
|
||||
display: grid;
|
||||
grid-auto-rows: minmax(24px, auto);
|
||||
gap: 2px;
|
||||
}
|
||||
.entete{
|
||||
background: #09c;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tableau>div{
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #999;
|
||||
grid-column: var(--x) / span var(--nbX);
|
||||
grid-row: var(--y) / span var(--nbY);
|
||||
}
|
||||
|
||||
[data-editable="true"]{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/***************************/
|
||||
/* Statut des cellules */
|
||||
/***************************/
|
||||
.selected{ outline: 1px solid #c09; }
|
||||
.modifying{ outline: 2px dashed #c09; }
|
||||
.wait{ outline: 2px solid #c90; }
|
||||
.good{ outline: 2px solid #9c0; }
|
||||
.modified { font-weight: bold; color:indigo}
|
||||
/***************************/
|
||||
/* Message */
|
||||
/***************************/
|
||||
.message{
|
||||
position: fixed;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
z-index: 10;
|
||||
padding: 20px;
|
||||
border-radius: 0 0 10px 10px;
|
||||
background: #ec7068;
|
||||
background: #90c;
|
||||
color: #FFF;
|
||||
font-size: 24px;
|
||||
animation: message 3s;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
@keyframes message{
|
||||
20%{transform: translate(-50%, 100%)}
|
||||
80%{transform: translate(-50%, 100%)}
|
||||
}
|
128
app/static/js/table_editor.js
Normal file
128
app/static/js/table_editor.js
Normal file
@ -0,0 +1,128 @@
|
||||
/* table_editor, par Sébastien L. 2021-11-12
|
||||
*/
|
||||
|
||||
/*****************************/
|
||||
/* 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}"
|
||||
data-x="${cellule.x}"
|
||||
data-y="${cellule.y}"
|
||||
data-nbX="${cellule.nbX || 1}"
|
||||
data-nbY="${cellule.nbY || 1}"
|
||||
data-data="${cellule.data}"
|
||||
data-orig="${cellule.data}"
|
||||
style="
|
||||
--x:${cellule.x};
|
||||
--y:${cellule.y};
|
||||
--nbX:${cellule.nbX || 1};
|
||||
--nbY: ${cellule.nbY || 1};
|
||||
">
|
||||
${cellule.data}
|
||||
</div>`;
|
||||
})
|
||||
document.querySelector(".tableau").innerHTML = output;
|
||||
installListeners();
|
||||
}
|
||||
|
||||
/*****************************/
|
||||
/* Gestion des évènements */
|
||||
/*****************************/
|
||||
$(function () {
|
||||
document.body.addEventListener("keydown", key);
|
||||
});
|
||||
function installListeners() {
|
||||
document.querySelectorAll("[data-editable=true]").forEach(cellule => {
|
||||
cellule.addEventListener("click", function () { selectCell(this) });
|
||||
cellule.addEventListener("dblclick", function () { modifCell(this) });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*********************************/
|
||||
/* Interaction avec les cellules */
|
||||
/*********************************/
|
||||
function selectCell(obj) {
|
||||
if (obj.classList.contains("modifying")) {
|
||||
return; // Cellule en cours de modification, ne pas sélectionner.
|
||||
}
|
||||
let currentModif = document.querySelector(".modifying");
|
||||
if (currentModif) {
|
||||
if (!save(currentModif)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelectorAll(".selected, .modifying").forEach(cellule => {
|
||||
cellule.classList.remove("selected", "modifying");
|
||||
cellule.removeAttribute("contentEditable");
|
||||
cellule.removeEventListener("keydown", keyCell);
|
||||
})
|
||||
obj.classList.add("selected");
|
||||
}
|
||||
|
||||
function modifCell(obj) {
|
||||
if (obj) {
|
||||
obj.classList.add("modifying");
|
||||
obj.contentEditable = true;
|
||||
obj.addEventListener("keydown", keyCell);
|
||||
obj.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function key(event) {
|
||||
switch (event.key) {
|
||||
case "Enter": modifCell(document.querySelector(".selected")); event.preventDefault(); break;
|
||||
case "ArrowRight": ArrowMove(1, 0); break;
|
||||
case "ArrowLeft": ArrowMove(-1, 0); break;
|
||||
case "ArrowUp": ArrowMove(0, -1); break;
|
||||
case "ArrowDown": ArrowMove(0, 1); break;
|
||||
}
|
||||
}
|
||||
|
||||
function ArrowMove(x, y) {
|
||||
if (document.querySelector(".modifying") || !document.querySelector(".selected")) {
|
||||
return; // S'il n'y a aucune cellule selectionnée ou si une cellule est encours de modification, on ne change pas
|
||||
}
|
||||
|
||||
let selected = document.querySelector(".selected");
|
||||
let next = document.querySelector(`[data-x="${parseInt(selected.dataset.x) + x}"][data-y="${parseInt(selected.dataset.y) + y}"][data-editable="true"]`);
|
||||
|
||||
if (next) {
|
||||
selectCell(next);
|
||||
}
|
||||
}
|
||||
|
||||
function keyCell(event) {
|
||||
if (event.key == "Enter") {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (!save(this)) {
|
||||
return
|
||||
}
|
||||
this.classList.remove("modifying");
|
||||
ArrowMove(0, 1);
|
||||
modifCell(document.querySelector(".selected"));
|
||||
}
|
||||
}
|
||||
|
||||
/******************************/
|
||||
/* Affichage d'un message */
|
||||
/******************************/
|
||||
function message(msg) {
|
||||
var div = document.createElement("div");
|
||||
div.className = "message";
|
||||
div.innerHTML = msg;
|
||||
document.querySelector("body").appendChild(div);
|
||||
setTimeout(() => {
|
||||
div.remove();
|
||||
}, 3000);
|
||||
}
|
72
app/templates/pn/form_modules_ue_coefs.html
Normal file
72
app/templates/pn/form_modules_ue_coefs.html
Normal file
@ -0,0 +1,72 @@
|
||||
<!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="tableau"></div>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
$.getJSON("{{data_source}}", function (data) {
|
||||
build_table(data);
|
||||
});
|
||||
});
|
||||
function save(obj) {
|
||||
var value = obj.innerText.trim();
|
||||
if (value.length == 0) {
|
||||
value = "0";
|
||||
}
|
||||
if (!/^[\d.,]+$/.test(value)) {
|
||||
message("Il est attendu un nombre");
|
||||
return false;
|
||||
}
|
||||
if (value == obj.dataset.data) {
|
||||
return true; // Aucune modification, pas d'enregistrement mais on continue normalement
|
||||
}
|
||||
obj.dataset.data = value;
|
||||
obj.classList.add("wait");
|
||||
// XXX DEBUG
|
||||
// console.log(`
|
||||
// x : ${getComputedStyle(obj).getPropertyValue("--x")}
|
||||
// y : ${getComputedStyle(obj).getPropertyValue("--y")}
|
||||
// data : ${value}
|
||||
// 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: value
|
||||
},
|
||||
function (result) {
|
||||
obj.classList.remove("wait");
|
||||
if (obj.dataset.orig != value)
|
||||
obj.classList.add("modified");
|
||||
else
|
||||
obj.classList.remove("modified");
|
||||
// Lorsque les données sont bien enregistrées, on enlève
|
||||
// l'indication que c'est bon au bout d'un temps
|
||||
//setTimeout(() => {
|
||||
// obj.classList.remove("modified");
|
||||
//}, 1000);
|
||||
}
|
||||
);
|
||||
return true;
|
||||
}
|
||||
</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
|
||||
)
|
||||
|
||||
|
169
app/views/pn_modules.py
Normal file
169
app/views/pn_modules.py
Normal file
@ -0,0 +1,169 @@
|
||||
# -*- 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
|
||||
"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] or "",
|
||||
"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 ###
|
64
migrations/versions/ada0d1f3d84f_but_poids_evaluations_ac.py
Normal file
64
migrations/versions/ada0d1f3d84f_but_poids_evaluations_ac.py
Normal file
@ -0,0 +1,64 @@
|
||||
"""BUT: poids evaluations, AC
|
||||
|
||||
Revision ID: ada0d1f3d84f
|
||||
Revises: 1efe07413835
|
||||
Create Date: 2021-11-07 22:49:22.697211
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "ada0d1f3d84f"
|
||||
down_revision = "1efe07413835"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"app_crit",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("code", sa.Text(), nullable=False),
|
||||
sa.Column("titre", sa.Text(), nullable=True),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"modules_acs",
|
||||
sa.Column("module_id", sa.Integer(), nullable=True),
|
||||
sa.Column("ac_id", sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["ac_id"],
|
||||
["app_crit.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["module_id"],
|
||||
["notes_modules.id"],
|
||||
),
|
||||
)
|
||||
op.create_table(
|
||||
"evaluation_ue_poids",
|
||||
sa.Column("evaluation_id", sa.Integer(), nullable=False),
|
||||
sa.Column("ue_id", sa.Integer(), nullable=False),
|
||||
sa.Column("poids", sa.Float(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["evaluation_id"],
|
||||
["notes_evaluation.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["ue_id"],
|
||||
["notes_ue.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("evaluation_id", "ue_id"),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table("evaluation_ue_poids")
|
||||
op.drop_table("modules_acs")
|
||||
op.drop_table("app_crit")
|
||||
# ### end Alembic commands ###
|
30
migrations/versions/c8efc54586d8_ue_semestre_idx.py
Normal file
30
migrations/versions/c8efc54586d8_ue_semestre_idx.py
Normal file
@ -0,0 +1,30 @@
|
||||
"""UE.semestre_idx
|
||||
|
||||
Revision ID: c8efc54586d8
|
||||
Revises: 6cfc21a7ae1b
|
||||
Create Date: 2021-11-14 17:35:39.602613
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'c8efc54586d8'
|
||||
down_revision = '6cfc21a7ae1b'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('notes_ue', sa.Column('semestre_idx', sa.Integer(), nullable=True))
|
||||
op.create_index(op.f('ix_notes_ue_semestre_idx'), 'notes_ue', ['semestre_idx'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_notes_ue_semestre_idx'), table_name='notes_ue')
|
||||
op.drop_column('notes_ue', 'semestre_idx')
|
||||
# ### 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
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.0.62"
|
||||
SCOVERSION = "9.1.0"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
@ -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]
|
||||
|
111
tests/unit/test_but_modules.py
Normal file
111
tests/unit/test_but_modules.py
Normal file
@ -0,0 +1,111 @@
|
||||
"""
|
||||
Test modèles évaluations avec poids BUT
|
||||
"""
|
||||
|
||||
from tests.unit import sco_fake_gen
|
||||
from app import db
|
||||
from app import models
|
||||
|
||||
"""
|
||||
mapp.set_sco_dept("RT")
|
||||
from app.auth.models import get_super_admin
|
||||
admin_user = get_super_admin()
|
||||
ctx.push()
|
||||
login_user(admin_user)
|
||||
"""
|
||||
|
||||
|
||||
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"
|
||||
)
|
||||
_ue1 = G.create_ue(formation_id=_f["formation_id"], acronyme="UE1", titre="ue 1")
|
||||
_ue2 = G.create_ue(formation_id=_f["formation_id"], acronyme="UE2", titre="ue 2")
|
||||
_ue3 = G.create_ue(formation_id=_f["formation_id"], acronyme="UE3", titre="ue 3")
|
||||
_mat = G.create_matiere(ue_id=_ue1["ue_id"], titre="matière test")
|
||||
_mod = G.create_module(
|
||||
matiere_id=_mat["matiere_id"],
|
||||
code="TSM1",
|
||||
coefficient=1.0,
|
||||
titre="module test",
|
||||
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=formation_id,
|
||||
semestre_id=1,
|
||||
date_debut="01/01/2021",
|
||||
date_fin="30/06/2021",
|
||||
) # formsemestre_id=716
|
||||
mi = G.create_moduleimpl(
|
||||
module_id=module_id,
|
||||
formsemestre_id=sem["formsemestre_id"],
|
||||
)
|
||||
moduleimpl_id = mi["id"]
|
||||
_e1 = G.create_evaluation(
|
||||
moduleimpl_id=moduleimpl_id,
|
||||
jour="01/01/2021",
|
||||
description="evaluation 1",
|
||||
coefficient=0,
|
||||
)
|
||||
evaluation_id = _e1["evaluation_id"] # evaluation_id=25246
|
||||
# ue1_id=1684
|
||||
# formation_id=199
|
||||
#
|
||||
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.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)
|
||||
assert len(e1.ue_poids) == len(ues)
|
||||
assert e1.get_ue_poids_dict()[ues[1].id] == poids[1]
|
||||
e1.set_ue_poids(ue1, p1)
|
||||
db.session.commit()
|
||||
poids2 = [10, 20]
|
||||
e1.update_ue_poids_dict({ue.id: p for (ue, p) in zip(ues[:-1], poids2)})
|
||||
assert e1.get_ue_poids_dict()[ues[0].id] == poids2[0]
|
||||
assert e1.get_ue_poids_dict()[ues[1].id] == poids2[1]
|
||||
assert e1.get_ue_poids_dict()[ues[2].id] == poids[2]
|
||||
# Delete UE
|
||||
db.session.delete(ues[2])
|
||||
db.session.commit()
|
||||
# Delete eval
|
||||
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
|
||||
# Gestion des coefs nuls:
|
||||
mod.set_ue_coef(ue2, 0.0)
|
||||
db.session.commit()
|
||||
assert len(mod.ue_coefs) == 0
|
@ -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
|
||||
|
@ -4,4 +4,4 @@ Architecture: amd64
|
||||
Maintainer: Emmanuel Viennet <emmanuel@viennet.net>
|
||||
Description: ScoDoc 9
|
||||
Un logiciel pour le suivi de la scolarité universitaire.
|
||||
Depends: adduser, curl, gcc, graphviz, libpq-dev, postfix|exim4, cracklib-runtime, libcrack2-dev, python3-dev, python3-venv, python3-pip, python3-wheel, nginx, postgresql, redis, ufw, systemctl
|
||||
Depends: adduser, curl, gcc, graphviz, libpq-dev, postfix|exim4, cracklib-runtime, libcrack2-dev, python3-dev, python3-venv, python3-pip, python3-wheel, nginx, postgresql, redis, ufw
|
||||
|
Loading…
x
Reference in New Issue
Block a user