Cache coefs et poids. Check conformité PN.
This commit is contained in:
parent
8647203f43
commit
47e752c95c
49
app/comp/df_cache.py
Normal file
49
app/comp/df_cache.py
Normal file
@ -0,0 +1,49 @@
|
||||
# -*- 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
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""caches pour tables APC
|
||||
"""
|
||||
|
||||
from app.scodoc import sco_cache
|
||||
|
||||
|
||||
class ModuleCoefsCache(sco_cache.ScoDocCache):
|
||||
"""Cache for module coefs
|
||||
Clé: formation_id.semestre_idx
|
||||
Valeur: DataFrame (df_load_module_coefs)
|
||||
"""
|
||||
|
||||
prefix = "MCO"
|
||||
|
||||
|
||||
class EvaluationsPoidsCache(sco_cache.ScoDocCache):
|
||||
"""Cache for poids evals
|
||||
Clé: moduleimpl_id
|
||||
Valeur: DataFrame (df_load_evaluations_poids)
|
||||
"""
|
||||
|
||||
prefix = "EPC"
|
@ -38,7 +38,7 @@ from app.models.formsemestre import FormSemestre
|
||||
from app.scodoc import sco_codes_parcours
|
||||
|
||||
|
||||
def df_load_module_coefs(formation_id: int, semestre_idx: int) -> pd.DataFrame:
|
||||
def df_load_module_coefs(formation_id: int, semestre_idx: int = None) -> pd.DataFrame:
|
||||
"""Charge les coefs des modules de la formation pour le semestre indiqué.
|
||||
|
||||
Ces coefs lient les modules à chaque UE.
|
||||
|
@ -52,6 +52,7 @@ class Evaluation(db.Model):
|
||||
e = dict(self.__dict__)
|
||||
e.pop("_sa_instance_state", None)
|
||||
# ScoDoc7 output_formators
|
||||
e["evaluation_id"] = self.id
|
||||
e["jour"] = ndb.DateISOtoDMY(e["jour"])
|
||||
e["numero"] = ndb.int_null_is_zero(e["numero"])
|
||||
return sco_evaluation_db.evaluation_enrich_dict(e)
|
||||
@ -88,6 +89,7 @@ class Evaluation(db.Model):
|
||||
ue = UniteEns.query.get(ue_id)
|
||||
L.append(EvaluationUEPoids(evaluation=self, ue=ue, poids=poids))
|
||||
self.ue_poids = L
|
||||
self.moduleimpl.invalidate_evaluations_poids() # inval cache
|
||||
|
||||
def update_ue_poids_dict(self, ue_poids_dict: dict):
|
||||
"""update poids vers UE (ajoute aux existants)"""
|
||||
|
@ -9,6 +9,7 @@ from app.scodoc import notesdb as ndb
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
from app.scodoc import sco_codes_parcours
|
||||
from app.comp import df_cache
|
||||
|
||||
|
||||
class Formation(db.Model):
|
||||
@ -46,6 +47,34 @@ class Formation(db.Model):
|
||||
"""get l'instance de TypeParcours de cette formation"""
|
||||
return sco_codes_parcours.get_parcours_from_code(self.type_parcours)
|
||||
|
||||
def get_module_coefs(self, semestre_idx: int = None):
|
||||
"""Les coefs des modules vers les UE (accès via cache)"""
|
||||
from app.comp import moy_ue
|
||||
|
||||
if semestre_idx is None:
|
||||
key = f"{self.id}"
|
||||
else:
|
||||
key = f"{self.id}.{semestre_idx}"
|
||||
|
||||
modules_coefficients = df_cache.ModuleCoefsCache.get(key)
|
||||
if modules_coefficients is None:
|
||||
modules_coefficients, _, _ = moy_ue.df_load_module_coefs(
|
||||
self.id, semestre_idx
|
||||
)
|
||||
df_cache.ModuleCoefsCache.set(key, modules_coefficients)
|
||||
return modules_coefficients
|
||||
|
||||
def invalidate_module_coefs(self, semestre_idx: int = None):
|
||||
"""Invalide les coefficients de modules cachés.
|
||||
Si semestre_idx est None, invalide tous les semestres,
|
||||
sinon invalide le semestre indiqué et le cache de la formation.
|
||||
"""
|
||||
if semestre_idx is None:
|
||||
keys = {f"{self.id}.{m.semestre_id}" for m in self.modules}
|
||||
else:
|
||||
keys = f"{self.id}.{semestre_idx}"
|
||||
df_cache.ModuleCoefsCache.delete_many(keys | {f"{self.id}"})
|
||||
|
||||
|
||||
class UniteEns(db.Model):
|
||||
"""Unité d'Enseignement (UE)"""
|
||||
@ -186,6 +215,13 @@ class Module(db.Model):
|
||||
f"<Module{ModuleType(self.module_type).name} id={self.id} code={self.code}>"
|
||||
)
|
||||
|
||||
def is_apc(self):
|
||||
"True si module SAÉ ou Ressource"
|
||||
return scu.ModuleType(self.module_type) in {
|
||||
scu.ModuleType.RESSOURCE,
|
||||
scu.ModuleType.SAE,
|
||||
}
|
||||
|
||||
def type_name(self):
|
||||
return scu.MODULE_TYPE_NAMES[self.module_type]
|
||||
|
||||
@ -206,6 +242,7 @@ class Module(db.Model):
|
||||
else:
|
||||
ue_coefs.append(ModuleUECoef(module=self, ue=ue, coef=coef))
|
||||
self.ue_coefs = ue_coefs
|
||||
self.formation.invalidate_module_coefs()
|
||||
|
||||
def update_ue_coef_dict(self, ue_coef_dict: dict):
|
||||
"""update coefs vers UE (ajoute aux existants)"""
|
||||
@ -222,6 +259,7 @@ class Module(db.Model):
|
||||
ue_coef = ModuleUECoef.query.get((self.id, ue.id))
|
||||
if ue_coef:
|
||||
db.session.delete(ue_coef)
|
||||
self.formation.invalidate_module_coefs()
|
||||
|
||||
def ue_coefs_descr(self):
|
||||
"""List of tuples [ (ue_acronyme, coef) ]"""
|
||||
|
@ -4,7 +4,10 @@
|
||||
"""
|
||||
from typing import Any
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from app import db
|
||||
from app.comp import df_cache
|
||||
from app.models import APO_CODE_STR_LEN
|
||||
from app.models import SHORT_STR_LEN
|
||||
from app.models import CODE_STR_LEN
|
||||
@ -36,6 +39,55 @@ class ModuleImpl(db.Model):
|
||||
computation_expr = db.Column(db.Text())
|
||||
|
||||
evaluations = db.relationship("Evaluation", lazy="dynamic", backref="moduleimpl")
|
||||
enseignants = db.relationship(
|
||||
"User",
|
||||
secondary="notes_modules_enseignants",
|
||||
lazy="dynamic",
|
||||
backref="moduleimpl",
|
||||
viewonly=True,
|
||||
)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(ModuleImpl, self).__init__(**kwargs)
|
||||
|
||||
def get_evaluations_poids(self) -> pd.DataFrame:
|
||||
"""Les poids des évaluations vers les UE (accès via cache)"""
|
||||
evaluations_poids = df_cache.EvaluationsPoidsCache.get(self.id)
|
||||
if evaluations_poids is None:
|
||||
from app.comp import moy_mod
|
||||
|
||||
evaluations_poids, _ = moy_mod.df_load_evaluations_poids(self.id)
|
||||
df_cache.EvaluationsPoidsCache.set(self.id, evaluations_poids)
|
||||
return evaluations_poids
|
||||
|
||||
def invalidate_evaluations_poids(self):
|
||||
"""Invalide poids cachés"""
|
||||
df_cache.EvaluationsPoidsCache.delete(self.id)
|
||||
|
||||
def check_apc_conformity(self) -> bool:
|
||||
"""true si les poids des évaluations du module permettent de satisfaire
|
||||
les coefficients du PN.
|
||||
"""
|
||||
if not self.module.formation.get_parcours().APC_SAE:
|
||||
return True
|
||||
from app.comp import moy_mod
|
||||
|
||||
return moy_mod.check_moduleimpl_conformity(
|
||||
self,
|
||||
self.get_evaluations_poids(),
|
||||
self.module.formation.get_module_coefs(self.module.semestre_id),
|
||||
)
|
||||
|
||||
def to_dict(self):
|
||||
"""as a dict, with the same conversions as in ScoDoc7"""
|
||||
e = dict(self.__dict__)
|
||||
e.pop("_sa_instance_state", None)
|
||||
# ScoDoc7 output_formators: (backward compat)
|
||||
e["moduleimpl_id"] = self.id
|
||||
e["ens"] = [
|
||||
{"moduleimpl_id": self.id, "ens_id": e.id} for e in self.enseignants
|
||||
]
|
||||
return e
|
||||
|
||||
|
||||
# Enseignants (chargés de TD ou TP) d'un moduleimpl
|
||||
@ -70,7 +122,7 @@ class ModuleImplInscription(db.Model):
|
||||
Identite,
|
||||
backref=db.backref("moduleimpl_inscriptions", cascade="all, delete-orphan"),
|
||||
)
|
||||
moduleimpl = db.relationship(
|
||||
modimpl = db.relationship(
|
||||
ModuleImpl,
|
||||
backref=db.backref("inscriptions", cascade="all, delete-orphan"),
|
||||
)
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
Ré-écrite pour ScoDoc8, utilise flask_caching et REDIS
|
||||
|
||||
ScoDoc est maintenant multiprocessus / mono-thread, avec un cache en mémoire partagé.
|
||||
ScoDoc est maintenant multiprocessus / mono-thread, avec un cache partagé.
|
||||
"""
|
||||
|
||||
|
||||
|
@ -33,6 +33,7 @@ from flask import g, url_for
|
||||
from flask_login import current_user
|
||||
from app.auth.models import User
|
||||
|
||||
from app.models import ModuleImpl
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
|
||||
@ -156,7 +157,8 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0):
|
||||
|
||||
def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
"""Tableau de bord module (liste des evaluations etc)"""
|
||||
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
|
||||
modimpl = ModuleImpl.query.get_or_404(moduleimpl_id)
|
||||
M = modimpl.to_dict()
|
||||
formsemestre_id = M["formsemestre_id"]
|
||||
Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
@ -224,10 +226,17 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
H.append("""</td><td>""")
|
||||
if not sem["etat"]:
|
||||
H.append(scu.icontag("lock32_img", title="verrouillé"))
|
||||
H.append(
|
||||
"""</td><td class="fichetitre2">Coef dans le semestre: %(coefficient)s</td><td></td></tr>"""
|
||||
% Mod
|
||||
)
|
||||
H.append("""</td><td class="fichetitre2">Coef dans le semestre: """)
|
||||
if modimpl.module.is_apc():
|
||||
coefs_descr = modimpl.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>"""
|
||||
H.append(coefs_descr_txt)
|
||||
else:
|
||||
H.append(f"{modimpl.module.coefficient}")
|
||||
H.append("""</td><td></td></tr>""")
|
||||
# 3ieme ligne: Formation
|
||||
H.append(
|
||||
"""<tr><td class="fichetitre2">Formation: </td><td>%(titre)s</td></tr>""" % F
|
||||
@ -289,6 +298,13 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
|
||||
H.append("</td></tr></table>")
|
||||
#
|
||||
if not modimpl.check_apc_conformity():
|
||||
H.append(
|
||||
"""<ul class="tf-msg"><li class="tf-msg warning conformite">Les poids des évaluations de ce module ne sont pas encore conformes au PN.
|
||||
Ses notes ne peuvent pas être prises en compte dans les moyennes d'UE.
|
||||
</li></ul>"""
|
||||
)
|
||||
#
|
||||
if has_expression and nt.expr_diagnostics:
|
||||
H.append(sco_formsemestre_status.html_expr_diagnostic(nt.expr_diagnostics))
|
||||
#
|
||||
|
Loading…
Reference in New Issue
Block a user