forked from ScoDoc/ScoDoc
Compare commits
10 Commits
44cb716154
...
c0b750dcfb
Author | SHA1 | Date | |
---|---|---|---|
c0b750dcfb | |||
44d56f2493 | |||
2af2ca6c43 | |||
41e065f6ab | |||
60c157222b | |||
735100de60 | |||
f7a42646bc | |||
d666483530 | |||
7712de19a2 | |||
c928ccdcfe |
@ -206,6 +206,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
sco_codes.DEF,
|
sco_codes.DEF,
|
||||||
sco_codes.DEM,
|
sco_codes.DEM,
|
||||||
sco_codes.EXCLU,
|
sco_codes.EXCLU,
|
||||||
|
sco_codes.NAR,
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -256,6 +257,8 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
self.annee_but = (formsemestre_last.semestre_id + 1) // 2
|
self.annee_but = (formsemestre_last.semestre_id + 1) // 2
|
||||||
"le rang de l'année dans le BUT: 1, 2, 3"
|
"le rang de l'année dans le BUT: 1, 2, 3"
|
||||||
assert self.annee_but in (1, 2, 3)
|
assert self.annee_but in (1, 2, 3)
|
||||||
|
self.autorisations_recorded = False
|
||||||
|
"vrai si on a enregistré l'autorisation de passage"
|
||||||
self.rcues_annee = []
|
self.rcues_annee = []
|
||||||
"""RCUEs de l'année
|
"""RCUEs de l'année
|
||||||
(peuvent concerner l'année scolaire antérieur pour les redoublants
|
(peuvent concerner l'année scolaire antérieur pour les redoublants
|
||||||
@ -444,6 +447,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
+ '</div><div class="warning">'.join(messages)
|
+ '</div><div class="warning">'.join(messages)
|
||||||
+ "</div>"
|
+ "</div>"
|
||||||
)
|
)
|
||||||
|
self.codes = [self.codes[0]] + sorted(self.codes[1:])
|
||||||
|
|
||||||
# WIP TODO XXX def get_moyenne_annuelle(self)
|
# WIP TODO XXX def get_moyenne_annuelle(self)
|
||||||
|
|
||||||
@ -749,7 +753,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
dec_ue.record(code)
|
dec_ue.record(code)
|
||||||
for dec_rcue, code in codes_rcues:
|
for dec_rcue, code in codes_rcues:
|
||||||
dec_rcue.record(code)
|
dec_rcue.record(code)
|
||||||
self.record(code_annee, mark_recorded=False)
|
self.record(code_annee) # XXX , mark_recorded=False)
|
||||||
self.record_autorisation_inscription(code_annee)
|
self.record_autorisation_inscription(code_annee)
|
||||||
self.record_all()
|
self.record_all()
|
||||||
self.recorded = True
|
self.recorded = True
|
||||||
@ -792,13 +796,15 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
etudid=self.etud.id,
|
etudid=self.etud.id,
|
||||||
msg=f"Validation année BUT{self.annee_but}: {code}",
|
msg=f"Validation année BUT{self.annee_but}: {code}",
|
||||||
)
|
)
|
||||||
if mark_recorded:
|
if mark_recorded:
|
||||||
self.recorded = True
|
self.recorded = True
|
||||||
self.invalidate_formsemestre_cache()
|
self.invalidate_formsemestre_cache()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def record_autorisation_inscription(self, code: str):
|
def record_autorisation_inscription(self, code: str):
|
||||||
"""Autorisation d'inscription dans semestre suivant"""
|
"""Autorisation d'inscription dans semestre suivant"""
|
||||||
|
if self.autorisations_recorded:
|
||||||
|
return
|
||||||
if self.inscription_etat != scu.INSCRIT:
|
if self.inscription_etat != scu.INSCRIT:
|
||||||
# les dem et DEF ne continuent jamais
|
# les dem et DEF ne continuent jamais
|
||||||
return
|
return
|
||||||
@ -813,6 +819,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
self.formsemestre.id,
|
self.formsemestre.id,
|
||||||
next_semestre_id,
|
next_semestre_id,
|
||||||
)
|
)
|
||||||
|
self.autorisations_recorded = True
|
||||||
|
|
||||||
def invalidate_formsemestre_cache(self):
|
def invalidate_formsemestre_cache(self):
|
||||||
"invalide le résultats des deux formsemestres"
|
"invalide le résultats des deux formsemestres"
|
||||||
@ -902,6 +909,8 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
Efface même si étudiant DEM ou DEF.
|
Efface même si étudiant DEM ou DEF.
|
||||||
Si à cheval ou only_one_sem, n'efface que les décisions UE et les
|
Si à cheval ou only_one_sem, n'efface que les décisions UE et les
|
||||||
autorisations de passage du semestre d'origine du deca.
|
autorisations de passage du semestre d'origine du deca.
|
||||||
|
|
||||||
|
Dans tous les cas, efface les validations de l'année en cours.
|
||||||
(commite la session.)
|
(commite la session.)
|
||||||
"""
|
"""
|
||||||
if only_one_sem or self.a_cheval:
|
if only_one_sem or self.a_cheval:
|
||||||
@ -916,8 +925,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
else:
|
else:
|
||||||
for dec_ue in self.decisions_ues.values():
|
for dec_ue in self.decisions_ues.values():
|
||||||
dec_ue.erase()
|
dec_ue.erase()
|
||||||
for dec_rcue in self.decisions_rcue_by_niveau.values():
|
|
||||||
dec_rcue.erase()
|
|
||||||
if self.formsemestre_impair:
|
if self.formsemestre_impair:
|
||||||
ScolarAutorisationInscription.delete_autorisation_etud(
|
ScolarAutorisationInscription.delete_autorisation_etud(
|
||||||
self.etud.id, self.formsemestre_impair.id
|
self.etud.id, self.formsemestre_impair.id
|
||||||
@ -926,21 +934,27 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
ScolarAutorisationInscription.delete_autorisation_etud(
|
ScolarAutorisationInscription.delete_autorisation_etud(
|
||||||
self.etud.id, self.formsemestre_pair.id
|
self.etud.id, self.formsemestre_pair.id
|
||||||
)
|
)
|
||||||
validations = ApcValidationAnnee.query.filter_by(
|
# Efface les RCUEs
|
||||||
|
for dec_rcue in self.decisions_rcue_by_niveau.values():
|
||||||
|
dec_rcue.erase()
|
||||||
|
|
||||||
|
# Efface les validations concernant l'année BUT
|
||||||
|
# de ce semestre
|
||||||
|
validations = (
|
||||||
|
ApcValidationAnnee.query.filter_by(
|
||||||
etudid=self.etud.id,
|
etudid=self.etud.id,
|
||||||
# XXX efface les validations émise depuis ce semestre
|
|
||||||
# et pas toutes celles concernant cette l'année...
|
|
||||||
# (utiliser formation_id pour changer cette politique)
|
|
||||||
formsemestre_id=self.formsemestre_impair.id,
|
|
||||||
ordre=self.annee_but,
|
ordre=self.annee_but,
|
||||||
)
|
)
|
||||||
for validation in validations:
|
.join(Formation)
|
||||||
db.session.delete(validation)
|
.filter_by(formation_code=self.formsemestre.formation.formation_code)
|
||||||
Scolog.logdb(
|
)
|
||||||
"jury_but",
|
for validation in validations:
|
||||||
etudid=self.etud.id,
|
db.session.delete(validation)
|
||||||
msg=f"Validation année BUT{self.annee_but}: effacée",
|
Scolog.logdb(
|
||||||
)
|
"jury_but",
|
||||||
|
etudid=self.etud.id,
|
||||||
|
msg=f"Validation année BUT{self.annee_but}: effacée",
|
||||||
|
)
|
||||||
|
|
||||||
# Efface éventuelles validations de semestre
|
# Efface éventuelles validations de semestre
|
||||||
# (en principe inutilisées en BUT)
|
# (en principe inutilisées en BUT)
|
||||||
@ -1113,6 +1127,7 @@ class DecisionsProposeesRCUE(DecisionsProposees):
|
|||||||
self.codes.insert(0, self.code_valide)
|
self.codes.insert(0, self.code_valide)
|
||||||
else:
|
else:
|
||||||
self.codes.insert(1, self.code_valide)
|
self.codes.insert(1, self.code_valide)
|
||||||
|
self.codes = [self.codes[0]] + sorted(self.codes[1:])
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"""<{self.__class__.__name__} rcue={self.rcue} valid={self.code_valide
|
return f"""<{self.__class__.__name__} rcue={self.rcue} valid={self.code_valide
|
||||||
@ -1286,7 +1301,7 @@ class DecisionsProposeesRCUE(DecisionsProposees):
|
|||||||
sco_cache.invalidate_formsemestre(
|
sco_cache.invalidate_formsemestre(
|
||||||
formsemestre_id=validation_rcue.formsemestre_id
|
formsemestre_id=validation_rcue.formsemestre_id
|
||||||
)
|
)
|
||||||
else:
|
elif ue1 and ue2:
|
||||||
# Crée nouvelle validation
|
# Crée nouvelle validation
|
||||||
validation_rcue = ApcValidationRCUE(
|
validation_rcue = ApcValidationRCUE(
|
||||||
etudid=self.etud.id, ue1_id=ue1.id, ue2_id=ue2.id, code=sco_codes.ADSUP
|
etudid=self.etud.id, ue1_id=ue1.id, ue2_id=ue2.id, code=sco_codes.ADSUP
|
||||||
@ -1380,20 +1395,20 @@ class DecisionsProposeesRCUE(DecisionsProposees):
|
|||||||
"Impossible de valider le niveau de compétence inférieur: pas 2 UEs associées'",
|
"Impossible de valider le niveau de compétence inférieur: pas 2 UEs associées'",
|
||||||
"warning",
|
"warning",
|
||||||
)
|
)
|
||||||
return
|
return [], None, None
|
||||||
ues_impaires = [ue for ue in ues if ue.semestre_idx % 2]
|
ues_impaires = [ue for ue in ues if ue.semestre_idx % 2]
|
||||||
if len(ues_impaires) != 1:
|
if len(ues_impaires) != 1:
|
||||||
flash(
|
flash(
|
||||||
"Impossible de valider le niveau de compétence inférieur: pas d'UE impaire associée"
|
"Impossible de valider le niveau de compétence inférieur: pas d'UE impaire associée"
|
||||||
)
|
)
|
||||||
return
|
return [], None, None
|
||||||
ue1 = ues_impaires[0]
|
ue1 = ues_impaires[0]
|
||||||
ues_paires = [ue for ue in ues if not ue.semestre_idx % 2]
|
ues_paires = [ue for ue in ues if not ue.semestre_idx % 2]
|
||||||
if len(ues_paires) != 1:
|
if len(ues_paires) != 1:
|
||||||
flash(
|
flash(
|
||||||
"Impossible de valider le niveau de compétence inférieur: pas d'UE paire associée"
|
"Impossible de valider le niveau de compétence inférieur: pas d'UE paire associée"
|
||||||
)
|
)
|
||||||
return
|
return [], None, None
|
||||||
ue2 = ues_paires[0]
|
ue2 = ues_paires[0]
|
||||||
return ues, ue1, ue2
|
return ues, ue1, ue2
|
||||||
|
|
||||||
@ -1476,6 +1491,7 @@ class DecisionsProposeesUE(DecisionsProposees):
|
|||||||
self.moy_ue = ue_status["cur_moy_ue"]
|
self.moy_ue = ue_status["cur_moy_ue"]
|
||||||
self.moy_ue_with_cap = ue_status["moy"]
|
self.moy_ue_with_cap = ue_status["moy"]
|
||||||
self.ue_status = ue_status
|
self.ue_status = ue_status
|
||||||
|
self.codes = [self.codes[0]] + sorted(self.codes[1:])
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"""<{self.__class__.__name__} ue={self.ue.acronyme} valid={self.code_valide
|
return f"""<{self.__class__.__name__} ue={self.ue.acronyme} valid={self.code_valide
|
||||||
|
@ -9,16 +9,15 @@
|
|||||||
Non spécifique au BUT.
|
Non spécifique au BUT.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import flask
|
from flask import flash, render_template
|
||||||
from flask import flash, render_template, url_for
|
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
|
|
||||||
from app.models import (
|
from app.models import (
|
||||||
ApcValidationAnnee,
|
ApcValidationAnnee,
|
||||||
ApcValidationRCUE,
|
ApcValidationRCUE,
|
||||||
FormSemestre,
|
|
||||||
Identite,
|
Identite,
|
||||||
UniteEns,
|
UniteEns,
|
||||||
ScolarAutorisationInscription,
|
ScolarAutorisationInscription,
|
||||||
@ -38,7 +37,12 @@ def jury_delete_manual(etud: Identite):
|
|||||||
ue_vals = (
|
ue_vals = (
|
||||||
ScolarFormSemestreValidation.query.filter_by(etudid=etud.id)
|
ScolarFormSemestreValidation.query.filter_by(etudid=etud.id)
|
||||||
.join(UniteEns)
|
.join(UniteEns)
|
||||||
.order_by(ScolarFormSemestreValidation.event_date, UniteEns.numero)
|
.order_by(
|
||||||
|
sa.extract("year", ScolarFormSemestreValidation.event_date),
|
||||||
|
UniteEns.semestre_idx,
|
||||||
|
UniteEns.numero,
|
||||||
|
UniteEns.acronyme,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
autorisations = ScolarAutorisationInscription.query.filter_by(
|
autorisations = ScolarAutorisationInscription.query.filter_by(
|
||||||
etudid=etud.id
|
etudid=etud.id
|
||||||
|
@ -322,7 +322,13 @@ class ModuleImplResultsAPC(ModuleImplResults):
|
|||||||
modimpl = ModuleImpl.query.get(self.moduleimpl_id)
|
modimpl = ModuleImpl.query.get(self.moduleimpl_id)
|
||||||
nb_etuds, nb_evals = self.evals_notes.shape
|
nb_etuds, nb_evals = self.evals_notes.shape
|
||||||
nb_ues = evals_poids_df.shape[1]
|
nb_ues = evals_poids_df.shape[1]
|
||||||
assert evals_poids_df.shape[0] == nb_evals # compat notes/poids
|
if evals_poids_df.shape[0] != nb_evals:
|
||||||
|
# compat notes/poids: race condition ?
|
||||||
|
app.critical_error(
|
||||||
|
f"""compute_module_moy: evals_poids_df.shape[0] != nb_evals ({
|
||||||
|
evals_poids_df.shape[0]} != {nb_evals})
|
||||||
|
"""
|
||||||
|
)
|
||||||
if nb_etuds == 0:
|
if nb_etuds == 0:
|
||||||
return pd.DataFrame(index=[], columns=evals_poids_df.columns)
|
return pd.DataFrame(index=[], columns=evals_poids_df.columns)
|
||||||
if nb_ues == 0:
|
if nb_ues == 0:
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
|
import app
|
||||||
from app import db
|
from app import db
|
||||||
from app import models
|
from app import models
|
||||||
from app.models import (
|
from app.models import (
|
||||||
@ -167,8 +168,14 @@ def notes_sem_assemble_cube(modimpls_notes: list[pd.DataFrame]) -> np.ndarray:
|
|||||||
"""
|
"""
|
||||||
assert len(modimpls_notes)
|
assert len(modimpls_notes)
|
||||||
modimpls_notes_arr = [df.values for df in modimpls_notes]
|
modimpls_notes_arr = [df.values for df in modimpls_notes]
|
||||||
modimpls_notes = np.stack(modimpls_notes_arr)
|
try:
|
||||||
# passe de (mod x etud x ue) à (etud x mod x ue)
|
modimpls_notes = np.stack(modimpls_notes_arr)
|
||||||
|
# passe de (mod x etud x ue) à (etud x mod x ue)
|
||||||
|
except ValueError:
|
||||||
|
app.critical_error(
|
||||||
|
f"""notes_sem_assemble_cube: shapes {
|
||||||
|
", ".join([x.shape for x in modimpls_notes_arr])}"""
|
||||||
|
)
|
||||||
return modimpls_notes.swapaxes(0, 1)
|
return modimpls_notes.swapaxes(0, 1)
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ from datetime import datetime
|
|||||||
import functools
|
import functools
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
|
from flask import g
|
||||||
from flask_sqlalchemy.query import Query
|
from flask_sqlalchemy.query import Query
|
||||||
from sqlalchemy.orm import class_mapper
|
from sqlalchemy.orm import class_mapper
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
@ -412,6 +413,20 @@ class ApcNiveau(db.Model, XMLModel):
|
|||||||
(dans ce cas, spécifier referentiel_competence)
|
(dans ce cas, spécifier referentiel_competence)
|
||||||
Si competence est indiquée, filtre les niveaux de cette compétence.
|
Si competence est indiquée, filtre les niveaux de cette compétence.
|
||||||
"""
|
"""
|
||||||
|
key = (
|
||||||
|
parcour.id if parcour else None,
|
||||||
|
annee,
|
||||||
|
referentiel_competence.id if referentiel_competence else None,
|
||||||
|
competence.id if competence else None,
|
||||||
|
)
|
||||||
|
_cache = getattr(g, "_niveaux_annee_de_parcours_cache", None)
|
||||||
|
if _cache:
|
||||||
|
result = g._niveaux_annee_de_parcours_cache.get(key, False)
|
||||||
|
if result is not False:
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
g._niveaux_annee_de_parcours_cache = {}
|
||||||
|
_cache = g._niveaux_annee_de_parcours_cache
|
||||||
if annee not in {1, 2, 3}:
|
if annee not in {1, 2, 3}:
|
||||||
raise ValueError("annee invalide pour un parcours BUT")
|
raise ValueError("annee invalide pour un parcours BUT")
|
||||||
referentiel_competence = (
|
referentiel_competence = (
|
||||||
@ -428,10 +443,13 @@ class ApcNiveau(db.Model, XMLModel):
|
|||||||
)
|
)
|
||||||
if competence is not None:
|
if competence is not None:
|
||||||
query = query.filter(ApcCompetence.id == competence.id)
|
query = query.filter(ApcCompetence.id == competence.id)
|
||||||
return query.all()
|
result = query.all()
|
||||||
|
_cache[key] = result
|
||||||
|
return result
|
||||||
|
|
||||||
annee_parcour: ApcAnneeParcours = parcour.annees.filter_by(ordre=annee).first()
|
annee_parcour: ApcAnneeParcours = parcour.annees.filter_by(ordre=annee).first()
|
||||||
if not annee_parcour:
|
if not annee_parcour:
|
||||||
|
_cache[key] = []
|
||||||
return []
|
return []
|
||||||
|
|
||||||
if competence is None:
|
if competence is None:
|
||||||
@ -446,6 +464,7 @@ class ApcNiveau(db.Model, XMLModel):
|
|||||||
niveaux: list[ApcNiveau] = competence.niveaux.filter_by(
|
niveaux: list[ApcNiveau] = competence.niveaux.filter_by(
|
||||||
annee=f"BUT{int(annee)}"
|
annee=f"BUT{int(annee)}"
|
||||||
).all()
|
).all()
|
||||||
|
_cache[key] = niveaux
|
||||||
return niveaux
|
return niveaux
|
||||||
|
|
||||||
|
|
||||||
|
@ -352,6 +352,7 @@ class ApcValidationAnnee(db.Model):
|
|||||||
"Affichage html"
|
"Affichage html"
|
||||||
return f"""Validation <b>année BUT{self.ordre}</b> émise par
|
return f"""Validation <b>année BUT{self.ordre}</b> émise par
|
||||||
{self.formsemestre.html_link_status() if self.formsemestre else "-"}
|
{self.formsemestre.html_link_status() if self.formsemestre else "-"}
|
||||||
|
: <b>{self.code}</b>
|
||||||
le {self.date.strftime("%d/%m/%Y")} à {self.date.strftime("%Hh%M")}
|
le {self.date.strftime("%d/%m/%Y")} à {self.date.strftime("%Hh%M")}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""ScoDoc 9 models : Unités d'Enseignement (UE)
|
"""ScoDoc 9 models : Unités d'Enseignement (UE)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from flask import g
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
from app import db, log
|
from app import db, log
|
||||||
@ -8,7 +9,6 @@ from app.models import APO_CODE_STR_LEN
|
|||||||
from app.models import SHORT_STR_LEN
|
from app.models import SHORT_STR_LEN
|
||||||
from app.models.but_refcomp import ApcNiveau, ApcParcours
|
from app.models.but_refcomp import ApcNiveau, ApcParcours
|
||||||
from app.models.modules import Module
|
from app.models.modules import Module
|
||||||
from app.scodoc.sco_exceptions import ScoFormationConflict
|
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
|
|
||||||
|
|
||||||
@ -107,6 +107,17 @@ class UniteEns(db.Model):
|
|||||||
If convert_objects, convert all attributes to native types
|
If convert_objects, convert all attributes to native types
|
||||||
(suitable for json encoding).
|
(suitable for json encoding).
|
||||||
"""
|
"""
|
||||||
|
# cache car très utilisé par anciens codes
|
||||||
|
key = (self.id, convert_objects, with_module_ue_coefs)
|
||||||
|
_cache = getattr(g, "_ue_to_dict_cache", None)
|
||||||
|
if _cache:
|
||||||
|
result = g._ue_to_dict_cache.get(key, False)
|
||||||
|
if result is not False:
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
g._ue_to_dict_cache = {}
|
||||||
|
_cache = g._ue_to_dict_cache
|
||||||
|
|
||||||
e = dict(self.__dict__)
|
e = dict(self.__dict__)
|
||||||
e.pop("_sa_instance_state", None)
|
e.pop("_sa_instance_state", None)
|
||||||
e.pop("evaluation_ue_poids", None)
|
e.pop("evaluation_ue_poids", None)
|
||||||
@ -133,6 +144,7 @@ class UniteEns(db.Model):
|
|||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
e.pop("module_ue_coefs", None)
|
e.pop("module_ue_coefs", None)
|
||||||
|
_cache[key] = e
|
||||||
return e
|
return e
|
||||||
|
|
||||||
def annee(self) -> int:
|
def annee(self) -> int:
|
||||||
@ -180,12 +192,23 @@ class UniteEns(db.Model):
|
|||||||
le parcours indiqué.
|
le parcours indiqué.
|
||||||
"""
|
"""
|
||||||
if parcour is not None:
|
if parcour is not None:
|
||||||
|
key = (parcour.id, self.id, only_parcours)
|
||||||
|
ue_ects_cache = getattr(g, "_ue_ects_cache", None)
|
||||||
|
if ue_ects_cache:
|
||||||
|
ects = g._ue_ects_cache.get(key, False)
|
||||||
|
if ects is not False:
|
||||||
|
return ects
|
||||||
|
else:
|
||||||
|
g._ue_ects_cache = {}
|
||||||
|
ue_ects_cache = g._ue_ects_cache
|
||||||
ue_parcour = UEParcours.query.filter_by(
|
ue_parcour = UEParcours.query.filter_by(
|
||||||
ue_id=self.id, parcours_id=parcour.id
|
ue_id=self.id, parcours_id=parcour.id
|
||||||
).first()
|
).first()
|
||||||
if ue_parcour is not None and ue_parcour.ects is not None:
|
if ue_parcour is not None and ue_parcour.ects is not None:
|
||||||
|
ue_ects_cache[key] = ue_parcour.ects
|
||||||
return ue_parcour.ects
|
return ue_parcour.ects
|
||||||
if only_parcours:
|
if only_parcours:
|
||||||
|
ue_ects_cache[key] = None
|
||||||
return None
|
return None
|
||||||
return self.ects
|
return self.ects
|
||||||
|
|
||||||
|
@ -79,17 +79,23 @@ class ScolarFormSemestreValidation(db.Model):
|
|||||||
def html(self, detail=False) -> str:
|
def html(self, detail=False) -> str:
|
||||||
"Affichage html"
|
"Affichage html"
|
||||||
if self.ue_id is not None:
|
if self.ue_id is not None:
|
||||||
return f"""Validation de l'UE {self.ue.acronyme} de {self.ue.formation.acronyme}
|
return f"""Validation de l'UE <b>{self.ue.acronyme}</b>
|
||||||
|
{('parcours <span class="parcours">'
|
||||||
|
+ ", ".join([p.code for p in self.ue.parcours]))
|
||||||
|
+ "</span>"
|
||||||
|
if self.ue.parcours else ""}
|
||||||
|
de {self.ue.formation.acronyme}
|
||||||
{("émise par " + self.formsemestre.html_link_status())
|
{("émise par " + self.formsemestre.html_link_status())
|
||||||
if self.formsemestre else ""}
|
if self.formsemestre else ""}
|
||||||
:<b>{self.code}</b>
|
: <b>{self.code}</b>
|
||||||
le {self.event_date.strftime("%d/%m/%Y")} à {self.event_date.strftime("%Hh%M")}
|
le {self.event_date.strftime("%d/%m/%Y")} à {self.event_date.strftime("%Hh%M")}
|
||||||
"""
|
"""
|
||||||
else:
|
else:
|
||||||
return f"""Validation du semestre S{
|
return f"""Validation du semestre S{
|
||||||
self.formsemestre.semestre_id if self.formsemestre else "?"}
|
self.formsemestre.semestre_id if self.formsemestre else "?"}
|
||||||
(<b>{self.code}</b>
|
{self.formsemestre.html_link_status() if self.formsemestre else ""}
|
||||||
le {self.event_date.strftime("%d/%m/%Y")} à {self.event_date.strftime("%Hh%M")})
|
: <b>{self.code}</b>
|
||||||
|
le {self.event_date.strftime("%d/%m/%Y")} à {self.event_date.strftime("%Hh%M")}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ def html_edit_formation_apc(
|
|||||||
if None in ects:
|
if None in ects:
|
||||||
ects_by_sem[semestre_idx] = '<span class="missing_ue_ects">manquant</span>'
|
ects_by_sem[semestre_idx] = '<span class="missing_ue_ects">manquant</span>'
|
||||||
else:
|
else:
|
||||||
ects_by_sem[semestre_idx] = sum(ects)
|
ects_by_sem[semestre_idx] = f"{sum(ects):g}"
|
||||||
|
|
||||||
arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags()
|
arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags()
|
||||||
|
|
||||||
|
@ -6,4 +6,8 @@ div.jury_decisions_list div {
|
|||||||
|
|
||||||
div.jury_decisions_list form {
|
div.jury_decisions_list form {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span.parcours {
|
||||||
|
color:blueviolet;
|
||||||
|
}
|
||||||
|
@ -551,6 +551,7 @@ class RowRecap(tb.Row):
|
|||||||
"etud_codes": "Codes",
|
"etud_codes": "Codes",
|
||||||
"identite_detail": "",
|
"identite_detail": "",
|
||||||
"identite_court": "",
|
"identite_court": "",
|
||||||
|
"rang": "",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
# --- Codes (seront cachés, mais exportés en excel)
|
# --- Codes (seront cachés, mais exportés en excel)
|
||||||
|
@ -26,9 +26,13 @@
|
|||||||
En conséquence, saisir ensuite <b>manuellement les décisions manquantes</b>,
|
En conséquence, saisir ensuite <b>manuellement les décisions manquantes</b>,
|
||||||
notamment sur les UEs en dessous de 10.
|
notamment sur les UEs en dessous de 10.
|
||||||
</p>
|
</p>
|
||||||
<p class="warning">
|
<div class="warning">
|
||||||
Il est nécessaire de relire soigneusement les décisions à l'issue de cette procédure !
|
<ul>
|
||||||
</p>
|
<li>Ne jamais lancer ce calcul avant que toutes les notes ne soient saisies !
|
||||||
|
(verrouiller le semestre ensuite)
|
||||||
|
</li>
|
||||||
|
<li>Il est nécessaire de relire soigneusement les décisions à l'issue de cette procédure !</li>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% for semestre_idx in semestre_ids %}
|
{% for semestre_idx in semestre_ids %}
|
||||||
<div class="formation_list_ues">
|
<div class="formation_list_ues">
|
||||||
<div class="formation_list_ues_titre">Unités d'Enseignement
|
<div class="formation_list_ues_titre">Unités d'Enseignement
|
||||||
semestre {{semestre_idx}} - {{"%g"|format(ects_by_sem[semestre_idx]) | safe}} ECTS
|
semestre {{semestre_idx}} - {{ects_by_sem[semestre_idx] | safe}} ECTS
|
||||||
</div>
|
</div>
|
||||||
<div class="formation_list_ues_content">
|
<div class="formation_list_ues_content">
|
||||||
<ul class="apc_ue_list">
|
<ul class="apc_ue_list">
|
||||||
|
@ -2497,7 +2497,7 @@ def formsemestre_validation_but(
|
|||||||
scodoc_dept=g.scodoc_dept, formsemestre_id=deca.formsemestre_id,
|
scodoc_dept=g.scodoc_dept, formsemestre_id=deca.formsemestre_id,
|
||||||
etudid=deca.etud.id)}" class="stdlink"
|
etudid=deca.etud.id)}" class="stdlink"
|
||||||
title="efface décisions issues des jurys de cette année"
|
title="efface décisions issues des jurys de cette année"
|
||||||
>effacer décisions</a>
|
>effacer décisions de ce jury</a>
|
||||||
|
|
||||||
<a style="margin-left: 16px;" class="stdlink"
|
<a style="margin-left: 16px;" class="stdlink"
|
||||||
href="{
|
href="{
|
||||||
@ -2898,7 +2898,12 @@ def formsemestre_jury_but_erase(formsemestre_id: int, etudid: int = None):
|
|||||||
)
|
)
|
||||||
+ """
|
+ """
|
||||||
<p>Les décisions des années scolaires précédentes ne seront pas modifiées.</p>
|
<p>Les décisions des années scolaires précédentes ne seront pas modifiées.</p>
|
||||||
<div class="warning">Cette opération est irréversible !</div>
|
<p>Efface aussi toutes les validations concernant l'année BUT de ce semestre,
|
||||||
|
même si elles ont été acquises ailleurs.
|
||||||
|
</p>
|
||||||
|
<div class="warning">Cette opération est irréversible !
|
||||||
|
A n'utiliser que dans des cas exceptionnels, vérifiez bien tous les étudiants ensuite.
|
||||||
|
</div>
|
||||||
""",
|
""",
|
||||||
cancel_url=dest_url,
|
cancel_url=dest_url,
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
SCOVERSION = "9.4.88"
|
SCOVERSION = "9.4.89"
|
||||||
|
|
||||||
SCONAME = "ScoDoc"
|
SCONAME = "ScoDoc"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user