1
0
forked from ScoDoc/ScoDoc

Bonus sport multiplicatifs ou additifs sur bulletins DUT et BUT

This commit is contained in:
Emmanuel Viennet 2022-01-29 22:45:39 +01:00
parent 7e4459a15e
commit 23672bebde
8 changed files with 186 additions and 112 deletions

View File

@ -28,10 +28,12 @@ class BulletinBUT(ResultatsSemestreBUT):
"dict synthèse résultats dans l'UE pour les modules indiqués"
d = {}
etud_idx = self.etud_index[etud.id]
if ue.type != UE_SPORT:
ue_idx = self.modimpl_coefs_df.index.get_loc(ue.id)
etud_moy_module = self.sem_cube[etud_idx] # module x UE
for modimpl in modimpls:
if self.modimpl_inscr_df[str(modimpl.id)][etud.id]: # si inscrit
if ue.type != UE_SPORT:
coef = self.modimpl_coefs_df[modimpl.id][ue.id]
if coef > 0:
d[modimpl.module.code] = {
@ -43,6 +45,12 @@ class BulletinBUT(ResultatsSemestreBUT):
][ue_idx]
),
}
# else: # modules dans UE bonus sport
# d[modimpl.module.code] = {
# "id": modimpl.id,
# "coef": "",
# "moyenne": "?x?",
# }
return d
def etud_ue_results(self, etud, ue):

View File

@ -38,7 +38,7 @@ class BonusSport:
notes moyennes aux modules (tous les étuds x tous les modimpls)
floats avec des NaN.
En classique: sem_matrix, ndarray (etuds x modimpls)
En APC: sem_cube, ndarray (etuds x modimpls x UEs)
En APC: sem_cube, ndarray (etuds x modimpls x UEs non bonus)
- ues: les ues du semestre (incluant le bonus sport)
- modimpl_inscr_df: matrice d'inscription aux modules du semestre (etud x modimpl)
- modimpl_coefs: les coefs des modules
@ -50,10 +50,9 @@ class BonusSport:
etud_moy_gen et etud_moy_ue ne sont PAS modifiés (mais utilisés par certains bonus non additifs).
"""
# Si vrai, en APC, si le bonus UE est None, reporte le bonus moy gen:
apc_apply_bonus_mg_to_ues = True
# Si True, reporte toujours le bonus moy gen sur les UE (même en formations classiques)
apply_bonus_mg_to_ues = False
# En classique, active un bonus sur les UEs: (dans ce cas bonus_moy_gen reste None)
classic_use_bonus_ues = False
# Attributs virtuels:
seuil_moy_gen = None
proportion_point = None
@ -77,7 +76,7 @@ class BonusSport:
self.etud_moy_ue = etud_moy_ue
self.etuds_idx = modimpl_inscr_df.index # les étudiants inscrits au semestre
self.bonus_ues: pd.DataFrame = None # virtual
self.bonus_moy_gen: pd.Series = None # virtual
self.bonus_moy_gen: pd.Series = None # virtual (pour formations non apc slt)
# Restreint aux modules standards des UE de type "sport":
modimpl_mask = np.array(
[
@ -94,13 +93,14 @@ class BonusSport:
"liste des modimpls sport"
# Les moyennes des modules "sport": (une par UE en APC)
# donc (nb_etuds, nb_mod_sport, nb_ues_non_bonus)
sem_modimpl_moys_spo = sem_modimpl_moys[:, modimpl_mask]
# Les inscriptions aux modules sport:
modimpl_inscr_spo = modimpl_inscr_df.values[:, modimpl_mask]
# Les coefficients des modules sport (en apc: nb_mod_sport x nb_ue)
# Les coefficients des modules sport (en apc: nb_mod_sport x nb_ue) (toutes ues)
modimpl_coefs_spo = modimpl_coefs[modimpl_mask]
# sem_modimpl_moys_spo est (nb_etuds, nb_mod_sport)
# ou (nb_etuds, nb_mod_sport, nb_ues)
# ou (nb_etuds, nb_mod_sport, nb_ues_non_bonus)
nb_etuds, nb_mod_sport = sem_modimpl_moys_spo.shape[:2]
nb_ues = len(ues)
# Enlève les NaN du numérateur:
@ -115,7 +115,7 @@ class BonusSport:
[modimpl_inscr_spo] * nb_ues_no_bonus, axis=2
)
# Ne prend pas en compte les notes des étudiants non inscrits au module:
# Annule les notes:
# Annule les notes: (nb_etuds, nb_mod_bonus, nb_ues_non_bonus)
sem_modimpl_moys_inscrits = np.where(
modimpl_inscr_spo_stacked, sem_modimpl_moys_no_nan, 0.0
)
@ -151,7 +151,7 @@ class BonusSport:
"""Calcul des bonus: méthode virtuelle à écraser.
Arguments:
- sem_modimpl_moys_inscrits:
ndarray (nb_etuds, mod_sport) ou en APC (nb_etuds, mods_sport, nb_ue)
ndarray (nb_etuds, mod_sport) ou en APC (nb_etuds, mods_sport, nb_ue_non_bonus)
les notes aux modules sports auxquel l'étudiant est inscrit, 0 sinon. Pas de nans.
- modimpl_coefs_etuds_no_nan:
les coefficients: float ndarray
@ -164,24 +164,16 @@ class BonusSport:
"""Les bonus à appliquer aux UE
Résultat: DataFrame de float, index etudid, columns: ue.id
"""
if self.bonus_ues is None and (
(self.apc_apply_bonus_mg_to_ues and self.formsemestre.formation.is_apc())
or self.apply_bonus_mg_to_ues
):
# reporte uniformément le bonus moyenne générale sur les UEs
# (assure la compatibilité de la plupart des anciens bonus avec le BUT)
# ues = self.formsemestre.query_ues(with_sport=False)
ues_idx = [ue.id for ue in self.formsemestre.query_ues(with_sport=False)]
bonus_moy_gen = self.get_bonus_moy_gen()
bonus_ues = np.stack([bonus_moy_gen.values] * len(ues_idx), axis=1)
return pd.DataFrame(bonus_ues, index=self.etuds_idx, columns=ues_idx)
if self.classic_use_bonus_ues or self.formsemestre.formation.is_apc():
return self.bonus_ues
return None
def get_bonus_moy_gen(self):
"""Le bonus à appliquer à la moyenne générale.
Résultat: Series de float, index etudid
"""
if self.formsemestre.formation.is_apc():
return None # garde-fou
return self.bonus_moy_gen
@ -200,8 +192,12 @@ class BonusSportAdditif(BonusSport):
proportion_point = 0.05 # multiplie les points au dessus du seuil
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
"""calcul du bonus"""
bonus_moy_gen_arr = np.sum(
"""calcul du bonus
sem_modimpl_moys_inscrits: les notes de sport
En APC: ndarray (nb_etuds, nb_mod_sport, nb_ues_non_bonus)
modimpl_coefs_etuds_no_nan:
"""
bonus_moy_arr = np.sum(
np.where(
sem_modimpl_moys_inscrits > self.seuil_moy_gen,
(sem_modimpl_moys_inscrits - self.seuil_moy_gen)
@ -210,18 +206,28 @@ class BonusSportAdditif(BonusSport):
),
axis=1,
)
# en APC, applati la moyenne gen. XXX pourrait être fait en amont
if len(bonus_moy_gen_arr.shape) > 1:
bonus_moy_gen_arr = bonus_moy_gen_arr.sum(axis=1)
# Bonus moyenne générale, et 0 sur les UE
self.bonus_moy_gen = pd.Series(
bonus_moy_gen_arr, index=self.etuds_idx, dtype=float
)
if self.bonus_max is not None:
# Seuil: bonus (sur moy. gen.) limité à bonus_max points
self.bonus_moy_gen = self.bonus_moy_gen.clip(upper=self.bonus_max)
# Seuil: bonus limité à bonus_max points (et >= 0)
bonus_moy_arr = np.clip(
bonus_moy_arr, 0.0, self.bonus_max, out=bonus_moy_arr
)
# Laisse bonus_ues à None, en APC le bonus moy. gen. sera réparti sur les UEs.
# en APC, bonus_moy_arr est (nb_etuds, nb_ues_non_bonus)
if self.formsemestre.formation.is_apc() or self.classic_use_bonus_ues:
# Bonus sur les UE et None sur moyenne générale
ues_idx = [ue.id for ue in self.formsemestre.query_ues(with_sport=False)]
self.bonus_ues = pd.DataFrame(
bonus_moy_arr, index=self.etuds_idx, columns=ues_idx, dtype=float
)
else:
# Bonus sur la moyenne générale seulement
self.bonus_moy_gen = pd.Series(
bonus_moy_arr, index=self.etuds_idx, dtype=float
)
# if len(bonus_moy_arr.shape) > 1:
# bonus_moy_arr = bonus_moy_arr.sum(axis=1)
# Laisse bonus_moy_gen à None, en APC le bonus moy. gen. sera réparti sur les UEs.
class BonusSportMultiplicatif(BonusSport):
@ -229,6 +235,8 @@ class BonusSportMultiplicatif(BonusSport):
seuil_moy_gen = 10.0 # seuls les points au dessus du seuil sont comptés
amplitude = 0.005 # multiplie les points au dessus du seuil
# En classique, les bonus multiplicatifs agissent par défaut sur les UE:
classic_use_bonus_ues = True
# C'est un bonus "multiplicatif": on l'exprime en additif,
# sur chaque moyenne d'UE m_0
@ -243,11 +251,12 @@ class BonusSportMultiplicatif(BonusSport):
sem_modimpl_moys_inscrits * modimpl_coefs_etuds_no_nan, axis=1
) / np.sum(modimpl_coefs_etuds_no_nan, axis=1)
notes = np.nan_to_num(notes, copy=False)
factor = (notes - self.seuil_moy_gen) * self.amplitude # 5% si note=20
factor[factor <= 0] = 0.0 # note < seuil_moy_gen, pas de bonus
# Ne s'applique qu'aux moyennes d'UE
if len(factor.shape) == 1: # classic
factor = factor[:, np.newaxis]
bonus = self.etud_moy_ue * factor
if self.bonus_max is not None:
# Seuil: bonus limité à bonus_max points
@ -255,9 +264,8 @@ class BonusSportMultiplicatif(BonusSport):
self.bonus_ues = bonus # DataFrame
if not self.formsemestre.formation.is_apc():
# s'applique à la moyenne générale
self.bonus_moy_gen = bonus
# Les bonus multiplicatifs ne s'appliquent pas à la moyenne générale
self.bonus_moy_gen = None
class BonusDirect(BonusSportAdditif):
@ -323,6 +331,7 @@ class BonusBordeaux1(BonusSportMultiplicatif):
name = "bonus_iutBordeaux1"
displayed_name = "IUT de Bordeaux 1"
classic_use_bonus_ues = True # s'applique aux UEs en DUT et LP
seuil_moy_gen = 10.0
amplitude = 0.005
@ -576,17 +585,15 @@ class BonusVilleAvray(BonusSport):
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
"""calcul du bonus"""
# Calcule moyenne pondérée des notes de sport:
bonus_moy_gen_arr = np.sum(
bonus_moy_arr = np.sum(
sem_modimpl_moys_inscrits * modimpl_coefs_etuds_no_nan, axis=1
) / np.sum(modimpl_coefs_etuds_no_nan, axis=1)
bonus_moy_gen_arr[bonus_moy_gen_arr >= 10.0] = 0.1
bonus_moy_gen_arr[bonus_moy_gen_arr >= 12.0] = 0.2
bonus_moy_gen_arr[bonus_moy_gen_arr >= 16.0] = 0.3
bonus_moy_arr[bonus_moy_arr >= 10.0] = 0.1
bonus_moy_arr[bonus_moy_arr >= 12.0] = 0.2
bonus_moy_arr[bonus_moy_arr >= 16.0] = 0.3
# Bonus moyenne générale, et 0 sur les UE
self.bonus_moy_gen = pd.Series(
bonus_moy_gen_arr, index=self.etuds_idx, dtype=float
)
self.bonus_moy_gen = pd.Series(bonus_moy_arr, index=self.etuds_idx, dtype=float)
if self.bonus_max is not None:
# Seuil: bonus (sur moy. gen.) limité à bonus_max points
self.bonus_moy_gen = self.bonus_moy_gen.clip(upper=self.bonus_max)

View File

@ -40,6 +40,7 @@ import pandas as pd
from app import db
from app.models import ModuleImpl, Evaluation, EvaluationUEPoids
from app.scodoc import sco_utils as scu
from app.scodoc.sco_codes_parcours import UE_SPORT
from app.scodoc.sco_exceptions import ScoValueError
@ -269,7 +270,8 @@ def load_evaluations_poids(moduleimpl_id: int) -> tuple[pd.DataFrame, list]:
"""Charge poids des évaluations d'un module et retourne un dataframe
rows = evaluations, columns = UE, value = poids (float).
Les valeurs manquantes (évaluations sans coef vers des UE) sont
remplies: 1 si le coef de ce module dans l'UE est non nul, zéro sinon.
remplies: 1 si le coef de ce module dans l'UE est non nul, zéro sinon
(sauf pour module bonus, defaut à 1)
Résultat: (evals_poids, liste de UEs du semestre sauf le sport)
"""
modimpl: ModuleImpl = ModuleImpl.query.get(moduleimpl_id)
@ -287,11 +289,13 @@ def load_evaluations_poids(moduleimpl_id: int) -> tuple[pd.DataFrame, list]:
pass # poids vers des UE qui n'existent plus ou sont dans un autre semestre...
# Initialise poids non enregistrés:
default_poids = 1.0 if modimpl.module.ue.type == UE_SPORT else 0.0
if np.isnan(evals_poids.values.flat).any():
ue_coefs = modimpl.module.get_ue_coef_dict()
for ue in ues:
evals_poids[ue.id][evals_poids[ue.id].isna()] = (
1 if ue_coefs.get(ue.id, 0.0) > 0 else 0
1 if ue_coefs.get(ue.id, default_poids) > 0 else 0
)
return evals_poids, ues

View File

@ -36,6 +36,7 @@ from app.models import UniteEns, Module, ModuleImpl, ModuleUECoef
from app.comp import moy_mod
from app.models.formsemestre import FormSemestre
from app.scodoc import sco_codes_parcours
from app.scodoc.sco_codes_parcours import UE_SPORT
from app.scodoc.sco_utils import ModuleType
@ -44,10 +45,10 @@ def df_load_module_coefs(formation_id: int, semestre_idx: int = None) -> pd.Data
En APC, ces coefs lient les modules à chaque UE.
Résultat: (module_coefs_df, ues, modules)
Résultat: (module_coefs_df, ues_no_bonus, modules)
DataFrame rows = UEs, columns = modules, value = coef.
Considère toutes les UE (sauf sport) et modules du semestre.
Considère toutes les UE sauf bonus et tous les modules du semestre.
Les coefs non définis (pas en base) sont mis à zéro.
Si semestre_idx None, prend toutes les UE de la formation.
@ -92,7 +93,17 @@ def df_load_module_coefs(formation_id: int, semestre_idx: int = None) -> pd.Data
module_coefs_df[mod_coef.module_id][mod_coef.ue_id] = mod_coef.coef
# silently ignore coefs associated to other modules (ie when module_type is changed)
module_coefs_df.fillna(value=0, inplace=True)
# Initialisation des poids non fixés:
# 0 pour modules normaux, 1. pour bonus (car par défaut, on veut qu'un bonus agisse
# sur toutes les UE)
default_poids = {
mod.id: 1.0
if (mod.module_type == ModuleType.STANDARD) and (mod.ue.type == UE_SPORT)
else 0.0
for mod in modules
}
module_coefs_df.fillna(value=default_poids, inplace=True)
return module_coefs_df, ues, modules
@ -104,9 +115,9 @@ def df_load_modimpl_coefs(
Comme df_load_module_coefs mais prend seulement les UE
et modules du formsemestre.
Si ues et modimpls sont None, prend tous ceux du formsemestre.
Si ues et modimpls sont None, prend tous ceux du formsemestre (sauf ue bonus).
Résultat: (module_coefs_df, ues, modules)
DataFrame rows = UEs (avec bonus), columns = modimpl, value = coef.
DataFrame rows = UEs (sans bonus), columns = modimpl, value = coef.
"""
if ues is None:
ues = formsemestre.query_ues().all()
@ -124,7 +135,19 @@ def df_load_modimpl_coefs(
for mod_coef in mod_coefs:
modimpl_coefs_df[mod2impl[mod_coef.module_id]][mod_coef.ue_id] = mod_coef.coef
modimpl_coefs_df.fillna(value=0, inplace=True)
# Initialisation des poids non fixés:
# 0 pour modules normaux, 1. pour bonus (car par défaut, on veut qu'un bonus agisse
# sur toutes les UE)
default_poids = {
modimpl.id: 1.0
if (modimpl.module.module_type == ModuleType.STANDARD)
and (modimpl.module.ue.type == UE_SPORT)
else 0.0
for modimpl in formsemestre.modimpls_sorted
}
modimpl_coefs_df.fillna(value=default_poids, inplace=True)
return modimpl_coefs_df, ues, modimpls

View File

@ -40,22 +40,22 @@ class ResultatsSemestreBUT(NotesTableCompat):
) = moy_ue.notes_sem_load_cube(self.formsemestre)
self.modimpl_inscr_df = inscr_mod.df_load_modimpl_inscr(self.formsemestre)
self.modimpl_coefs_df, _, _ = moy_ue.df_load_modimpl_coefs(
self.formsemestre, ues=self.ues, modimpls=self.formsemestre.modimpls_sorted
self.formsemestre, modimpls=self.formsemestre.modimpls_sorted
)
# l'idx de la colonne du mod modimpl.id est
# modimpl_coefs_df.columns.get_loc(modimpl.id)
# idx de l'UE: modimpl_coefs_df.index.get_loc(ue.id)
# Elimine les coefs des UE bonus sports
no_bonus = [ue.type != UE_SPORT for ue in self.ues]
modimpl_coefs_no_bonus_df = self.modimpl_coefs_df[no_bonus]
# Elimine les coefs des UE bonus sports XXX inutile car df_load_modimpl_coefs sans bonus
# no_bonus = [ue.type != UE_SPORT for ue in self.ues]
# modimpl_coefs_no_bonus_df = self.modimpl_coefs_df[no_bonus]
self.etud_moy_ue = moy_ue.compute_ue_moys_apc(
self.sem_cube,
self.etuds,
self.formsemestre.modimpls_sorted,
self.ues,
self.modimpl_inscr_df,
modimpl_coefs_no_bonus_df,
self.modimpl_coefs_df,
)
# Les coefficients d'UE ne sont pas utilisés en APC
self.etud_coef_ue_df = pd.DataFrame(
@ -63,6 +63,12 @@ class ResultatsSemestreBUT(NotesTableCompat):
)
# --- Bonus Sport & Culture
modimpl_sport = [
modimpl
for modimpl in self.formsemestre.modimpls_sorted
if modimpl.module.ue.type == UE_SPORT
]
if len(modimpl_sport) > 0:
bonus_class = ScoDocSiteConfig.get_bonus_sport_class()
if bonus_class is not None:
bonus: BonusSport = bonus_class(
@ -80,8 +86,10 @@ class ResultatsSemestreBUT(NotesTableCompat):
self.etud_moy_ue.clip(lower=0.0, upper=20.0, inplace=True)
# Moyenne générale indicative:
# (note: le bonus sport a déjà été appliqué aux moyenens d'UE, et impacte
# donc la moyenne indicative)
self.etud_moy_gen = moy_sem.compute_sem_moys_apc(
self.etud_moy_ue, modimpl_coefs_no_bonus_df
self.etud_moy_ue, self.modimpl_coefs_df
)
self.etud_moy_gen_ranks = moy_sem.comp_ranks_series(self.etud_moy_gen)

View File

@ -219,18 +219,18 @@ class NotesTableCompat(ResultatsSemestre):
ues.append(d)
return ues
def get_modimpls_dict(self, ue_id=None):
def get_modimpls_dict(self, ue_id=None) -> list[dict]:
"""Liste des modules pour une UE (ou toutes si ue_id==None),
triés par numéros (selon le type de formation)
"""
if ue_id is None:
return [m.to_dict() for m in self.formsemestre.modimpls_sorted]
else:
return [
m.to_dict()
for m in self.formsemestre.modimpls_sorted
if m.module.ue.id == ue_id
]
modimpls_dict = []
for modimpl in self.formsemestre.modimpls_sorted:
if ue_id == None or modimpl.module.ue.id == ue_id:
d = modimpl.to_dict()
# compat ScoDoc < 9.2: ajoute matières
d["mat"] = modimpl.module.matiere.to_dict()
modimpls_dict.append(d)
return modimpls_dict
def get_etud_decision_sem(self, etudid: int) -> dict:
"""Decision du jury prise pour cet etudiant, ou None s'il n'y en pas eu.
@ -259,13 +259,10 @@ class NotesTableCompat(ResultatsSemestre):
return ""
return ins.etat
def get_etud_moy_gen(self, etudid): # -> float | str
"""Moyenne générale de cet etudiant dans ce semestre.
Prend(ra) en compte les UE capitalisées. (TODO) XXX
Si apc, moyenne indicative.
Si pas de notes: 'NA'
"""
return self.etud_moy_gen[etudid]
def get_etud_mat_moy(self, matiere_id, etudid):
"""moyenne d'un étudiant dans une matière (ou NA si pas de notes)"""
# non supporté en 9.2
return "na"
def get_etud_mod_moy(self, moduleimpl_id: int, etudid: int) -> float:
"""La moyenne de l'étudiant dans le moduleimpl
@ -274,6 +271,14 @@ class NotesTableCompat(ResultatsSemestre):
"""
raise NotImplementedError() # virtual method
def get_etud_moy_gen(self, etudid): # -> float | str
"""Moyenne générale de cet etudiant dans ce semestre.
Prend(ra) en compte les UE capitalisées. (TODO) XXX
Si apc, moyenne indicative.
Si pas de notes: 'NA'
"""
return self.etud_moy_gen[etudid]
def get_etud_ue_status(self, etudid: int, ue_id: int):
coef_ue = self.etud_coef_ue_df[ue_id][etudid]
return {

View File

@ -161,3 +161,16 @@ class Matiere(db.Model):
numero = db.Column(db.Integer) # ordre de présentation
modules = db.relationship("Module", lazy="dynamic", backref="matiere")
def __repr__(self):
return f"""<{self.__class__.__name__}(id={self.id}, ue_id={
self.ue_id}, titre='{self.titre}')>"""
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
e["ue_id"] = self.id
e["numero"] = e["numero"] if e["numero"] else 0
return e

View File

@ -310,7 +310,10 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
else:
x = ""
if isinstance(x, str):
if nt.bonus_ues is None:
u["cur_moy_ue_txt"] = "pas de bonus"
else:
u["cur_moy_ue_txt"] = "bonus appliqué sur les UEs"
else:
u["cur_moy_ue_txt"] = "bonus de %.3g points" % x
u["moy_ue_txt"] = scu.fmt_note(ue_status["moy"])
@ -380,7 +383,7 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
)
else:
if prefs["bul_show_ue_rangs"] and ue["type"] != sco_codes_parcours.UE_SPORT:
if ue_attente: # nt.get_moduleimpls_attente():
if ue_attente or nt.ue_rangs[ue["ue_id"]][0] is None:
u["ue_descr_txt"] = "%s/%s" % (
scu.RANG_ATTENTE_STR,
nt.ue_rangs[ue["ue_id"]][1],
@ -398,8 +401,8 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
I["ues"].append(u) # ne montre pas les UE si non inscrit
# Accès par matieres
# voir si on supporte encore cela en #sco92 XXX
# I["matieres_modules"].update(_sort_mod_by_matiere(modules, nt, etudid))
# En #sco92, pas d'information
I["matieres_modules"].update(_sort_mod_by_matiere(modules, nt, etudid))
#
C = make_context_dict(I["sem"], I["etud"])
@ -616,6 +619,9 @@ def _ue_mod_bulletin(etudid, formsemestre_id, ue_id, modimpls, nt, version):
# Classement
if bul_show_mod_rangs and mod["mod_moy_txt"] != "-" and not is_malus:
rg = nt.mod_rangs[modimpl["moduleimpl_id"]]
if rg[0] is None:
mod["mod_rang_txt"] = ""
else:
if mod_attente: # nt.get_moduleimpls_attente():
mod["mod_rang"] = scu.RANG_ATTENTE_STR
else: