forked from ScoDoc/ScoDoc
Merge branch 'dev92' of https://scodoc.org/git/ScoDoc/ScoDoc into entreprises
This commit is contained in:
commit
60552feec7
@ -198,7 +198,10 @@ class BonusSportAdditif(BonusSport):
|
|||||||
à la moyenne générale du semestre déjà obtenue par l'étudiant.
|
à la moyenne générale du semestre déjà obtenue par l'étudiant.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
seuil_moy_gen = 10.0 # seuls les points au dessus du seuil sont comptés
|
seuil_moy_gen = 10.0 # seuls les bonus au dessus du seuil sont pris en compte
|
||||||
|
seuil_comptage = (
|
||||||
|
None # les points au dessus du seuil sont comptés (defaut: seuil_moy_gen)
|
||||||
|
)
|
||||||
proportion_point = 0.05 # multiplie les points au dessus du seuil
|
proportion_point = 0.05 # multiplie les points au dessus du seuil
|
||||||
|
|
||||||
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
||||||
@ -211,11 +214,13 @@ class BonusSportAdditif(BonusSport):
|
|||||||
if 0 in sem_modimpl_moys_inscrits.shape:
|
if 0 in sem_modimpl_moys_inscrits.shape:
|
||||||
# pas d'étudiants ou pas d'UE ou pas de module...
|
# pas d'étudiants ou pas d'UE ou pas de module...
|
||||||
return
|
return
|
||||||
|
seuil_comptage = (
|
||||||
|
self.seuil_moy_gen if self.seuil_comptage is None else self.seuil_comptage
|
||||||
|
)
|
||||||
bonus_moy_arr = np.sum(
|
bonus_moy_arr = np.sum(
|
||||||
np.where(
|
np.where(
|
||||||
sem_modimpl_moys_inscrits > self.seuil_moy_gen,
|
sem_modimpl_moys_inscrits > self.seuil_moy_gen,
|
||||||
(sem_modimpl_moys_inscrits - self.seuil_moy_gen)
|
(sem_modimpl_moys_inscrits - seuil_comptage) * self.proportion_point,
|
||||||
* self.proportion_point,
|
|
||||||
0.0,
|
0.0,
|
||||||
),
|
),
|
||||||
axis=1,
|
axis=1,
|
||||||
@ -338,9 +343,12 @@ class BonusAisneStQuentin(BonusSportAdditif):
|
|||||||
# pas d'étudiants ou pas d'UE ou pas de module...
|
# pas d'étudiants ou pas d'UE ou pas de module...
|
||||||
return
|
return
|
||||||
# Calcule moyenne pondérée des notes de sport:
|
# Calcule moyenne pondérée des notes de sport:
|
||||||
bonus_moy_arr = np.sum(
|
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
|
||||||
sem_modimpl_moys_inscrits * modimpl_coefs_etuds_no_nan, axis=1
|
bonus_moy_arr = np.sum(
|
||||||
) / np.sum(modimpl_coefs_etuds_no_nan, axis=1)
|
sem_modimpl_moys_inscrits * modimpl_coefs_etuds_no_nan, axis=1
|
||||||
|
) / np.sum(modimpl_coefs_etuds_no_nan, axis=1)
|
||||||
|
np.nan_to_num(bonus_moy_arr, nan=0.0, copy=False)
|
||||||
|
|
||||||
bonus_moy_arr[bonus_moy_arr < 10.0] = 0.0
|
bonus_moy_arr[bonus_moy_arr < 10.0] = 0.0
|
||||||
bonus_moy_arr[bonus_moy_arr >= 18.1] = 0.5
|
bonus_moy_arr[bonus_moy_arr >= 18.1] = 0.5
|
||||||
bonus_moy_arr[bonus_moy_arr >= 16.1] = 0.4
|
bonus_moy_arr[bonus_moy_arr >= 16.1] = 0.4
|
||||||
@ -604,8 +612,9 @@ class BonusLaRochelle(BonusSportAdditif):
|
|||||||
|
|
||||||
name = "bonus_iutlr"
|
name = "bonus_iutlr"
|
||||||
displayed_name = "IUT de La Rochelle"
|
displayed_name = "IUT de La Rochelle"
|
||||||
seuil_moy_gen = 10.0 # tous les points sont comptés
|
seuil_moy_gen = 10.0 # si bonus > 10,
|
||||||
proportion_point = 0.01
|
seuil_comptage = 0.0 # tous les points sont comptés
|
||||||
|
proportion_point = 0.01 # 1%
|
||||||
|
|
||||||
|
|
||||||
class BonusLeHavre(BonusSportMultiplicatif):
|
class BonusLeHavre(BonusSportMultiplicatif):
|
||||||
@ -823,9 +832,11 @@ class BonusVilleAvray(BonusSport):
|
|||||||
# pas d'étudiants ou pas d'UE ou pas de module...
|
# pas d'étudiants ou pas d'UE ou pas de module...
|
||||||
return
|
return
|
||||||
# Calcule moyenne pondérée des notes de sport:
|
# Calcule moyenne pondérée des notes de sport:
|
||||||
bonus_moy_arr = np.sum(
|
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
|
||||||
sem_modimpl_moys_inscrits * modimpl_coefs_etuds_no_nan, axis=1
|
bonus_moy_arr = np.sum(
|
||||||
) / np.sum(modimpl_coefs_etuds_no_nan, axis=1)
|
sem_modimpl_moys_inscrits * modimpl_coefs_etuds_no_nan, axis=1
|
||||||
|
) / np.sum(modimpl_coefs_etuds_no_nan, axis=1)
|
||||||
|
np.nan_to_num(bonus_moy_arr, nan=0.0, copy=False)
|
||||||
bonus_moy_arr[bonus_moy_arr < 10.0] = 0.0
|
bonus_moy_arr[bonus_moy_arr < 10.0] = 0.0
|
||||||
bonus_moy_arr[bonus_moy_arr >= 16.0] = 0.3
|
bonus_moy_arr[bonus_moy_arr >= 16.0] = 0.3
|
||||||
bonus_moy_arr[bonus_moy_arr >= 12.0] = 0.2
|
bonus_moy_arr[bonus_moy_arr >= 12.0] = 0.2
|
||||||
|
@ -40,13 +40,11 @@ def compute_mat_moys_classic(
|
|||||||
modimpl_mask = np.array(
|
modimpl_mask = np.array(
|
||||||
[m.module.matiere.id == matiere_id for m in formsemestre.modimpls_sorted]
|
[m.module.matiere.id == matiere_id for m in formsemestre.modimpls_sorted]
|
||||||
)
|
)
|
||||||
etud_moy_gen, _, _ = moy_ue.compute_ue_moys_classic(
|
etud_moy_mat = moy_ue.compute_mat_moys_classic(
|
||||||
formsemestre,
|
|
||||||
sem_matrix=sem_matrix,
|
sem_matrix=sem_matrix,
|
||||||
ues=ues,
|
|
||||||
modimpl_inscr_df=modimpl_inscr_df,
|
modimpl_inscr_df=modimpl_inscr_df,
|
||||||
modimpl_coefs=modimpl_coefs,
|
modimpl_coefs=modimpl_coefs,
|
||||||
modimpl_mask=modimpl_mask,
|
modimpl_mask=modimpl_mask,
|
||||||
)
|
)
|
||||||
matiere_moy[matiere_id] = etud_moy_gen
|
matiere_moy[matiere_id] = etud_moy_mat
|
||||||
return matiere_moy
|
return matiere_moy
|
||||||
|
@ -294,7 +294,8 @@ def compute_ue_moys_classic(
|
|||||||
modimpl_coefs: np.array,
|
modimpl_coefs: np.array,
|
||||||
modimpl_mask: np.array,
|
modimpl_mask: np.array,
|
||||||
) -> tuple[pd.Series, pd.DataFrame, pd.DataFrame]:
|
) -> tuple[pd.Series, pd.DataFrame, pd.DataFrame]:
|
||||||
"""Calcul de la moyenne d'UE en mode classique.
|
"""Calcul de la moyenne d'UE et de la moy. générale en mode classique (DUT, LMD, ...).
|
||||||
|
|
||||||
La moyenne d'UE est un nombre (note/20), ou NI ou NA ou ERR
|
La moyenne d'UE est un nombre (note/20), ou NI ou NA ou ERR
|
||||||
NI non inscrit à (au moins un) module de cette UE
|
NI non inscrit à (au moins un) module de cette UE
|
||||||
NA pas de notes disponibles
|
NA pas de notes disponibles
|
||||||
@ -363,7 +364,7 @@ def compute_ue_moys_classic(
|
|||||||
modimpl_coefs_etuds_no_nan_stacked = np.stack(
|
modimpl_coefs_etuds_no_nan_stacked = np.stack(
|
||||||
[modimpl_coefs_etuds_no_nan.T] * nb_ues
|
[modimpl_coefs_etuds_no_nan.T] * nb_ues
|
||||||
)
|
)
|
||||||
# nb_ue x nb_etuds x nb_mods : coefs prenant en compte NaN et inscriptions
|
# nb_ue x nb_etuds x nb_mods : coefs prenant en compte NaN et inscriptions:
|
||||||
coefs = (modimpl_coefs_etuds_no_nan_stacked * ue_modules).swapaxes(1, 2)
|
coefs = (modimpl_coefs_etuds_no_nan_stacked * ue_modules).swapaxes(1, 2)
|
||||||
if coefs.dtype == np.object: # arrive sur des tableaux vides
|
if coefs.dtype == np.object: # arrive sur des tableaux vides
|
||||||
coefs = coefs.astype(np.float)
|
coefs = coefs.astype(np.float)
|
||||||
@ -408,6 +409,68 @@ def compute_ue_moys_classic(
|
|||||||
return etud_moy_gen_s, etud_moy_ue_df, etud_coef_ue_df
|
return etud_moy_gen_s, etud_moy_ue_df, etud_coef_ue_df
|
||||||
|
|
||||||
|
|
||||||
|
def compute_mat_moys_classic(
|
||||||
|
sem_matrix: np.array,
|
||||||
|
modimpl_inscr_df: pd.DataFrame,
|
||||||
|
modimpl_coefs: np.array,
|
||||||
|
modimpl_mask: np.array,
|
||||||
|
) -> pd.Series:
|
||||||
|
"""Calcul de la moyenne sur un sous-enemble de modules en formation CLASSIQUE
|
||||||
|
|
||||||
|
La moyenne est un nombre (note/20 ou NaN.
|
||||||
|
|
||||||
|
Le masque modimpl_mask est un tableau de booléens (un par modimpl) qui
|
||||||
|
permet de sélectionner un sous-ensemble de modules (ceux de la matière d'intérêt).
|
||||||
|
|
||||||
|
sem_matrix: notes moyennes aux modules (tous les étuds x tous les modimpls)
|
||||||
|
ndarray (etuds x modimpls)
|
||||||
|
(floats avec des NaN)
|
||||||
|
etuds : listes des étudiants (dim. 0 de la matrice)
|
||||||
|
modimpl_inscr_df: matrice d'inscription du semestre (etud x modimpl)
|
||||||
|
modimpl_coefs: vecteur des coefficients de modules
|
||||||
|
modimpl_mask: masque des modimpls à prendre en compte
|
||||||
|
|
||||||
|
Résultat:
|
||||||
|
- moyennes: pd.Series, index etudid
|
||||||
|
"""
|
||||||
|
if (not len(modimpl_mask)) or (
|
||||||
|
sem_matrix.shape[0] == 0
|
||||||
|
): # aucun module ou aucun étudiant
|
||||||
|
# etud_moy_gen_s, etud_moy_ue_df, etud_coef_ue_df
|
||||||
|
return pd.Series(
|
||||||
|
[0.0] * len(modimpl_inscr_df.index), index=modimpl_inscr_df.index
|
||||||
|
)
|
||||||
|
# Restreint aux modules sélectionnés:
|
||||||
|
sem_matrix = sem_matrix[:, modimpl_mask]
|
||||||
|
modimpl_inscr = modimpl_inscr_df.values[:, modimpl_mask]
|
||||||
|
modimpl_coefs = modimpl_coefs[modimpl_mask]
|
||||||
|
|
||||||
|
nb_etuds, nb_modules = sem_matrix.shape
|
||||||
|
assert len(modimpl_coefs) == nb_modules
|
||||||
|
|
||||||
|
# Enlève les NaN du numérateur:
|
||||||
|
sem_matrix_no_nan = np.nan_to_num(sem_matrix, nan=0.0)
|
||||||
|
# Ne prend pas en compte les notes des étudiants non inscrits au module:
|
||||||
|
# Annule les notes:
|
||||||
|
sem_matrix_inscrits = np.where(modimpl_inscr, sem_matrix_no_nan, 0.0)
|
||||||
|
# Annule les coefs des modules où l'étudiant n'est pas inscrit:
|
||||||
|
modimpl_coefs_etuds = np.where(
|
||||||
|
modimpl_inscr, np.stack([modimpl_coefs.T] * nb_etuds), 0.0
|
||||||
|
)
|
||||||
|
# Annule les coefs des modules NaN (nb_etuds x nb_mods)
|
||||||
|
modimpl_coefs_etuds_no_nan = np.where(
|
||||||
|
np.isnan(sem_matrix), 0.0, modimpl_coefs_etuds
|
||||||
|
)
|
||||||
|
if modimpl_coefs_etuds_no_nan.dtype == np.object: # arrive sur des tableaux vides
|
||||||
|
modimpl_coefs_etuds_no_nan = modimpl_coefs_etuds_no_nan.astype(np.float)
|
||||||
|
|
||||||
|
etud_moy_mat = (modimpl_coefs_etuds_no_nan * sem_matrix_inscrits).sum(
|
||||||
|
axis=1
|
||||||
|
) / modimpl_coefs_etuds_no_nan.sum(axis=1)
|
||||||
|
|
||||||
|
return pd.Series(etud_moy_mat, index=modimpl_inscr_df.index)
|
||||||
|
|
||||||
|
|
||||||
def compute_malus(
|
def compute_malus(
|
||||||
formsemestre: FormSemestre,
|
formsemestre: FormSemestre,
|
||||||
sem_modimpl_moys: np.array,
|
sem_modimpl_moys: np.array,
|
||||||
|
@ -8,11 +8,13 @@
|
|||||||
"""
|
"""
|
||||||
from flask import g
|
from flask import g
|
||||||
|
|
||||||
|
from app import db
|
||||||
from app.comp.jury import ValidationsSemestre
|
from app.comp.jury import ValidationsSemestre
|
||||||
from app.comp.res_common import ResultatsSemestre
|
from app.comp.res_common import ResultatsSemestre
|
||||||
from app.comp.res_classic import ResultatsSemestreClassic
|
from app.comp.res_classic import ResultatsSemestreClassic
|
||||||
from app.comp.res_but import ResultatsSemestreBUT
|
from app.comp.res_but import ResultatsSemestreBUT
|
||||||
from app.models.formsemestre import FormSemestre
|
from app.models.formsemestre import FormSemestre
|
||||||
|
from app.scodoc import sco_cache
|
||||||
|
|
||||||
|
|
||||||
def load_formsemestre_results(formsemestre: FormSemestre) -> ResultatsSemestre:
|
def load_formsemestre_results(formsemestre: FormSemestre) -> ResultatsSemestre:
|
||||||
@ -23,6 +25,13 @@ def load_formsemestre_results(formsemestre: FormSemestre) -> ResultatsSemestre:
|
|||||||
Search in local cache (g.formsemestre_result_cache)
|
Search in local cache (g.formsemestre_result_cache)
|
||||||
If not in cache, build it and cache it.
|
If not in cache, build it and cache it.
|
||||||
"""
|
"""
|
||||||
|
is_apc = formsemestre.formation.is_apc()
|
||||||
|
if is_apc and formsemestre.semestre_id == -1:
|
||||||
|
formsemestre.semestre_id = 1
|
||||||
|
db.session.add(formsemestre)
|
||||||
|
db.session.commit()
|
||||||
|
sco_cache.invalidate_formsemestre(formsemestre.id)
|
||||||
|
|
||||||
# --- Try local cache (within the same request context)
|
# --- Try local cache (within the same request context)
|
||||||
if not hasattr(g, "formsemestre_results_cache"):
|
if not hasattr(g, "formsemestre_results_cache"):
|
||||||
g.formsemestre_results_cache = {}
|
g.formsemestre_results_cache = {}
|
||||||
@ -30,11 +39,7 @@ def load_formsemestre_results(formsemestre: FormSemestre) -> ResultatsSemestre:
|
|||||||
if formsemestre.id in g.formsemestre_results_cache:
|
if formsemestre.id in g.formsemestre_results_cache:
|
||||||
return g.formsemestre_results_cache[formsemestre.id]
|
return g.formsemestre_results_cache[formsemestre.id]
|
||||||
|
|
||||||
klass = (
|
klass = ResultatsSemestreBUT if is_apc else ResultatsSemestreClassic
|
||||||
ResultatsSemestreBUT
|
|
||||||
if formsemestre.formation.is_apc()
|
|
||||||
else ResultatsSemestreClassic
|
|
||||||
)
|
|
||||||
g.formsemestre_results_cache[formsemestre.id] = klass(formsemestre)
|
g.formsemestre_results_cache[formsemestre.id] = klass(formsemestre)
|
||||||
return g.formsemestre_results_cache[formsemestre.id]
|
return g.formsemestre_results_cache[formsemestre.id]
|
||||||
|
|
||||||
|
@ -207,7 +207,11 @@ class FormSemestre(db.Model):
|
|||||||
modimpls = self.modimpls.all()
|
modimpls = self.modimpls.all()
|
||||||
if self.formation.is_apc():
|
if self.formation.is_apc():
|
||||||
modimpls.sort(
|
modimpls.sort(
|
||||||
key=lambda m: (m.module.module_type, m.module.numero, m.module.code)
|
key=lambda m: (
|
||||||
|
m.module.module_type or 0,
|
||||||
|
m.module.numero or 0,
|
||||||
|
m.module.code or 0,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
modimpls.sort(
|
modimpls.sort(
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from flask import send_file, request
|
from flask import send_file, request
|
||||||
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
|
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app.scodoc import sco_formsemestre
|
from app.scodoc import sco_formsemestre
|
||||||
@ -97,8 +98,12 @@ def pe_view_sem_recap(
|
|||||||
template_latex = ""
|
template_latex = ""
|
||||||
# template fourni via le formulaire Web
|
# template fourni via le formulaire Web
|
||||||
if avis_tmpl_file:
|
if avis_tmpl_file:
|
||||||
template_latex = avis_tmpl_file.read().decode('utf-8')
|
try:
|
||||||
template_latex = template_latex
|
template_latex = avis_tmpl_file.read().decode("utf-8")
|
||||||
|
except UnicodeDecodeError as e:
|
||||||
|
raise ScoValueError(
|
||||||
|
"Données (template) invalides (caractères non UTF8 ?)"
|
||||||
|
) from e
|
||||||
else:
|
else:
|
||||||
# template indiqué dans préférences ScoDoc ?
|
# template indiqué dans préférences ScoDoc ?
|
||||||
template_latex = pe_avislatex.get_code_latex_from_scodoc_preference(
|
template_latex = pe_avislatex.get_code_latex_from_scodoc_preference(
|
||||||
@ -114,7 +119,7 @@ def pe_view_sem_recap(
|
|||||||
footer_latex = ""
|
footer_latex = ""
|
||||||
# template fourni via le formulaire Web
|
# template fourni via le formulaire Web
|
||||||
if footer_tmpl_file:
|
if footer_tmpl_file:
|
||||||
footer_latex = footer_tmpl_file.read().decode('utf-8')
|
footer_latex = footer_tmpl_file.read().decode("utf-8")
|
||||||
footer_latex = footer_latex
|
footer_latex = footer_latex
|
||||||
else:
|
else:
|
||||||
footer_latex = pe_avislatex.get_code_latex_from_scodoc_preference(
|
footer_latex = pe_avislatex.get_code_latex_from_scodoc_preference(
|
||||||
|
@ -300,9 +300,7 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
|||||||
if ue_status["coef_ue"] != None:
|
if ue_status["coef_ue"] != None:
|
||||||
u["coef_ue_txt"] = scu.fmt_coef(ue_status["coef_ue"])
|
u["coef_ue_txt"] = scu.fmt_coef(ue_status["coef_ue"])
|
||||||
else:
|
else:
|
||||||
# C'est un bug:
|
u["coef_ue_txt"] = "-"
|
||||||
log("u=" + pprint.pformat(u))
|
|
||||||
raise Exception("invalid None coef for ue")
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
dpv
|
dpv
|
||||||
|
@ -33,17 +33,12 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# API ScoDoc8 pour les caches:
|
# API pour les caches:
|
||||||
# sco_cache.NotesTableCache.get( formsemestre_id)
|
# sco_cache.MyCache.get( formsemestre_id)
|
||||||
# => sco_cache.NotesTableCache.get(formsemestre_id)
|
# => sco_cache.MyCache.get(formsemestre_id)
|
||||||
#
|
#
|
||||||
# sco_core.inval_cache(formsemestre_id=None, pdfonly=False, formsemestre_id_list=None)
|
# sco_cache.MyCache.delete(formsemestre_id)
|
||||||
# => deprecated, NotesTableCache.invalidate_formsemestre(formsemestre_id=None, pdfonly=False)
|
# sco_cache.MyCache.delete_many(formsemestre_id_list)
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nouvelles fonctions:
|
|
||||||
# sco_cache.NotesTableCache.delete(formsemestre_id)
|
|
||||||
# sco_cache.NotesTableCache.delete_many(formsemestre_id_list)
|
|
||||||
#
|
#
|
||||||
# Bulletins PDF:
|
# Bulletins PDF:
|
||||||
# sco_cache.SemBulletinsPDFCache.get(formsemestre_id, version)
|
# sco_cache.SemBulletinsPDFCache.get(formsemestre_id, version)
|
||||||
@ -203,49 +198,6 @@ class SemInscriptionsCache(ScoDocCache):
|
|||||||
duration = 12 * 60 * 60 # ttl 12h
|
duration = 12 * 60 * 60 # ttl 12h
|
||||||
|
|
||||||
|
|
||||||
class NotesTableCache(ScoDocCache):
|
|
||||||
"""Cache pour les NotesTable
|
|
||||||
Clé: formsemestre_id
|
|
||||||
Valeur: NotesTable instance
|
|
||||||
"""
|
|
||||||
|
|
||||||
prefix = "NT"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get(cls, formsemestre_id, compute=True):
|
|
||||||
"""Returns NotesTable for this formsemestre
|
|
||||||
Search in local cache (g.nt_cache) or global app cache (eg REDIS)
|
|
||||||
If not in cache:
|
|
||||||
If compute is True, build it and cache it
|
|
||||||
Else return None
|
|
||||||
"""
|
|
||||||
# try local cache (same request)
|
|
||||||
if not hasattr(g, "nt_cache"):
|
|
||||||
g.nt_cache = {}
|
|
||||||
else:
|
|
||||||
if formsemestre_id in g.nt_cache:
|
|
||||||
return g.nt_cache[formsemestre_id]
|
|
||||||
# try REDIS
|
|
||||||
key = cls._get_key(formsemestre_id)
|
|
||||||
nt = CACHE.get(key)
|
|
||||||
if nt:
|
|
||||||
g.nt_cache[formsemestre_id] = nt # cache locally (same request)
|
|
||||||
return nt
|
|
||||||
if not compute:
|
|
||||||
return None
|
|
||||||
# Recompute requested table:
|
|
||||||
from app.scodoc import notes_table
|
|
||||||
|
|
||||||
t0 = time.time()
|
|
||||||
nt = notes_table.NotesTable(formsemestre_id)
|
|
||||||
t1 = time.time()
|
|
||||||
_ = cls.set(formsemestre_id, nt) # cache in REDIS
|
|
||||||
t2 = time.time()
|
|
||||||
log(f"cached formsemestre_id={formsemestre_id} ({(t1-t0):g}s +{(t2-t1):g}s)")
|
|
||||||
g.nt_cache[formsemestre_id] = nt
|
|
||||||
return nt
|
|
||||||
|
|
||||||
|
|
||||||
def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=False)
|
def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=False)
|
||||||
formsemestre_id=None, pdfonly=False
|
formsemestre_id=None, pdfonly=False
|
||||||
):
|
):
|
||||||
@ -278,22 +230,24 @@ def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=Fa
|
|||||||
|
|
||||||
if not pdfonly:
|
if not pdfonly:
|
||||||
# Delete cached notes and evaluations
|
# Delete cached notes and evaluations
|
||||||
NotesTableCache.delete_many(formsemestre_ids)
|
|
||||||
if formsemestre_id:
|
if formsemestre_id:
|
||||||
for fid in formsemestre_ids:
|
for fid in formsemestre_ids:
|
||||||
EvaluationCache.invalidate_sem(fid)
|
EvaluationCache.invalidate_sem(fid)
|
||||||
if hasattr(g, "nt_cache") and fid in g.nt_cache:
|
if (
|
||||||
del g.nt_cache[fid]
|
hasattr(g, "formsemestre_results_cache")
|
||||||
|
and fid in g.formsemestre_results_cache
|
||||||
|
):
|
||||||
|
del g.formsemestre_results_cache[fid]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# optimization when we invalidate all evaluations:
|
# optimization when we invalidate all evaluations:
|
||||||
EvaluationCache.invalidate_all_sems()
|
EvaluationCache.invalidate_all_sems()
|
||||||
if hasattr(g, "nt_cache"):
|
if hasattr(g, "formsemestre_results_cache"):
|
||||||
del g.nt_cache
|
del g.formsemestre_results_cache
|
||||||
SemInscriptionsCache.delete_many(formsemestre_ids)
|
SemInscriptionsCache.delete_many(formsemestre_ids)
|
||||||
|
ResultatsSemestreCache.delete_many(formsemestre_ids)
|
||||||
|
ValidationsSemestreCache.delete_many(formsemestre_ids)
|
||||||
SemBulletinsPDFCache.invalidate_sems(formsemestre_ids)
|
SemBulletinsPDFCache.invalidate_sems(formsemestre_ids)
|
||||||
ResultatsSemestreCache.delete_many(formsemestre_ids)
|
|
||||||
ValidationsSemestreCache.delete_many(formsemestre_ids)
|
|
||||||
|
|
||||||
|
|
||||||
class DefferedSemCacheManager:
|
class DefferedSemCacheManager:
|
||||||
|
@ -51,6 +51,7 @@ import fcntl
|
|||||||
import subprocess
|
import subprocess
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from flask import flash
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
@ -124,6 +125,7 @@ def sco_dump_and_send_db():
|
|||||||
fcntl.flock(x, fcntl.LOCK_UN)
|
fcntl.flock(x, fcntl.LOCK_UN)
|
||||||
|
|
||||||
log("sco_dump_and_send_db: done.")
|
log("sco_dump_and_send_db: done.")
|
||||||
|
flash("Données envoyées au serveur d'assistance")
|
||||||
return "\n".join(H) + html_sco_header.sco_footer()
|
return "\n".join(H) + html_sco_header.sco_footer()
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ def html_edit_formation_apc(
|
|||||||
"""
|
"""
|
||||||
parcours = formation.get_parcours()
|
parcours = formation.get_parcours()
|
||||||
assert parcours.APC_SAE
|
assert parcours.APC_SAE
|
||||||
|
|
||||||
ressources = formation.modules.filter_by(module_type=ModuleType.RESSOURCE).order_by(
|
ressources = formation.modules.filter_by(module_type=ModuleType.RESSOURCE).order_by(
|
||||||
Module.semestre_id, Module.numero, Module.code
|
Module.semestre_id, Module.numero, Module.code
|
||||||
)
|
)
|
||||||
@ -68,6 +69,19 @@ def html_edit_formation_apc(
|
|||||||
).order_by(
|
).order_by(
|
||||||
Module.semestre_id, Module.module_type.desc(), Module.numero, Module.code
|
Module.semestre_id, Module.module_type.desc(), Module.numero, Module.code
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ues_by_sem = {}
|
||||||
|
ects_by_sem = {}
|
||||||
|
for semestre_idx in semestre_ids:
|
||||||
|
ues_by_sem[semestre_idx] = formation.ues.filter_by(
|
||||||
|
semestre_idx=semestre_idx
|
||||||
|
).order_by(UniteEns.semestre_idx, UniteEns.numero, UniteEns.acronyme)
|
||||||
|
ects = [ue.ects for ue in ues_by_sem[semestre_idx]]
|
||||||
|
if None in ects:
|
||||||
|
ects_by_sem[semestre_idx] = '<span class="missing_ue_ects">manquant</span>'
|
||||||
|
else:
|
||||||
|
ects_by_sem[semestre_idx] = sum(ects)
|
||||||
|
|
||||||
arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags()
|
arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags()
|
||||||
|
|
||||||
icons = {
|
icons = {
|
||||||
@ -93,7 +107,8 @@ def html_edit_formation_apc(
|
|||||||
editable=editable,
|
editable=editable,
|
||||||
tag_editable=tag_editable,
|
tag_editable=tag_editable,
|
||||||
icons=icons,
|
icons=icons,
|
||||||
UniteEns=UniteEns,
|
ues_by_sem=ues_by_sem,
|
||||||
|
ects_by_sem=ects_by_sem,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
for semestre_idx in semestre_ids:
|
for semestre_idx in semestre_ids:
|
||||||
|
@ -562,7 +562,7 @@ def module_edit(module_id=None):
|
|||||||
"code",
|
"code",
|
||||||
{
|
{
|
||||||
"size": 10,
|
"size": 10,
|
||||||
"explanation": "code du module (doit être unique dans la formation)",
|
"explanation": "code du module (issu du programme, exemple M1203 ou R2.01. Doit être unique dans la formation)",
|
||||||
"allow_null": False,
|
"allow_null": False,
|
||||||
"validator": lambda val, field, formation_id=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
|
val, field, formation_id, module_id=module_id
|
||||||
@ -701,7 +701,10 @@ def module_edit(module_id=None):
|
|||||||
{
|
{
|
||||||
"title": "Code Apogée",
|
"title": "Code Apogée",
|
||||||
"size": 25,
|
"size": 25,
|
||||||
"explanation": "(optionnel) code élément pédagogique Apogée ou liste de codes ELP séparés par des virgules",
|
"explanation": """(optionnel) code élément pédagogique Apogée ou liste de codes ELP
|
||||||
|
séparés par des virgules (ce code est propre à chaque établissement, se rapprocher
|
||||||
|
du référent Apogée).
|
||||||
|
""",
|
||||||
"validator": lambda val, _: len(val) < APO_CODE_STR_LEN,
|
"validator": lambda val, _: len(val) < APO_CODE_STR_LEN,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -305,7 +305,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||||||
(
|
(
|
||||||
"numero",
|
"numero",
|
||||||
{
|
{
|
||||||
"size": 2,
|
"size": 4,
|
||||||
"explanation": "numéro (1,2,3,4) de l'UE pour l'ordre d'affichage",
|
"explanation": "numéro (1,2,3,4) de l'UE pour l'ordre d'affichage",
|
||||||
"type": "int",
|
"type": "int",
|
||||||
},
|
},
|
||||||
@ -722,16 +722,16 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
|
|||||||
<a href="{url_for('notes.refcomp_show',
|
<a href="{url_for('notes.refcomp_show',
|
||||||
scodoc_dept=g.scodoc_dept, refcomp_id=formation.referentiel_competence.id)}">
|
scodoc_dept=g.scodoc_dept, refcomp_id=formation.referentiel_competence.id)}">
|
||||||
{formation.referentiel_competence.type_titre} {formation.referentiel_competence.specialite_long}
|
{formation.referentiel_competence.type_titre} {formation.referentiel_competence.specialite_long}
|
||||||
</a> """
|
</a> """
|
||||||
msg_refcomp = "changer"
|
msg_refcomp = "changer"
|
||||||
H.append(
|
H.append(
|
||||||
f"""
|
f"""
|
||||||
<ul>
|
<ul>
|
||||||
<li>{descr_refcomp} <a class="stdlink" href="{url_for('notes.refcomp_assoc_formation',
|
<li>{descr_refcomp} <a class="stdlink" href="{url_for('notes.refcomp_assoc_formation',
|
||||||
scodoc_dept=g.scodoc_dept, formation_id=formation_id)
|
scodoc_dept=g.scodoc_dept, formation_id=formation_id)
|
||||||
}">{msg_refcomp}</a>
|
}">{msg_refcomp}</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a class="stdlink" href="{
|
<li> <a class="stdlink" href="{
|
||||||
url_for('notes.edit_modules_ue_coefs', scodoc_dept=g.scodoc_dept, formation_id=formation_id, semestre_idx=semestre_idx)
|
url_for('notes.edit_modules_ue_coefs', scodoc_dept=g.scodoc_dept, formation_id=formation_id, semestre_idx=semestre_idx)
|
||||||
}">éditer les coefficients des ressources et SAÉs</a>
|
}">éditer les coefficients des ressources et SAÉs</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -151,8 +151,14 @@ def formation_export(
|
|||||||
if mod["ects"] is None:
|
if mod["ects"] is None:
|
||||||
del mod["ects"]
|
del mod["ects"]
|
||||||
|
|
||||||
|
filename = f"scodoc_formation_{formation.departement.acronym}_{formation.acronyme or ''}_v{formation.version}"
|
||||||
return scu.sendResult(
|
return scu.sendResult(
|
||||||
F, name="formation", format=format, force_outer_xml_tag=False, attached=True
|
F,
|
||||||
|
name="formation",
|
||||||
|
format=format,
|
||||||
|
force_outer_xml_tag=False,
|
||||||
|
attached=True,
|
||||||
|
filename=filename,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ def formsemestre_createwithmodules():
|
|||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(
|
html_sco_header.sco_header(
|
||||||
page_title="Création d'un semestre",
|
page_title="Création d'un semestre",
|
||||||
javascripts=["libjs/AutoSuggest.js"],
|
javascripts=["libjs/AutoSuggest.js", "js/formsemestre_edit.js"],
|
||||||
cssstyles=["css/autosuggest_inquisitor.css"],
|
cssstyles=["css/autosuggest_inquisitor.css"],
|
||||||
bodyOnLoad="init_tf_form('')",
|
bodyOnLoad="init_tf_form('')",
|
||||||
),
|
),
|
||||||
@ -99,7 +99,7 @@ def formsemestre_editwithmodules(formsemestre_id):
|
|||||||
H = [
|
H = [
|
||||||
html_sco_header.html_sem_header(
|
html_sco_header.html_sem_header(
|
||||||
"Modification du semestre",
|
"Modification du semestre",
|
||||||
javascripts=["libjs/AutoSuggest.js"],
|
javascripts=["libjs/AutoSuggest.js", "js/formsemestre_edit.js"],
|
||||||
cssstyles=["css/autosuggest_inquisitor.css"],
|
cssstyles=["css/autosuggest_inquisitor.css"],
|
||||||
bodyOnLoad="init_tf_form('')",
|
bodyOnLoad="init_tf_form('')",
|
||||||
)
|
)
|
||||||
@ -213,7 +213,10 @@ def do_formsemestre_createwithmodules(edit=False):
|
|||||||
# en APC, ne permet pas de changer de semestre
|
# en APC, ne permet pas de changer de semestre
|
||||||
semestre_id_list = [formsemestre.semestre_id]
|
semestre_id_list = [formsemestre.semestre_id]
|
||||||
else:
|
else:
|
||||||
semestre_id_list = [-1] + list(range(1, NB_SEM + 1))
|
semestre_id_list = list(range(1, NB_SEM + 1))
|
||||||
|
if not formation.is_apc():
|
||||||
|
# propose "pas de semestre" seulement en classique
|
||||||
|
semestre_id_list.insert(0, -1)
|
||||||
|
|
||||||
semestre_id_labels = []
|
semestre_id_labels = []
|
||||||
for sid in semestre_id_list:
|
for sid in semestre_id_list:
|
||||||
@ -341,6 +344,9 @@ def do_formsemestre_createwithmodules(edit=False):
|
|||||||
"explanation": "en BUT, on ne peut pas modifier le semestre après création"
|
"explanation": "en BUT, on ne peut pas modifier le semestre après création"
|
||||||
if formation.is_apc()
|
if formation.is_apc()
|
||||||
else "",
|
else "",
|
||||||
|
"attributes": ['onchange="change_semestre_id();"']
|
||||||
|
if formation.is_apc()
|
||||||
|
else "",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -493,7 +499,8 @@ def do_formsemestre_createwithmodules(edit=False):
|
|||||||
{
|
{
|
||||||
"input_type": "boolcheckbox",
|
"input_type": "boolcheckbox",
|
||||||
"title": "",
|
"title": "",
|
||||||
"explanation": "Autoriser tous les enseignants associés à un module à y créer des évaluations",
|
"explanation": """Autoriser tous les enseignants associés
|
||||||
|
à un module à y créer des évaluations""",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@ -534,11 +541,19 @@ def do_formsemestre_createwithmodules(edit=False):
|
|||||||
]
|
]
|
||||||
|
|
||||||
nbmod = 0
|
nbmod = 0
|
||||||
if edit:
|
|
||||||
templ_sep = "<tr><td>%(label)s</td><td><b>Responsable</b></td><td><b>Inscrire</b></td></tr>"
|
|
||||||
else:
|
|
||||||
templ_sep = "<tr><td>%(label)s</td><td><b>Responsable</b></td></tr>"
|
|
||||||
for semestre_id in semestre_ids:
|
for semestre_id in semestre_ids:
|
||||||
|
if formation.is_apc():
|
||||||
|
# pour restreindre l'édition aux module du semestre sélectionné
|
||||||
|
tr_class = f'class="sem{semestre_id}"'
|
||||||
|
else:
|
||||||
|
tr_class = ""
|
||||||
|
if edit:
|
||||||
|
templ_sep = f"""<tr {tr_class}><td>%(label)s</td><td><b>Responsable</b></td><td><b>Inscrire</b></td></tr>"""
|
||||||
|
else:
|
||||||
|
templ_sep = (
|
||||||
|
f"""<tr {tr_class}><td>%(label)s</td><td><b>Responsable</b></td></tr>"""
|
||||||
|
)
|
||||||
modform.append(
|
modform.append(
|
||||||
(
|
(
|
||||||
"sep",
|
"sep",
|
||||||
@ -588,12 +603,12 @@ def do_formsemestre_createwithmodules(edit=False):
|
|||||||
)
|
)
|
||||||
fcg += "</select>"
|
fcg += "</select>"
|
||||||
itemtemplate = (
|
itemtemplate = (
|
||||||
"""<tr><td class="tf-fieldlabel">%(label)s</td><td class="tf-field">%(elem)s</td><td>"""
|
f"""<tr {tr_class}><td class="tf-fieldlabel">%(label)s</td><td class="tf-field">%(elem)s</td><td>"""
|
||||||
+ fcg
|
+ fcg
|
||||||
+ "</td></tr>"
|
+ "</td></tr>"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
itemtemplate = """<tr><td class="tf-fieldlabel">%(label)s</td><td class="tf-field">%(elem)s</td></tr>"""
|
itemtemplate = f"""<tr {tr_class}><td class="tf-fieldlabel">%(label)s</td><td class="tf-field">%(elem)s</td></tr>"""
|
||||||
modform.append(
|
modform.append(
|
||||||
(
|
(
|
||||||
"MI" + str(mod["module_id"]),
|
"MI" + str(mod["module_id"]),
|
||||||
|
@ -987,7 +987,6 @@ Il y a des notes en attente ! Le classement des étudiants n'a qu'une valeur ind
|
|||||||
def formsemestre_status(formsemestre_id=None):
|
def formsemestre_status(formsemestre_id=None):
|
||||||
"""Tableau de bord semestre HTML"""
|
"""Tableau de bord semestre HTML"""
|
||||||
# porté du DTML
|
# porté du DTML
|
||||||
cnx = ndb.GetDBConnexion()
|
|
||||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True)
|
sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True)
|
||||||
modimpls = sco_moduleimpl.moduleimpl_withmodule_list(
|
modimpls = sco_moduleimpl.moduleimpl_withmodule_list(
|
||||||
formsemestre_id=formsemestre_id
|
formsemestre_id=formsemestre_id
|
||||||
|
96
app/scodoc/sco_groups_exports.py
Normal file
96
app/scodoc/sco_groups_exports.py
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# -*- mode: python -*-
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gestion scolarite IUT
|
||||||
|
#
|
||||||
|
# Copyright (c) 1999 - 2022 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
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""Exports groupes
|
||||||
|
"""
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
from app.scodoc import notesdb as ndb
|
||||||
|
from app.scodoc import sco_excel
|
||||||
|
from app.scodoc import sco_groups_view
|
||||||
|
from app.scodoc import sco_preferences
|
||||||
|
from app.scodoc.gen_tables import GenTable
|
||||||
|
import app.scodoc.sco_utils as scu
|
||||||
|
import sco_version
|
||||||
|
|
||||||
|
|
||||||
|
def groups_list_annotation(group_ids: list[int]) -> list[dict]:
|
||||||
|
"""Renvoie la liste des annotations pour les groupes d"étudiants indiqués
|
||||||
|
Arg: liste des id de groupes
|
||||||
|
Clés: etudid, ine, nip, nom, prenom, date, comment
|
||||||
|
"""
|
||||||
|
cnx = ndb.GetDBConnexion()
|
||||||
|
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||||
|
annotations = []
|
||||||
|
for group_id in group_ids:
|
||||||
|
cursor.execute(
|
||||||
|
"""SELECT i.id AS etudid, i.code_nip, i.code_ine, i.nom, i.prenom, ea.date, ea.comment
|
||||||
|
FROM group_membership gm, identite i, etud_annotations ea
|
||||||
|
WHERE gm.group_id=%(group_ids)s
|
||||||
|
AND gm.etudid=i.id
|
||||||
|
AND i.id=ea.etudid
|
||||||
|
""",
|
||||||
|
{"group_ids": group_id},
|
||||||
|
)
|
||||||
|
annotations += cursor.dictfetchall()
|
||||||
|
return annotations
|
||||||
|
|
||||||
|
|
||||||
|
def groups_export_annotations(group_ids, formsemestre_id=None, format="html"):
|
||||||
|
"""Les annotations"""
|
||||||
|
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
||||||
|
group_ids, formsemestre_id=formsemestre_id
|
||||||
|
)
|
||||||
|
annotations = groups_list_annotation(groups_infos.group_ids)
|
||||||
|
for annotation in annotations:
|
||||||
|
annotation["date_str"] = annotation["date"].strftime("%d/%m/%Y à %Hh%M")
|
||||||
|
if format == "xls":
|
||||||
|
columns_ids = ("etudid", "nom", "prenom", "date", "comment")
|
||||||
|
else:
|
||||||
|
columns_ids = ("etudid", "nom", "prenom", "date_str", "comment")
|
||||||
|
table = GenTable(
|
||||||
|
rows=annotations,
|
||||||
|
columns_ids=columns_ids,
|
||||||
|
titles={
|
||||||
|
"etudid": "etudid",
|
||||||
|
"nom": "Nom",
|
||||||
|
"prenom": "Prénom",
|
||||||
|
"date": "Date",
|
||||||
|
"date_str": "Date",
|
||||||
|
"comment": "Annotation",
|
||||||
|
},
|
||||||
|
origin="Généré par %s le " % sco_version.SCONAME
|
||||||
|
+ scu.timedate_human_repr()
|
||||||
|
+ "",
|
||||||
|
page_title=f"Annotations sur les étudiants de {groups_infos.groups_titles}",
|
||||||
|
caption="Annotations",
|
||||||
|
base_url=groups_infos.base_url,
|
||||||
|
html_sortable=True,
|
||||||
|
html_class="table_leftalign",
|
||||||
|
preferences=sco_preferences.SemPreferences(formsemestre_id),
|
||||||
|
)
|
||||||
|
return table.make_page(format=format)
|
@ -826,6 +826,8 @@ def tab_absences_html(groups_infos, etat=None):
|
|||||||
% groups_infos.groups_query_args,
|
% groups_infos.groups_query_args,
|
||||||
"""<li><a class="stdlink" href="trombino?%s&format=pdflist">Liste d'appel avec photos</a></li>"""
|
"""<li><a class="stdlink" href="trombino?%s&format=pdflist">Liste d'appel avec photos</a></li>"""
|
||||||
% groups_infos.groups_query_args,
|
% groups_infos.groups_query_args,
|
||||||
|
"""<li><a class="stdlink" href="groups_export_annotations?%s">Liste des annotations</a></li>"""
|
||||||
|
% groups_infos.groups_query_args,
|
||||||
"</ul>",
|
"</ul>",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -645,21 +645,30 @@ class ScoDocJSONEncoder(json.JSONEncoder):
|
|||||||
return json.JSONEncoder.default(self, o)
|
return json.JSONEncoder.default(self, o)
|
||||||
|
|
||||||
|
|
||||||
def sendJSON(data, attached=False):
|
def sendJSON(data, attached=False, filename=None):
|
||||||
js = json.dumps(data, indent=1, cls=ScoDocJSONEncoder)
|
js = json.dumps(data, indent=1, cls=ScoDocJSONEncoder)
|
||||||
return send_file(
|
return send_file(
|
||||||
js, filename="sco_data.json", mime=JSON_MIMETYPE, attached=attached
|
js, filename=filename or "sco_data.json", mime=JSON_MIMETYPE, attached=attached
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def sendXML(data, tagname=None, force_outer_xml_tag=True, attached=False, quote=True):
|
def sendXML(
|
||||||
|
data,
|
||||||
|
tagname=None,
|
||||||
|
force_outer_xml_tag=True,
|
||||||
|
attached=False,
|
||||||
|
quote=True,
|
||||||
|
filename=None,
|
||||||
|
):
|
||||||
if type(data) != list:
|
if type(data) != list:
|
||||||
data = [data] # always list-of-dicts
|
data = [data] # always list-of-dicts
|
||||||
if force_outer_xml_tag:
|
if force_outer_xml_tag:
|
||||||
data = [{tagname: data}]
|
data = [{tagname: data}]
|
||||||
tagname += "_list"
|
tagname += "_list"
|
||||||
doc = sco_xml.simple_dictlist2xml(data, tagname=tagname, quote=quote)
|
doc = sco_xml.simple_dictlist2xml(data, tagname=tagname, quote=quote)
|
||||||
return send_file(doc, filename="sco_data.xml", mime=XML_MIMETYPE, attached=attached)
|
return send_file(
|
||||||
|
doc, filename=filename or "sco_data.xml", mime=XML_MIMETYPE, attached=attached
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def sendResult(
|
def sendResult(
|
||||||
@ -669,6 +678,7 @@ def sendResult(
|
|||||||
force_outer_xml_tag=True,
|
force_outer_xml_tag=True,
|
||||||
attached=False,
|
attached=False,
|
||||||
quote_xml=True,
|
quote_xml=True,
|
||||||
|
filename=None,
|
||||||
):
|
):
|
||||||
if (format is None) or (format == "html"):
|
if (format is None) or (format == "html"):
|
||||||
return data
|
return data
|
||||||
@ -679,9 +689,10 @@ def sendResult(
|
|||||||
force_outer_xml_tag=force_outer_xml_tag,
|
force_outer_xml_tag=force_outer_xml_tag,
|
||||||
attached=attached,
|
attached=attached,
|
||||||
quote=quote_xml,
|
quote=quote_xml,
|
||||||
|
filename=filename,
|
||||||
)
|
)
|
||||||
elif format == "json":
|
elif format == "json":
|
||||||
return sendJSON(data, attached=attached)
|
return sendJSON(data, attached=attached, filename=filename)
|
||||||
else:
|
else:
|
||||||
raise ValueError("invalid format: %s" % format)
|
raise ValueError("invalid format: %s" % format)
|
||||||
|
|
||||||
@ -799,7 +810,7 @@ def abbrev_prenom(prenom):
|
|||||||
|
|
||||||
#
|
#
|
||||||
def timedate_human_repr():
|
def timedate_human_repr():
|
||||||
"representation du temps courant pour utilisateur: a localiser"
|
"representation du temps courant pour utilisateur"
|
||||||
return time.strftime("%d/%m/%Y à %Hh%M")
|
return time.strftime("%d/%m/%Y à %Hh%M")
|
||||||
|
|
||||||
|
|
||||||
|
@ -1297,7 +1297,7 @@ th.formsemestre_status_inscrits {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
td.formsemestre_status_code {
|
td.formsemestre_status_code {
|
||||||
width: 2em;
|
/* width: 2em; */
|
||||||
padding-right: 1em;
|
padding-right: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
14
app/static/js/formsemestre_edit.js
Normal file
14
app/static/js/formsemestre_edit.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Formulaire formsemestre_createwithmodules
|
||||||
|
|
||||||
|
function change_semestre_id() {
|
||||||
|
var semestre_id = $("#tf_semestre_id")[0].value;
|
||||||
|
for (var i = -1; i < 12; i++) {
|
||||||
|
$(".sem" + i).hide();
|
||||||
|
}
|
||||||
|
$(".sem" + semestre_id).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(window).on('load', function () {
|
||||||
|
change_semestre_id();
|
||||||
|
});
|
@ -3,11 +3,9 @@
|
|||||||
<div class="formation_list_ues">
|
<div class="formation_list_ues">
|
||||||
<div class="formation_list_ues_titre">Unités d'Enseignement (UEs)</div>
|
<div class="formation_list_ues_titre">Unités d'Enseignement (UEs)</div>
|
||||||
{% for semestre_idx in semestre_ids %}
|
{% for semestre_idx in semestre_ids %}
|
||||||
<div class="formation_list_ues_sem">Semestre S{{semestre_idx}}</div>
|
<div class="formation_list_ues_sem">Semestre S{{semestre_idx}} (ECTS: {{ects_by_sem[semestre_idx] | safe}})</div>
|
||||||
<ul class="apc_ue_list">
|
<ul class="apc_ue_list">
|
||||||
{% for ue in formation.ues.filter_by(semestre_idx=semestre_idx).order_by(
|
{% for ue in ues_by_sem[semestre_idx] %}
|
||||||
UniteEns.semestre_idx, UniteEns.numero, UniteEns.acronyme
|
|
||||||
) %}
|
|
||||||
<li class="notes_ue_list">
|
<li class="notes_ue_list">
|
||||||
{% if editable and not loop.first %}
|
{% if editable and not loop.first %}
|
||||||
<a href="{{ url_for('notes.ue_move',
|
<a href="{{ url_for('notes.ue_move',
|
||||||
|
@ -611,8 +611,7 @@ def SignaleAbsenceGrSemestre(
|
|||||||
"""<option value="%(modimpl_id)s" %(sel)s>%(modname)s</option>\n"""
|
"""<option value="%(modimpl_id)s" %(sel)s>%(modname)s</option>\n"""
|
||||||
% {
|
% {
|
||||||
"modimpl_id": modimpl["moduleimpl_id"],
|
"modimpl_id": modimpl["moduleimpl_id"],
|
||||||
"modname": modimpl["module"]["code"]
|
"modname": (modimpl["module"]["code"] or "")
|
||||||
or ""
|
|
||||||
+ " "
|
+ " "
|
||||||
+ (modimpl["module"]["abbrev"] or modimpl["module"]["titre"]),
|
+ (modimpl["module"]["abbrev"] or modimpl["module"]["titre"]),
|
||||||
"sel": sel,
|
"sel": sel,
|
||||||
|
@ -68,8 +68,6 @@ from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
|
|||||||
import app
|
import app
|
||||||
from app.scodoc.gen_tables import GenTable
|
from app.scodoc.gen_tables import GenTable
|
||||||
from app.scodoc import html_sco_header
|
from app.scodoc import html_sco_header
|
||||||
from app.scodoc import html_sidebar
|
|
||||||
from app.scodoc import imageresize
|
|
||||||
from app.scodoc import sco_import_etuds
|
from app.scodoc import sco_import_etuds
|
||||||
from app.scodoc import sco_abs
|
from app.scodoc import sco_abs
|
||||||
from app.scodoc import sco_archives_etud
|
from app.scodoc import sco_archives_etud
|
||||||
@ -87,12 +85,9 @@ from app.scodoc import sco_formsemestre_edit
|
|||||||
from app.scodoc import sco_formsemestre_inscriptions
|
from app.scodoc import sco_formsemestre_inscriptions
|
||||||
from app.scodoc import sco_groups
|
from app.scodoc import sco_groups
|
||||||
from app.scodoc import sco_groups_edit
|
from app.scodoc import sco_groups_edit
|
||||||
|
from app.scodoc import sco_groups_exports
|
||||||
from app.scodoc import sco_groups_view
|
from app.scodoc import sco_groups_view
|
||||||
from app.scodoc import sco_logos
|
|
||||||
from app.scodoc import sco_news
|
|
||||||
from app.scodoc import sco_page_etud
|
from app.scodoc import sco_page_etud
|
||||||
from app.scodoc import sco_parcours_dut
|
|
||||||
from app.scodoc import sco_permissions
|
|
||||||
from app.scodoc import sco_permissions_check
|
from app.scodoc import sco_permissions_check
|
||||||
from app.scodoc import sco_photos
|
from app.scodoc import sco_photos
|
||||||
from app.scodoc import sco_portal_apogee
|
from app.scodoc import sco_portal_apogee
|
||||||
@ -364,6 +359,12 @@ sco_publish(
|
|||||||
methods=["GET", "POST"],
|
methods=["GET", "POST"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
sco_publish(
|
||||||
|
"/groups_export_annotations",
|
||||||
|
sco_groups_exports.groups_export_annotations,
|
||||||
|
Permission.ScoView,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/groups_view")
|
@bp.route("/groups_view")
|
||||||
@scodoc
|
@scodoc
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
SCOVERSION = "9.2a-66"
|
SCOVERSION = "9.2a-70"
|
||||||
|
|
||||||
SCONAME = "ScoDoc"
|
SCONAME = "ScoDoc"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user