Compare commits
10 Commits
b30ea5f5fd
...
267dbb6460
Author | SHA1 | Date | |
---|---|---|---|
267dbb6460 | |||
02a73de04d | |||
e78a2d3ffe | |||
a200be586a | |||
607604f91e | |||
8eedac0f03 | |||
aea2204d9e | |||
9c15cbe647 | |||
6761f5a620 | |||
69a53adb55 |
@ -337,7 +337,7 @@ def assiduites_group(with_query: bool = False):
|
||||
try:
|
||||
etuds = [int(etu) for etu in etuds]
|
||||
except ValueError:
|
||||
return json_error(404, "Le champs etudids n'est pas correctement formé")
|
||||
return json_error(404, "Le champ etudids n'est pas correctement formé")
|
||||
|
||||
# Vérification que tous les étudiants sont du même département
|
||||
query = Identite.query.filter(Identite.id.in_(etuds))
|
||||
|
@ -447,8 +447,24 @@ def formsemestre_warning_apc_setup(
|
||||
}">formation n'est pas associée à un référentiel de compétence.</a>
|
||||
</div>
|
||||
"""
|
||||
# Vérifie les niveaux de chaque parcours
|
||||
H = []
|
||||
# Le semestre n'a pas de parcours, mais les UE ont des parcours ?
|
||||
if not formsemestre.parcours:
|
||||
nb_ues_sans_parcours = len(
|
||||
formsemestre.formation.query_ues_parcour(None)
|
||||
.filter(UniteEns.semestre_idx == formsemestre.semestre_id)
|
||||
.all()
|
||||
)
|
||||
nb_ues_tot = (
|
||||
UniteEns.query.filter_by(formation=formsemestre.formation, type=UE_STANDARD)
|
||||
.filter(UniteEns.semestre_idx == formsemestre.semestre_id)
|
||||
.count()
|
||||
)
|
||||
if nb_ues_sans_parcours != nb_ues_tot:
|
||||
H.append(
|
||||
f"""Le semestre n'est associé à aucun parcours, mais les UEs de la formation ont des parcours"""
|
||||
)
|
||||
# Vérifie les niveaux de chaque parcours
|
||||
for parcour in formsemestre.parcours or [None]:
|
||||
annee = (formsemestre.semestre_id + 1) // 2
|
||||
niveaux_ids = {
|
||||
|
@ -256,7 +256,7 @@ def _gen_but_niveau_ue(
|
||||
return f"""<div class="but_niveau_ue {ue_class}
|
||||
{'annee_prec' if annee_prec else ''}
|
||||
">
|
||||
<div title="{ue.titre}">{ue.acronyme}</div>
|
||||
<div title="{ue.titre or ''}">{ue.acronyme}</div>
|
||||
<div class="but_note with_scoplement">
|
||||
<div>{moy_ue_str}</div>
|
||||
{scoplement}
|
||||
|
@ -17,7 +17,7 @@ def UEParcoursECTSForm(ue: UniteEns) -> FlaskForm:
|
||||
pass
|
||||
|
||||
parcours: list[ApcParcours] = ue.formation.referentiel_competence.parcours
|
||||
# Initialise un champs de saisie par parcours
|
||||
# Initialise un champ de saisie par parcours
|
||||
for parcour in parcours:
|
||||
ects = ue.get_ects(parcour, only_parcours=True)
|
||||
setattr(
|
||||
|
@ -82,7 +82,7 @@ class ConfigCASForm(FlaskForm):
|
||||
|
||||
cas_attribute_id = StringField(
|
||||
label="Attribut CAS utilisé comme id (laissez vide pour prendre l'id par défaut)",
|
||||
description="""Le champs CAS qui sera considéré comme l'id unique des
|
||||
description="""Le champ CAS qui sera considéré comme l'id unique des
|
||||
comptes utilisateurs.""",
|
||||
)
|
||||
|
||||
|
@ -297,7 +297,7 @@ class ScoDocSiteConfig(db.Model):
|
||||
|
||||
@classmethod
|
||||
def _get_int_field(cls, name: str, default=None) -> int:
|
||||
"""Valeur d'un champs integer"""
|
||||
"""Valeur d'un champ integer"""
|
||||
cfg = ScoDocSiteConfig.query.filter_by(name=name).first()
|
||||
if (cfg is None) or cfg.value is None:
|
||||
return default
|
||||
@ -311,7 +311,7 @@ class ScoDocSiteConfig(db.Model):
|
||||
default=None,
|
||||
range_values: tuple = (),
|
||||
) -> bool:
|
||||
"""Set champs integer. True si changement."""
|
||||
"""Set champ integer. True si changement."""
|
||||
if value != cls._get_int_field(name, default=default):
|
||||
if not isinstance(value, int) or (
|
||||
range_values and (value < range_values[0]) or (value > range_values[1])
|
||||
|
@ -6,18 +6,38 @@
|
||||
"""Affichages, debug
|
||||
"""
|
||||
|
||||
from flask import g
|
||||
from app import log
|
||||
|
||||
PE_DEBUG = 0
|
||||
PE_DEBUG = True
|
||||
|
||||
if not PE_DEBUG:
|
||||
# log to notes.log
|
||||
def pe_print(*a, **kw):
|
||||
# kw is ignored. log always add a newline
|
||||
log(" ".join(a))
|
||||
|
||||
else:
|
||||
pe_print = print # print function
|
||||
# On stocke les logs PE dans g.scodoc_pe_log
|
||||
# pour ne pas modifier les nombreux appels à pe_print.
|
||||
def pe_start_log() -> list[str]:
|
||||
"Initialize log"
|
||||
g.scodoc_pe_log = []
|
||||
return g.scodoc_pe_log
|
||||
|
||||
|
||||
def pe_print(*a):
|
||||
"Log (or print in PE_DEBUG mode) and store in g"
|
||||
if PE_DEBUG:
|
||||
msg = " ".join(a)
|
||||
print(msg)
|
||||
else:
|
||||
lines = getattr(g, "scodoc_pe_log")
|
||||
if lines is None:
|
||||
lines = pe_start_log()
|
||||
msg = " ".join(a)
|
||||
lines.append(msg)
|
||||
log(msg)
|
||||
|
||||
|
||||
def pe_get_log() -> str:
|
||||
"Renvoie une chaîne avec tous les messages loggués"
|
||||
return "\n".join(getattr(g, "scodoc_pe_log", []))
|
||||
|
||||
|
||||
# Affichage dans le tableur pe en cas d'absence de notes
|
||||
SANS_NOTE = "-"
|
||||
|
@ -284,3 +284,13 @@ def get_cosemestres_diplomants(annee_diplome: int) -> dict[int, FormSemestre]:
|
||||
cosemestres[fid] = cosem
|
||||
|
||||
return cosemestres
|
||||
|
||||
|
||||
def tri_semestres_par_rang(cosemestres: dict[int, FormSemestre]):
|
||||
"""Partant d'un dictionnaire de cosemestres, les tri par rang (semestre_id) dans un
|
||||
dictionnaire {rang: [liste des semestres du dit rang]}"""
|
||||
cosemestres_tries = {}
|
||||
for sem in cosemestres.values():
|
||||
cosemestres_tries[sem.semestre_id] = cosemestres_tries.get(sem.semestre_id, []) + [sem]
|
||||
return cosemestres_tries
|
||||
|
||||
|
@ -37,6 +37,7 @@ Created on 17/01/2024
|
||||
"""
|
||||
import pandas as pd
|
||||
|
||||
from app import ScoValueError
|
||||
from app.models import FormSemestre, Identite, Formation
|
||||
from app.pe import pe_comp, pe_affichage
|
||||
from app.scodoc import codes_cursus
|
||||
@ -141,7 +142,7 @@ class EtudiantsJuryPE:
|
||||
assert nbre_abandons == len(self.abandons_ids)
|
||||
|
||||
pe_affichage.pe_print(
|
||||
f" => {nbre_abandons} étudiants non considérés (redoublement, réorientation, abandon"
|
||||
f" => {nbre_abandons} étudiants traités mais non diplômés (redoublement, réorientation, abandon)"
|
||||
)
|
||||
# pe_affichage.pe_print(
|
||||
# " => quelques étudiants futurs diplômés : "
|
||||
@ -187,7 +188,11 @@ class EtudiantsJuryPE:
|
||||
etudiants = {etudid: self.identites[etudid] for etudid in etudids}
|
||||
return etudiants
|
||||
|
||||
def analyse_etat_etudiant(self, etudid: int, cosemestres: dict[int, FormSemestre]):
|
||||
def analyse_etat_etudiant(
|
||||
self,
|
||||
etudid: int,
|
||||
cosemestres: dict[int, FormSemestre]
|
||||
):
|
||||
"""Analyse le cursus d'un étudiant pouvant être :
|
||||
|
||||
* l'un de ceux sur lesquels le jury va statuer (année de diplômation du jury considéré)
|
||||
@ -198,8 +203,11 @@ class EtudiantsJuryPE:
|
||||
|
||||
* à insérer une entrée dans ``self.cursus`` pour mémoriser son identité,
|
||||
avec son nom, prénom, etc...
|
||||
* à analyser son parcours, pour déterminer s'il n'a (ou non) abandonné l'IUT en cours de
|
||||
route (cf. clé abandon)
|
||||
* à analyser son parcours, pour déterminer s'il a démissionné, redoublé (autre année de diplôme)
|
||||
ou a abandonné l'IUT en cours de route (cf. clé abandon). Un étudiant est considéré
|
||||
en abandon si connaissant son dernier semestre (par ex. un S3) il n'est pas systématiquement
|
||||
inscrit à l'un des S4, S5 ou S6 existants dans les cosemestres.
|
||||
|
||||
|
||||
Args:
|
||||
etudid: L'etudid d'un étudiant, à ajouter à ceux traiter par le jury
|
||||
@ -232,15 +240,19 @@ class EtudiantsJuryPE:
|
||||
"abandon": False, # va être traité en dessous
|
||||
}
|
||||
|
||||
# Est-il démissionnaire : charge son dernier semestre pour connaitre son état ?
|
||||
dernier_semes_etudiant = formsemestres[0]
|
||||
res = load_formsemestre_results(dernier_semes_etudiant)
|
||||
etud_etat = res.get_etud_etat(etudid)
|
||||
if etud_etat == scu.DEMISSION:
|
||||
self.cursus[etudid]["abandon"] |= True
|
||||
else:
|
||||
# Est-il réorienté ou a-t-il arrêté volontairement sa formation ?
|
||||
self.cursus[etudid]["abandon"] |= arret_de_formation(identite, cosemestres)
|
||||
# Si l'étudiant est succeptible d'être diplomé
|
||||
if self.cursus[etudid]["diplome"] == self.annee_diplome:
|
||||
# Est-il démissionnaire : charge son dernier semestre pour connaitre son état ?
|
||||
dernier_semes_etudiant = formsemestres[0]
|
||||
res = load_formsemestre_results(dernier_semes_etudiant)
|
||||
etud_etat = res.get_etud_etat(etudid)
|
||||
if etud_etat == scu.DEMISSION:
|
||||
self.cursus[etudid]["abandon"] = True
|
||||
else:
|
||||
# Est-il réorienté ou a-t-il arrêté (volontairement) sa formation ?
|
||||
self.cursus[etudid]["abandon"] = arret_de_formation(
|
||||
identite, cosemestres
|
||||
)
|
||||
|
||||
def get_semestres_significatifs(self, etudid: int):
|
||||
"""Ensemble des semestres d'un étudiant, qui l'auraient amené à être diplomé
|
||||
@ -446,8 +458,8 @@ def get_semestres_apc(identite: Identite) -> list:
|
||||
return semestres_apc
|
||||
|
||||
|
||||
def arret_de_formation(etud: Identite, cosemestres: list[FormSemestre]) -> bool:
|
||||
"""Détermine si un étudiant a arrêté sa formation. Il peut s'agir :
|
||||
def arret_de_formation(etud: Identite, cosemestres: dict[int, FormSemestre]) -> bool:
|
||||
"""Détermine si un étudiant a arrêté sa formation (volontairement ou non). Il peut s'agir :
|
||||
|
||||
* d'une réorientation à l'initiative du jury de semestre ou d'une démission
|
||||
(on pourrait utiliser les code NAR pour réorienté & DEM pour démissionnaire
|
||||
@ -458,7 +470,8 @@ def arret_de_formation(etud: Identite, cosemestres: list[FormSemestre]) -> bool:
|
||||
|
||||
Dans les cas, on considérera que l'étudiant a arrêté sa formation s'il n'est pas
|
||||
dans l'un des "derniers" cosemestres (semestres conduisant à la même année de diplômation)
|
||||
connu dans Scodoc.
|
||||
connu dans Scodoc. Par "derniers" cosemestres, est fait le choix d'analyser tous les cosemestres
|
||||
de rang/semestre_id supérieur (et donc de dates) au dernier semestre dans lequel il a été inscrit.
|
||||
|
||||
Par ex: au moment du jury PE en fin de S5 (pas de S6 renseigné dans Scodoc),
|
||||
l'étudiant doit appartenir à une instance des S5 qui conduisent à la diplomation dans
|
||||
@ -485,50 +498,95 @@ def arret_de_formation(etud: Identite, cosemestres: list[FormSemestre]) -> bool:
|
||||
Est-il réorienté, démissionnaire ou a-t-il arrêté de son propre chef sa formation ?
|
||||
|
||||
TODO:: A reprendre pour le cas des étudiants à l'étranger
|
||||
TODO:: A reprendre si BUT avec semestres décalés
|
||||
"""
|
||||
|
||||
# Les semestres APC de l'étudiant
|
||||
semestres = get_semestres_apc(etud)
|
||||
semestres_apc = {sem.semestre_id: sem for sem in semestres}
|
||||
if not semestres_apc:
|
||||
return True
|
||||
|
||||
# Son dernier semestre APC en date
|
||||
dernier_formsemestre = get_dernier_semestre_en_date(semestres_apc)
|
||||
numero_dernier_formsemestre = dernier_formsemestre.semestre_id
|
||||
# Le dernier semestre de l'étudiant
|
||||
dernier_formsemestre = semestres[0]
|
||||
rang_dernier_semestre = dernier_formsemestre.semestre_id
|
||||
|
||||
# Les numéro de semestres possible dans lesquels il pourrait s'incrire
|
||||
# semestre impair => passage de droit en semestre pair suivant (effet de l'annualisation)
|
||||
if numero_dernier_formsemestre % 2 == 1:
|
||||
numeros_possibles = list(
|
||||
range(numero_dernier_formsemestre + 1, pe_comp.NBRE_SEMESTRES_DIPLOMANT)
|
||||
)
|
||||
# semestre pair => passage en année supérieure ou redoublement
|
||||
else: #
|
||||
numeros_possibles = list(
|
||||
range(
|
||||
max(numero_dernier_formsemestre - 1, 1),
|
||||
pe_comp.NBRE_SEMESTRES_DIPLOMANT,
|
||||
)
|
||||
# Les cosemestres de rang supérieur ou égal à celui de formsemestre, triés par rang,
|
||||
# sous la forme ``{semestre_id: [liste des comestres associé à ce semestre_id]}``
|
||||
cosemestres_tries_par_rang = pe_comp.tri_semestres_par_rang(cosemestres)
|
||||
|
||||
cosemestres_superieurs = {}
|
||||
for rang in cosemestres_tries_par_rang:
|
||||
if rang > rang_dernier_semestre:
|
||||
cosemestres_superieurs[rang] = cosemestres_tries_par_rang[rang]
|
||||
|
||||
# Si pas d'autres cosemestres postérieurs
|
||||
if not cosemestres_superieurs:
|
||||
return False
|
||||
|
||||
# Pour chaque rang de (co)semestres, y-a-il un dans lequel il est inscrit ?
|
||||
etat_inscriptions = {rang: False for rang in cosemestres_superieurs}
|
||||
for rang in etat_inscriptions:
|
||||
for sem in cosemestres_superieurs[rang]:
|
||||
etudiants_du_sem = {ins.etudid for ins in sem.inscriptions}
|
||||
if etud.etudid in etudiants_du_sem:
|
||||
etat_inscriptions[rang] = True
|
||||
|
||||
# Vérifie qu'il n'y a pas de "trous" dans les rangs des cosemestres
|
||||
rangs = sorted(etat_inscriptions.keys())
|
||||
if list(rangs) != list(range(min(rangs), max(rangs) + 1)):
|
||||
difference = set(range(min(rangs), max(rangs) + 1)) - set(rangs)
|
||||
affichage = ",".join([f"S{val}" for val in difference])
|
||||
raise ScoValueError(
|
||||
f"Il manque le(s) semestre(s) {affichage} au cursus de {etud.etat_civil} ({etud.etudid})."
|
||||
)
|
||||
|
||||
# Y-a-t-il des cosemestres dans lesquels il aurait pu s'incrire ?
|
||||
formsestres_superieurs_possibles = []
|
||||
for fid, sem in cosemestres.items(): # Les semestres ayant des inscrits
|
||||
if (
|
||||
fid != dernier_formsemestre.formsemestre_id
|
||||
and sem.semestre_id in numeros_possibles
|
||||
and sem.date_debut.year >= dernier_formsemestre.date_debut.year
|
||||
):
|
||||
# date de debut des semestres possibles postérieur au dernier semestre de l'étudiant
|
||||
# et de niveau plus élevé que le dernier semestre valide de l'étudiant
|
||||
formsestres_superieurs_possibles.append(fid)
|
||||
# Est-il inscrit à tous les semestres de rang supérieur ? Si non, est démissionnaire
|
||||
est_demissionnaire = sum(etat_inscriptions.values()) != len(rangs)
|
||||
if est_demissionnaire:
|
||||
non_inscrit_a = [
|
||||
rang for rang in etat_inscriptions if not etat_inscriptions[rang]
|
||||
]
|
||||
affichage = ",".join([f"S{val}" for val in non_inscrit_a])
|
||||
pe_affichage.pe_print(
|
||||
f"{etud.etat_civil} ({etud.etudid}) considéré en abandon car non inscrit dans un (ou des) semestre(s) {affichage} amenant à diplômation"
|
||||
)
|
||||
|
||||
if len(formsestres_superieurs_possibles) > 0:
|
||||
return True
|
||||
return est_demissionnaire
|
||||
|
||||
return False
|
||||
# # Son dernier semestre APC en date
|
||||
# dernier_formsemestre = get_dernier_semestre_en_date(semestres_apc)
|
||||
# numero_dernier_formsemestre = dernier_formsemestre.semestre_id
|
||||
#
|
||||
# # Les numéro de semestres possible dans lesquels il pourrait s'incrire
|
||||
# # semestre impair => passage de droit en semestre pair suivant (effet de l'annualisation)
|
||||
# if numero_dernier_formsemestre % 2 == 1:
|
||||
# numeros_possibles = list(
|
||||
# range(numero_dernier_formsemestre + 1, pe_comp.NBRE_SEMESTRES_DIPLOMANT)
|
||||
# )
|
||||
# # semestre pair => passage en année supérieure ou redoublement
|
||||
# else: #
|
||||
# numeros_possibles = list(
|
||||
# range(
|
||||
# max(numero_dernier_formsemestre - 1, 1),
|
||||
# pe_comp.NBRE_SEMESTRES_DIPLOMANT,
|
||||
# )
|
||||
# )
|
||||
#
|
||||
# # Y-a-t-il des cosemestres dans lesquels il aurait pu s'incrire ?
|
||||
# formsestres_superieurs_possibles = []
|
||||
# for fid, sem in cosemestres.items(): # Les semestres ayant des inscrits
|
||||
# if (
|
||||
# fid != dernier_formsemestre.formsemestre_id
|
||||
# and sem.semestre_id in numeros_possibles
|
||||
# and sem.date_debut.year >= dernier_formsemestre.date_debut.year
|
||||
# ):
|
||||
# # date de debut des semestres possibles postérieur au dernier semestre de l'étudiant
|
||||
# # et de niveau plus élevé que le dernier semestre valide de l'étudiant
|
||||
# formsestres_superieurs_possibles.append(fid)
|
||||
#
|
||||
# if len(formsestres_superieurs_possibles) > 0:
|
||||
# return True
|
||||
#
|
||||
# return False
|
||||
|
||||
|
||||
def get_dernier_semestre_en_date(semestres: dict[int, FormSemestre]) -> FormSemestre:
|
||||
|
@ -44,6 +44,7 @@ Created on Fri Sep 9 09:15:05 2016
|
||||
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
from zipfile import ZipFile
|
||||
|
||||
import numpy as np
|
||||
@ -69,14 +70,18 @@ class JuryPE(object):
|
||||
diplome : l'année d'obtention du diplome BUT et du jury de PE (généralement février XXXX)
|
||||
"""
|
||||
|
||||
def __init__(self, diplome):
|
||||
def __init__(self, diplome: int):
|
||||
pe_affichage.pe_start_log()
|
||||
self.diplome = diplome
|
||||
"L'année du diplome"
|
||||
|
||||
self.nom_export_zip = f"Jury_PE_{self.diplome}"
|
||||
"Nom du zip où ranger les fichiers générés"
|
||||
|
||||
# Chargement des étudiants à prendre en compte Sydans le jury
|
||||
pe_affichage.pe_print(
|
||||
f"Données de poursuite d'étude générées le {time.strftime('%d/%m/%Y à %H:%M')}\n"
|
||||
)
|
||||
# Chargement des étudiants à prendre en compte dans le jury
|
||||
pe_affichage.pe_print(
|
||||
f"""*** Recherche et chargement des étudiants diplômés en {
|
||||
self.diplome}"""
|
||||
@ -96,6 +101,8 @@ class JuryPE(object):
|
||||
self._gen_xls_interclassements_rcss(zipfile)
|
||||
self._gen_xls_synthese_jury_par_tag(zipfile)
|
||||
self._gen_xls_synthese_par_etudiant(zipfile)
|
||||
# et le log
|
||||
self._add_log_to_zip(zipfile)
|
||||
|
||||
# Fin !!!! Tada :)
|
||||
|
||||
@ -251,6 +258,11 @@ class JuryPE(object):
|
||||
zipfile, f"synthese_jury_{self.diplome}_par_etudiant.xlsx", output.read()
|
||||
)
|
||||
|
||||
def _add_log_to_zip(self, zipfile):
|
||||
"""Add a text file with the log messages"""
|
||||
log_data = pe_affichage.pe_get_log()
|
||||
self.add_file_to_zip(zipfile, "pe_log.txt", log_data)
|
||||
|
||||
def add_file_to_zip(self, zipfile: ZipFile, filename: str, data, path=""):
|
||||
"""Add a file to given zip
|
||||
All files under NOM_EXPORT_ZIP/
|
||||
@ -397,7 +409,7 @@ class JuryPE(object):
|
||||
|
||||
# Les moys
|
||||
champ = (descr, NOM_STAT_GROUPE, "moy")
|
||||
moys = moy_traj.get_max()
|
||||
moys = moy_traj.get_moy()
|
||||
donnees.loc[etudids_communs, champ] = moys.loc[etudids_communs]
|
||||
|
||||
# Ajoute les données d'interclassement
|
||||
@ -425,7 +437,7 @@ class JuryPE(object):
|
||||
|
||||
# Les moys
|
||||
champ = (descr, nom_stat_promo, "moy")
|
||||
moys = moy_interclass.get_max()
|
||||
moys = moy_interclass.get_moy()
|
||||
donnees.loc[etudids_communs, champ] = moys.loc[etudids_communs]
|
||||
|
||||
df_synthese = df_synthese.join(donnees)
|
||||
|
@ -76,6 +76,7 @@ class SemestreTag(TableTag):
|
||||
# Les étudiants
|
||||
self.etuds = self.nt.etuds
|
||||
self.etudiants = {etud.etudid: etud.etat_civil for etud in self.etuds}
|
||||
self.etudids = list(self.etudiants.keys())
|
||||
|
||||
# Les notes, les modules implémentés triés, les étudiants, les coeffs,
|
||||
# récupérés notamment de py:mod:`res_but`
|
||||
@ -94,12 +95,12 @@ class SemestreTag(TableTag):
|
||||
tags_personnalises = get_synthese_tags_personnalises_semestre(
|
||||
self.nt.formsemestre
|
||||
)
|
||||
noms_tags_perso = list(set(tags_personnalises.keys()))
|
||||
noms_tags_perso = sorted(list(set(tags_personnalises.keys())))
|
||||
|
||||
## Déduit des compétences
|
||||
dict_ues_competences = get_noms_competences_from_ues(self.nt.formsemestre)
|
||||
noms_tags_comp = list(set(dict_ues_competences.values()))
|
||||
noms_tags_auto = ["but"] + noms_tags_comp
|
||||
# dict_ues_competences = get_noms_competences_from_ues(self.nt.formsemestre)
|
||||
# noms_tags_comp = list(set(dict_ues_competences.values()))
|
||||
noms_tags_auto = ["but"] # + noms_tags_comp
|
||||
self.tags = noms_tags_perso + noms_tags_auto
|
||||
"""Tags du semestre taggué"""
|
||||
|
||||
@ -122,23 +123,31 @@ class SemestreTag(TableTag):
|
||||
"""
|
||||
raise ScoValueError(message)
|
||||
|
||||
ues_hors_sport = [ue for ue in self.ues if ue.type != UE_SPORT]
|
||||
|
||||
# Calcul des moyennes & les classements de chaque étudiant à chaque tag
|
||||
self.moyennes_tags = {}
|
||||
|
||||
for tag in tags_personnalises:
|
||||
# pe_affichage.pe_print(f" -> Traitement du tag {tag}")
|
||||
moy_gen_tag = self.compute_moyenne_tag(tag, tags_personnalises)
|
||||
self.moyennes_tags[tag] = MoyenneTag(tag, moy_gen_tag)
|
||||
moy_ues_tag = self.compute_moy_ues_tag(tag, tags_personnalises)
|
||||
moy_gen_tag = self.compute_moy_gen_tag(moy_ues_tag)
|
||||
|
||||
# Ajoute les moyennes générales de BUT pour le semestre considéré
|
||||
self.moyennes_tags[tag] = MoyenneTag(
|
||||
tag, ues_hors_sport, moy_ues_tag, moy_gen_tag
|
||||
)
|
||||
|
||||
# Ajoute les d'UE moyennes générales de BUT pour le semestre considéré
|
||||
# moy_gen_but = self.nt.etud_moy_gen
|
||||
# self.moyennes_tags["but"] = MoyenneTag("but", [], None, moy_gen_but, )
|
||||
|
||||
# Ajoute les moyennes par UEs (et donc par compétence) + la moyenne générale (but)
|
||||
df_ues = pd.DataFrame({ue.id: self.nt.etud_moy_ue[ue.id] for ue in ues_hors_sport},
|
||||
index = self.etudids)
|
||||
# moy_ues = self.nt.etud_moy_ue[ue_id]
|
||||
moy_gen_but = self.nt.etud_moy_gen
|
||||
self.moyennes_tags["but"] = MoyenneTag("but", moy_gen_but)
|
||||
self.moyennes_tags["but"] = MoyenneTag("but", ues_hors_sport, df_ues, moy_gen_but)
|
||||
|
||||
# Ajoute les moyennes par compétence
|
||||
for ue_id, competence in dict_ues_competences.items():
|
||||
if competence not in self.moyennes_tags:
|
||||
moy_ue = self.nt.etud_moy_ue[ue_id]
|
||||
self.moyennes_tags[competence] = MoyenneTag(competence, moy_ue)
|
||||
|
||||
self.tags_sorted = self.get_all_tags()
|
||||
"""Tags (personnalisés+compétences) par ordre alphabétique"""
|
||||
@ -156,8 +165,8 @@ class SemestreTag(TableTag):
|
||||
"""Nom affiché pour le semestre taggué"""
|
||||
return app.pe.pe_etudiant.nom_semestre_etape(self.formsemestre, avec_fid=True)
|
||||
|
||||
def compute_moyenne_tag(self, tag: str, tags_infos: dict) -> pd.Series:
|
||||
"""Calcule la moyenne des étudiants pour le tag indiqué,
|
||||
def compute_moy_ues_tag(self, tag: str, tags_infos: dict) -> pd.Series:
|
||||
"""Calcule la moyenne par UE des étudiants pour le tag indiqué,
|
||||
pour ce SemestreTag, en ayant connaissance des informations sur
|
||||
les tags (dictionnaire donnant les coeff de repondération)
|
||||
|
||||
@ -199,7 +208,12 @@ class SemestreTag(TableTag):
|
||||
self.dispense_ues,
|
||||
block=self.formsemestre.block_moyennes,
|
||||
)
|
||||
return moyennes_ues_tag
|
||||
|
||||
def compute_moy_gen_tag(self, moy_ues_tag: pd.DataFrame) -> pd.Series:
|
||||
"""Calcule la moyenne générale (toutes UE confondus)
|
||||
pour le tag considéré, en les pondérant par les crédits ECTS.
|
||||
"""
|
||||
# Les ects
|
||||
ects = self.ues_inscr_parcours_df.fillna(0.0) * [
|
||||
ue.ects for ue in self.ues if ue.type != UE_SPORT
|
||||
@ -207,7 +221,7 @@ class SemestreTag(TableTag):
|
||||
|
||||
# Calcule la moyenne générale dans le semestre (pondérée par le ECTS)
|
||||
moy_gen_tag = comp.moy_sem.compute_sem_moys_apc_using_ects(
|
||||
moyennes_ues_tag,
|
||||
moy_ues_tag,
|
||||
ects,
|
||||
formation_id=self.formsemestre.formation_id,
|
||||
skip_empty_ues=True,
|
||||
|
@ -42,19 +42,27 @@ import numpy as np
|
||||
|
||||
from app import ScoValueError
|
||||
from app.comp.moy_sem import comp_ranks_series
|
||||
from app.models import UniteEns
|
||||
from app.pe import pe_affichage
|
||||
from app.pe.pe_affichage import SANS_NOTE
|
||||
from app.scodoc import sco_utils as scu
|
||||
import pandas as pd
|
||||
|
||||
from app.scodoc.codes_cursus import UE_SPORT
|
||||
|
||||
TAGS_RESERVES = ["but"]
|
||||
|
||||
|
||||
class MoyenneTag:
|
||||
def __init__(self, tag: str, notes: pd.Series):
|
||||
def __init__(
|
||||
self,
|
||||
tag: str,
|
||||
ues: list[UniteEns],
|
||||
notes_ues: pd.DataFrame,
|
||||
notes_gen: pd.Series,
|
||||
):
|
||||
"""Classe centralisant la synthèse des moyennes/classements d'une série
|
||||
d'étudiants à un tag donné, en stockant un dictionnaire :
|
||||
d'étudiants à un tag donné, en stockant :
|
||||
|
||||
``
|
||||
{
|
||||
@ -69,16 +77,26 @@ class MoyenneTag:
|
||||
|
||||
Args:
|
||||
tag: Un tag
|
||||
note: Une série de notes (moyenne) sous forme d'un pd.Series()
|
||||
ues: La liste des UEs ayant servie au calcul de la moyenne
|
||||
notes_ues: Les moyennes (etudid x ues) aux différentes UEs et pour le tag
|
||||
notes_gen: Une série de notes (moyenne) sous forme d'un pd.Series() (toutes UEs confondues)
|
||||
"""
|
||||
self.tag = tag
|
||||
"""Le tag associé à la moyenne"""
|
||||
self.etudids = list(notes.index) # calcul à venir
|
||||
self.etudids = list(notes_gen.index) # calcul à venir
|
||||
"""Les id des étudiants"""
|
||||
self.inscrits_ids = notes[notes.notnull()].index.to_list()
|
||||
"""Les id des étudiants dont la moyenne est non nulle"""
|
||||
self.df: pd.DataFrame = self.comp_moy_et_stat(notes)
|
||||
"""Le dataframe retraçant les moyennes/classements/statistiques"""
|
||||
self.ues: list[UniteEns] = ues
|
||||
"""Les UEs sur lesquelles sont calculées les moyennes"""
|
||||
self.df_ues: dict[int, pd.DataFrame] = {}
|
||||
"""Les dataframes retraçant les moyennes/classements/statistiques des étudiants aux UEs"""
|
||||
for ue in self.ues: # if ue.type != UE_SPORT:
|
||||
notes = notes_ues[ue.id]
|
||||
self.df_ues[ue.id] = self.comp_moy_et_stat(notes)
|
||||
|
||||
self.inscrits_ids = notes_gen[notes_gen.notnull()].index.to_list()
|
||||
"""Les id des étudiants dont la moyenne générale est non nulle"""
|
||||
self.df_gen: pd.DataFrame = self.comp_moy_et_stat(notes_gen)
|
||||
"""Le dataframe retraçant les moyennes/classements/statistiques général"""
|
||||
self.synthese = self.to_dict()
|
||||
"""La synthèse (dictionnaire) des notes/classements/statistiques"""
|
||||
|
||||
@ -88,7 +106,8 @@ class MoyenneTag:
|
||||
|
||||
def comp_moy_et_stat(self, notes: pd.Series) -> dict:
|
||||
"""Calcule et structure les données nécessaires au PE pour une série
|
||||
de notes (souvent une moyenne par tag) dans un dictionnaire spécifique.
|
||||
de notes (pouvant être une moyenne d'un tag à une UE ou une moyenne générale
|
||||
d'un tag) dans un dictionnaire spécifique.
|
||||
|
||||
Partant des notes, sont calculés les classements (en ne tenant compte
|
||||
que des notes non nulles).
|
||||
@ -121,64 +140,65 @@ class MoyenneTag:
|
||||
|
||||
# Les nb d'étudiants & nb d'inscrits
|
||||
df["nb_etuds"] = len(self.etudids)
|
||||
df.loc[self.inscrits_ids, "nb_inscrits"] = len(self.inscrits_ids)
|
||||
# Les étudiants dont la note n'est pas nulle
|
||||
inscrits_ids = notes[notes.notnull()].index.to_list()
|
||||
df.loc[inscrits_ids, "nb_inscrits"] = len(inscrits_ids)
|
||||
|
||||
# Le classement des inscrits
|
||||
notes_non_nulles = notes[self.inscrits_ids]
|
||||
notes_non_nulles = notes[inscrits_ids]
|
||||
(class_str, class_int) = comp_ranks_series(notes_non_nulles)
|
||||
df.loc[self.inscrits_ids, "classement"] = class_int
|
||||
df.loc[inscrits_ids, "classement"] = class_int
|
||||
|
||||
# Le rang (classement/nb_inscrit)
|
||||
df["rang"] = df["rang"].astype(str)
|
||||
df.loc[self.inscrits_ids, "rang"] = (
|
||||
df.loc[self.inscrits_ids, "classement"].astype(int).astype(str)
|
||||
df.loc[inscrits_ids, "rang"] = (
|
||||
df.loc[inscrits_ids, "classement"].astype(int).astype(str)
|
||||
+ "/"
|
||||
+ df.loc[self.inscrits_ids, "nb_inscrits"].astype(int).astype(str)
|
||||
+ df.loc[inscrits_ids, "nb_inscrits"].astype(int).astype(str)
|
||||
)
|
||||
|
||||
# Les stat (des inscrits)
|
||||
df.loc[self.inscrits_ids, "min"] = notes.min()
|
||||
df.loc[self.inscrits_ids, "max"] = notes.max()
|
||||
df.loc[self.inscrits_ids, "moy"] = notes.mean()
|
||||
df.loc[inscrits_ids, "min"] = notes.min()
|
||||
df.loc[inscrits_ids, "max"] = notes.max()
|
||||
df.loc[inscrits_ids, "moy"] = notes.mean()
|
||||
|
||||
return df
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Renvoie un dictionnaire de synthèse des moyennes/classements/statistiques"""
|
||||
synthese = {
|
||||
"notes": self.df["note"],
|
||||
"classements": self.df["classement"],
|
||||
"min": self.df["min"].mean(),
|
||||
"max": self.df["max"].mean(),
|
||||
"moy": self.df["moy"].mean(),
|
||||
"nb_inscrits": self.df["nb_inscrits"].mean(),
|
||||
"notes": self.df_gen["note"],
|
||||
"classements": self.df_gen["classement"],
|
||||
"min": self.df_gen["min"].mean(),
|
||||
"max": self.df_gen["max"].mean(),
|
||||
"moy": self.df_gen["moy"].mean(),
|
||||
"nb_inscrits": self.df_gen["nb_inscrits"].mean(),
|
||||
}
|
||||
return synthese
|
||||
|
||||
def get_notes(self):
|
||||
"""Série des notes, arrondies à 2 chiffres après la virgule"""
|
||||
return self.df["note"].round(2)
|
||||
return self.df_gen["note"].round(2)
|
||||
|
||||
def get_rangs_inscrits(self) -> pd.Series:
|
||||
"""Série des rangs classement/nbre_inscrit"""
|
||||
return self.df["rang"]
|
||||
return self.df_gen["rang"]
|
||||
|
||||
def get_min(self) -> pd.Series:
|
||||
"""Série des min"""
|
||||
return self.df["min"].round(2)
|
||||
return self.df_gen["min"].round(2)
|
||||
|
||||
def get_max(self) -> pd.Series:
|
||||
"""Série des max"""
|
||||
return self.df["max"].round(2)
|
||||
return self.df_gen["max"].round(2)
|
||||
|
||||
def get_moy(self) -> pd.Series:
|
||||
"""Série des moy"""
|
||||
return self.df["moy"].round(2)
|
||||
|
||||
return self.df_gen["moy"].round(2)
|
||||
|
||||
def get_note_for_df(self, etudid: int):
|
||||
"""Note d'un étudiant donné par son etudid"""
|
||||
return round(self.df["note"].loc[etudid], 2)
|
||||
return round(self.df_gen["note"].loc[etudid], 2)
|
||||
|
||||
def get_min_for_df(self) -> float:
|
||||
"""Min renseigné pour affichage dans un df"""
|
||||
@ -195,7 +215,7 @@ class MoyenneTag:
|
||||
def get_class_for_df(self, etudid: int) -> str:
|
||||
"""Classement ramené au nombre d'inscrits,
|
||||
pour un étudiant donné par son etudid"""
|
||||
classement = self.df["rang"].loc[etudid]
|
||||
classement = self.df_gen["rang"].loc[etudid]
|
||||
if not pd.isna(classement):
|
||||
return classement
|
||||
else:
|
||||
|
@ -72,14 +72,16 @@ def pe_view_sem_recap(formsemestre_id: int):
|
||||
|
||||
# Cosemestres diplomants
|
||||
cosemestres = pe_comp.get_cosemestres_diplomants(annee_diplome)
|
||||
|
||||
cosemestres_tries = pe_comp.tri_semestres_par_rang(cosemestres)
|
||||
affichage_cosemestres_tries = {rang: ", ".join([sem.titre_annee() for sem in cosemestres_tries[rang]]) for rang in cosemestres_tries}
|
||||
if request.method == "GET":
|
||||
return render_template(
|
||||
"pe/pe_view_sem_recap.j2",
|
||||
annee_diplome=annee_diplome,
|
||||
formsemestre=formsemestre,
|
||||
sco=ScoData(formsemestre=formsemestre),
|
||||
cosemestres=cosemestres,
|
||||
cosemestres=affichage_cosemestres_tries,
|
||||
rangs_tries=sorted(affichage_cosemestres_tries.keys())
|
||||
)
|
||||
|
||||
# request.method == "POST"
|
||||
@ -102,11 +104,3 @@ def pe_view_sem_recap(formsemestre_id: int):
|
||||
download_name=scu.sanitize_filename(jury.nom_export_zip + ".zip"),
|
||||
as_attachment=True,
|
||||
)
|
||||
|
||||
return render_template(
|
||||
"pe/pe_view_sem_recap.j2",
|
||||
annee_diplome=annee_diplome,
|
||||
formsemestre=formsemestre,
|
||||
sco=ScoData(formsemestre=formsemestre),
|
||||
cosemestres=cosemestres,
|
||||
)
|
||||
|
@ -396,7 +396,7 @@ class TF(object):
|
||||
self.values[field] = int(self.values[field])
|
||||
except ValueError:
|
||||
msg.append(
|
||||
f"valeur invalide ({self.values[field]}) pour le champs {field}"
|
||||
f"valeur invalide ({self.values[field]}) pour le champ {field}"
|
||||
)
|
||||
ok = False
|
||||
elif typ == "float" or typ == "real":
|
||||
@ -404,7 +404,7 @@ class TF(object):
|
||||
self.values[field] = float(self.values[field].replace(",", "."))
|
||||
except ValueError:
|
||||
msg.append(
|
||||
f"valeur invalide ({self.values[field]}) pour le champs {field}"
|
||||
f"valeur invalide ({self.values[field]}) pour le champ {field}"
|
||||
)
|
||||
ok = False
|
||||
if ok:
|
||||
|
@ -265,7 +265,7 @@ def DBUpdateArgs(cnx, table, vals, where=None, commit=False, convert_empty_to_nu
|
||||
# log('vals=%s\n'%vals)
|
||||
except psycopg2.errors.StringDataRightTruncation as exc:
|
||||
cnx.rollback()
|
||||
raise ScoValueError("champs de texte trop long !") from exc
|
||||
raise ScoValueError("champ de texte trop long !") from exc
|
||||
except:
|
||||
cnx.rollback() # get rid of this transaction
|
||||
log('Exception in DBUpdateArgs:\n\treq="%s"\n\tvals="%s"\n' % (req, vals))
|
||||
|
@ -166,9 +166,9 @@ def process_field(
|
||||
values={pprint.pformat(cdict)}
|
||||
"""
|
||||
)
|
||||
text = f"""<para><i>format invalide: champs</i> {missing_key} <i>inexistant !</i></para>"""
|
||||
text = f"""<para><i>format invalide: champ</i> {missing_key} <i>inexistant !</i></para>"""
|
||||
scu.flash_once(
|
||||
f"Attention: format PDF invalide (champs {field}, clef {missing_key})"
|
||||
f"Attention: format PDF invalide (champ {field}, clef {missing_key})"
|
||||
)
|
||||
raise
|
||||
except: # pylint: disable=bare-except
|
||||
|
@ -126,53 +126,59 @@ def html_edit_formation_apc(
|
||||
UniteEns.type != codes_cursus.UE_SPORT,
|
||||
).first()
|
||||
H += [
|
||||
render_template(
|
||||
"pn/form_mods.j2",
|
||||
formation=formation,
|
||||
titre=f"Ressources du S{semestre_idx}",
|
||||
create_element_msg="créer une nouvelle ressource",
|
||||
# matiere_parent=matiere_parent,
|
||||
modules=ressources_in_sem,
|
||||
module_type=ModuleType.RESSOURCE,
|
||||
editable=editable,
|
||||
tag_editable=tag_editable,
|
||||
icons=icons,
|
||||
scu=scu,
|
||||
semestre_id=semestre_idx,
|
||||
)
|
||||
if ues_by_sem[semestre_idx].count() > 0
|
||||
else "",
|
||||
render_template(
|
||||
"pn/form_mods.j2",
|
||||
formation=formation,
|
||||
titre=f"Situations d'Apprentissage et d'Évaluation (SAÉs) S{semestre_idx}",
|
||||
create_element_msg="créer une nouvelle SAÉ",
|
||||
# matiere_parent=matiere_parent,
|
||||
modules=saes_in_sem,
|
||||
module_type=ModuleType.SAE,
|
||||
editable=editable,
|
||||
tag_editable=tag_editable,
|
||||
icons=icons,
|
||||
scu=scu,
|
||||
semestre_id=semestre_idx,
|
||||
)
|
||||
if ues_by_sem[semestre_idx].count() > 0
|
||||
else "",
|
||||
render_template(
|
||||
"pn/form_mods.j2",
|
||||
formation=formation,
|
||||
titre=f"Autres modules (non BUT) du S{semestre_idx}",
|
||||
create_element_msg="créer un nouveau module",
|
||||
modules=other_modules_in_sem,
|
||||
module_type=ModuleType.STANDARD,
|
||||
editable=editable,
|
||||
tag_editable=tag_editable,
|
||||
icons=icons,
|
||||
scu=scu,
|
||||
semestre_id=semestre_idx,
|
||||
)
|
||||
if ues_by_sem[semestre_idx].count() > 0
|
||||
else """<span class="fontred">créer une UE pour pouvoir ajouter des modules</span>""",
|
||||
(
|
||||
render_template(
|
||||
"pn/form_mods.j2",
|
||||
formation=formation,
|
||||
titre=f"Ressources du S{semestre_idx}",
|
||||
create_element_msg="créer une nouvelle ressource",
|
||||
# matiere_parent=matiere_parent,
|
||||
modules=ressources_in_sem,
|
||||
module_type=ModuleType.RESSOURCE,
|
||||
editable=editable,
|
||||
tag_editable=tag_editable,
|
||||
icons=icons,
|
||||
scu=scu,
|
||||
semestre_id=semestre_idx,
|
||||
)
|
||||
if ues_by_sem[semestre_idx].count() > 0
|
||||
else ""
|
||||
),
|
||||
(
|
||||
render_template(
|
||||
"pn/form_mods.j2",
|
||||
formation=formation,
|
||||
titre=f"Situations d'Apprentissage et d'Évaluation (SAÉs) S{semestre_idx}",
|
||||
create_element_msg="créer une nouvelle SAÉ",
|
||||
# matiere_parent=matiere_parent,
|
||||
modules=saes_in_sem,
|
||||
module_type=ModuleType.SAE,
|
||||
editable=editable,
|
||||
tag_editable=tag_editable,
|
||||
icons=icons,
|
||||
scu=scu,
|
||||
semestre_id=semestre_idx,
|
||||
)
|
||||
if ues_by_sem[semestre_idx].count() > 0
|
||||
else ""
|
||||
),
|
||||
(
|
||||
render_template(
|
||||
"pn/form_mods.j2",
|
||||
formation=formation,
|
||||
titre=f"Autres modules (non BUT) du S{semestre_idx}",
|
||||
create_element_msg="créer un nouveau module",
|
||||
modules=other_modules_in_sem,
|
||||
module_type=ModuleType.STANDARD,
|
||||
editable=editable,
|
||||
tag_editable=tag_editable,
|
||||
icons=icons,
|
||||
scu=scu,
|
||||
semestre_id=semestre_idx,
|
||||
)
|
||||
if ues_by_sem[semestre_idx].count() > 0
|
||||
else """<span class="fontred">créer une UE pour pouvoir ajouter des modules</span>"""
|
||||
),
|
||||
]
|
||||
|
||||
return "\n".join(H)
|
||||
@ -202,7 +208,7 @@ def html_ue_infos(ue):
|
||||
)
|
||||
return render_template(
|
||||
"pn/ue_infos.j2",
|
||||
titre=f"UE {ue.acronyme} {ue.titre}",
|
||||
titre=f"UE {ue.acronyme} {ue.titre or ''}",
|
||||
ue=ue,
|
||||
formsemestres=formsemestres,
|
||||
nb_etuds_valid_ue=nb_etuds_valid_ue,
|
||||
|
@ -104,7 +104,7 @@ def matiere_create(ue_id=None):
|
||||
default_numero = max([mat.numero for mat in ue.matieres] or [9]) + 1
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title="Création d'une matière"),
|
||||
f"""<h2>Création d'une matière dans l'UE {ue.titre} ({ue.acronyme})</h2>
|
||||
f"""<h2>Création d'une matière dans l'UE {ue.titre or ''} ({ue.acronyme})</h2>
|
||||
<p class="help">Les matières sont des groupes de modules dans une UE
|
||||
d'une formation donnée. Les matières servent surtout pour la
|
||||
présentation (bulletins, etc) mais <em>n'ont pas de rôle dans le calcul
|
||||
|
@ -85,7 +85,7 @@ _moduleEditor = ndb.EditableTable(
|
||||
"heures_tp": ndb.float_null_is_zero,
|
||||
"numero": ndb.int_null_is_zero,
|
||||
"coefficient": ndb.float_null_is_zero,
|
||||
"module_type": ndb.int_null_is_zero
|
||||
"module_type": ndb.int_null_is_zero,
|
||||
#'ects' : ndb.float_null_is_null
|
||||
},
|
||||
)
|
||||
@ -387,14 +387,16 @@ def module_edit(
|
||||
"scodoc/help/modules.j2",
|
||||
is_apc=is_apc,
|
||||
semestre_id=semestre_id,
|
||||
formsemestres=FormSemestre.query.filter(
|
||||
ModuleImpl.formsemestre_id == FormSemestre.id,
|
||||
ModuleImpl.module_id == module_id,
|
||||
)
|
||||
.order_by(FormSemestre.date_debut)
|
||||
.all()
|
||||
if not create
|
||||
else None,
|
||||
formsemestres=(
|
||||
FormSemestre.query.filter(
|
||||
ModuleImpl.formsemestre_id == FormSemestre.id,
|
||||
ModuleImpl.module_id == module_id,
|
||||
)
|
||||
.order_by(FormSemestre.date_debut)
|
||||
.all()
|
||||
if not create
|
||||
else None
|
||||
),
|
||||
create=create,
|
||||
),
|
||||
]
|
||||
@ -413,9 +415,11 @@ def module_edit(
|
||||
}
|
||||
if module:
|
||||
module_types |= {
|
||||
scu.ModuleType(module.module_type)
|
||||
if module.module_type
|
||||
else scu.ModuleType.STANDARD
|
||||
(
|
||||
scu.ModuleType(module.module_type)
|
||||
if module.module_type
|
||||
else scu.ModuleType.STANDARD
|
||||
)
|
||||
}
|
||||
# Numéro du module
|
||||
# cherche le numero adéquat (pour placer le module en fin de liste)
|
||||
@ -571,15 +575,17 @@ def module_edit(
|
||||
"input_type": "menu",
|
||||
"title": "Rattachement :" if is_apc else "Matière :",
|
||||
"explanation": (
|
||||
"UE de rattachement, utilisée notamment pour les malus"
|
||||
+ (
|
||||
" (module utilisé, ne peut pas être changé de semestre)"
|
||||
if in_use
|
||||
else ""
|
||||
(
|
||||
"UE de rattachement, utilisée notamment pour les malus"
|
||||
+ (
|
||||
" (module utilisé, ne peut pas être changé de semestre)"
|
||||
if in_use
|
||||
else ""
|
||||
)
|
||||
)
|
||||
)
|
||||
if is_apc
|
||||
else "un module appartient à une seule matière.",
|
||||
if is_apc
|
||||
else "un module appartient à une seule matière."
|
||||
),
|
||||
"labels": mat_names,
|
||||
"allowed_values": ue_mat_ids,
|
||||
"enabled": unlocked,
|
||||
@ -733,7 +739,7 @@ def module_edit(
|
||||
"title": f"""<span class="fontred">{scu.EMO_WARNING }
|
||||
L'UE <a class="stdlink" href="{
|
||||
url_for("notes.ue_edit", scodoc_dept=g.scodoc_dept, ue_id=ue.id)
|
||||
}">{ue.acronyme} {ue.titre}</a>
|
||||
}">{ue.acronyme} {ue.titre or ''}</a>
|
||||
n'est pas associée à un niveau de compétences
|
||||
</span>""",
|
||||
},
|
||||
@ -766,12 +772,14 @@ def module_edit(
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
descr,
|
||||
html_foot_markup=f"""<div class="sco_tag_module_edit"><span
|
||||
html_foot_markup=(
|
||||
f"""<div class="sco_tag_module_edit"><span
|
||||
class="sco_tag_edit"><textarea data-module_id="{module_id}" class="module_tag_editor"
|
||||
>{','.join(sco_tag_module.module_tag_list(module_id))}</textarea></span></div>
|
||||
"""
|
||||
if not create
|
||||
else "",
|
||||
if not create
|
||||
else ""
|
||||
),
|
||||
initvalues=module_dict if module else {},
|
||||
submitlabel="Modifier ce module" if module else "Créer ce module",
|
||||
cancelbutton="Annuler",
|
||||
@ -814,7 +822,7 @@ def module_edit(
|
||||
tf[2]["matiere_id"] = matiere.id
|
||||
else:
|
||||
matiere_id = sco_edit_matiere.do_matiere_create(
|
||||
{"ue_id": ue.id, "titre": ue.titre, "numero": 1},
|
||||
{"ue_id": ue.id, "titre": ue.titre or "", "numero": 1},
|
||||
)
|
||||
tf[2]["matiere_id"] = matiere_id
|
||||
|
||||
|
@ -167,7 +167,7 @@ def do_ue_delete(ue: UniteEns, delete_validations=False, force=False):
|
||||
if not ue.can_be_deleted():
|
||||
raise ScoNonEmptyFormationObject(
|
||||
f"UE (id={ue.id}, dud)",
|
||||
msg=ue.titre,
|
||||
msg=f"{ue.titre or ''} ({ue.acronyme})",
|
||||
dest_url=url_for(
|
||||
"notes.ue_table",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
@ -639,8 +639,8 @@ def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False):
|
||||
)
|
||||
if not ue.can_be_deleted():
|
||||
raise ScoNonEmptyFormationObject(
|
||||
f"UE",
|
||||
msg=ue.titre,
|
||||
"UE",
|
||||
msg=f"{ue.titre or ''} ({ue.acronyme})",
|
||||
dest_url=url_for(
|
||||
"notes.ue_table",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
@ -651,7 +651,7 @@ def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False):
|
||||
|
||||
if not dialog_confirmed:
|
||||
return scu.confirm_dialog(
|
||||
f"<h2>Suppression de l'UE {ue.titre} ({ue.acronyme})</h2>",
|
||||
f"<h2>Suppression de l'UE {ue.titre or ''} ({ue.acronyme})</h2>",
|
||||
dest_url="",
|
||||
parameters={"ue_id": ue.id},
|
||||
cancel_url=url_for(
|
||||
@ -1452,7 +1452,7 @@ def ue_sharing_code(ue_code: str = "", ue_id: int = None, hide_ue_id: int = None
|
||||
H.append("<ul>")
|
||||
for ue in ues:
|
||||
H.append(
|
||||
f"""<li>{ue.acronyme} ({ue.titre}) dans
|
||||
f"""<li>{ue.acronyme} ({ue.titre or ''}) dans
|
||||
<a class="stdlink" href="{
|
||||
url_for("notes.ue_table",
|
||||
scodoc_dept=g.scodoc_dept, formation_id=ue.formation.id)}"
|
||||
|
@ -283,7 +283,7 @@ def evaluation_create_form(
|
||||
"coef. mod.:" +str(coef_ue) if coef_ue
|
||||
else "ce module n'a pas de coef. dans cette UE"
|
||||
})</span>
|
||||
<span class="eval_coef_ue_titre">{ue.titre}</span>
|
||||
<span class="eval_coef_ue_titre">{ue.titre or ''}</span>
|
||||
""",
|
||||
"allow_null": False,
|
||||
# ok si poids nul ou coef vers l'UE nul:
|
||||
|
@ -258,13 +258,17 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid):
|
||||
submitlabel="Enregistrer ces validations",
|
||||
cancelbutton="Annuler",
|
||||
initvalues=initvalues,
|
||||
cssclass="tf_ext_edit_ue_validations ext_apc"
|
||||
if formsemestre.formation.is_apc()
|
||||
else "tf_ext_edit_ue_validations",
|
||||
cssclass=(
|
||||
"tf_ext_edit_ue_validations ext_apc"
|
||||
if formsemestre.formation.is_apc()
|
||||
else "tf_ext_edit_ue_validations"
|
||||
),
|
||||
# En APC, stocke les coefficients pour l'affichage de la moyenne en direct
|
||||
form_attrs=f"""data-ue_coefs='[{', '.join(str(ue.ects or 0) for ue in ues)}]'"""
|
||||
if formsemestre.formation.is_apc()
|
||||
else "",
|
||||
form_attrs=(
|
||||
f"""data-ue_coefs='[{', '.join(str(ue.ects or 0) for ue in ues)}]'"""
|
||||
if formsemestre.formation.is_apc()
|
||||
else ""
|
||||
),
|
||||
)
|
||||
if tf[0] == -1:
|
||||
return "<h4>annulation</h4>"
|
||||
@ -421,12 +425,18 @@ def _ue_form_description(
|
||||
"input_type": "text",
|
||||
"size": 4,
|
||||
"template": itemtemplate,
|
||||
"title": "<tt>"
|
||||
+ (f"S{ue.semestre_idx} " if ue.semestre_idx is not None else "")
|
||||
+ f"<b>{ue.acronyme}</b></tt> {ue.titre}"
|
||||
+ f" ({ue.ects} ECTS)"
|
||||
if ue.ects is not None
|
||||
else "",
|
||||
"title": (
|
||||
"<tt>"
|
||||
+ (
|
||||
f"S{ue.semestre_idx} "
|
||||
if ue.semestre_idx is not None
|
||||
else ""
|
||||
)
|
||||
+ f"<b>{ue.acronyme}</b></tt> {ue.titre or ''}"
|
||||
+ f" ({ue.ects} ECTS)"
|
||||
if ue.ects is not None
|
||||
else ""
|
||||
),
|
||||
"attributes": [coef_disabled],
|
||||
},
|
||||
)
|
||||
|
@ -281,9 +281,11 @@ def formsemestre_status_menubar(formsemestre: FormSemestre) -> str:
|
||||
|
||||
menu_inscriptions = [
|
||||
{
|
||||
"title": "Gérer les inscriptions aux UE et modules"
|
||||
if formsemestre.formation.is_apc()
|
||||
else "Gérer les inscriptions aux modules",
|
||||
"title": (
|
||||
"Gérer les inscriptions aux UE et modules"
|
||||
if formsemestre.formation.is_apc()
|
||||
else "Gérer les inscriptions aux modules"
|
||||
),
|
||||
"endpoint": "notes.moduleimpl_inscriptions_stats",
|
||||
"args": {"formsemestre_id": formsemestre_id},
|
||||
}
|
||||
@ -619,9 +621,9 @@ def formsemestre_description_table(
|
||||
if ue.color:
|
||||
for k in list(ue_info.keys()):
|
||||
if not k.startswith("_"):
|
||||
ue_info[
|
||||
f"_{k}_td_attrs"
|
||||
] = f'style="background-color: {ue.color} !important;"'
|
||||
ue_info[f"_{k}_td_attrs"] = (
|
||||
f'style="background-color: {ue.color} !important;"'
|
||||
)
|
||||
if not is_apc:
|
||||
# n'affiche la ligne UE qu'en formation classique
|
||||
# car l'UE de rattachement n'a pas d'intérêt en BUT
|
||||
@ -1050,9 +1052,11 @@ def formsemestre_status(formsemestre_id=None, check_parcours=True):
|
||||
formsemestre_id=formsemestre_id, page_title="Tableau de bord"
|
||||
),
|
||||
formsemestre_warning_apc_setup(formsemestre, nt),
|
||||
formsemestre_warning_etuds_sans_note(formsemestre, nt)
|
||||
if can_change_all_notes
|
||||
else "",
|
||||
(
|
||||
formsemestre_warning_etuds_sans_note(formsemestre, nt)
|
||||
if can_change_all_notes
|
||||
else ""
|
||||
),
|
||||
"""<p style="font-size: 130%"><b>Tableau de bord : </b>""",
|
||||
]
|
||||
if formsemestre.est_courant():
|
||||
@ -1226,7 +1230,7 @@ def formsemestre_tableau_modules(
|
||||
ue = modimpl.module.ue
|
||||
if show_ues and (prev_ue_id != ue.id):
|
||||
prev_ue_id = ue.id
|
||||
titre = ue.titre
|
||||
titre = ue.titre or ""
|
||||
if use_ue_coefs:
|
||||
titre += f""" <b>(coef. {ue.coefficient or 0.0})</b>"""
|
||||
H.append(
|
||||
|
@ -728,7 +728,9 @@ def formsemestre_recap_parcours_table(
|
||||
)
|
||||
# Dispense BUT ?
|
||||
if (etudid, ue.id) in nt.dispense_ues:
|
||||
moy_ue_txt = "❎" if (ue_status and ue_status["is_capitalized"]) else "⭕"
|
||||
moy_ue_txt = (
|
||||
"❎" if (ue_status and ue_status["is_capitalized"]) else "⭕"
|
||||
)
|
||||
explanation_ue.append("non inscrit (dispense)")
|
||||
else:
|
||||
moy_ue_txt = scu.fmt_note(moy_ue)
|
||||
@ -1098,7 +1100,7 @@ def formsemestre_validate_previous_ue(formsemestre: FormSemestre, etud: Identite
|
||||
|
||||
ue_names = ["Choisir..."] + [
|
||||
f"""{('S'+str(ue.semestre_idx)+' : ') if ue.semestre_idx is not None else ''
|
||||
}{ue.acronyme} {ue.titre} ({ue.ue_code or ""})"""
|
||||
}{ue.acronyme} {ue.titre or ''} ({ue.ue_code or ""})"""
|
||||
for ue in ues
|
||||
]
|
||||
ue_ids = [""] + [ue.id for ue in ues]
|
||||
|
@ -494,7 +494,7 @@ def _normalize_apo_fields(infolist):
|
||||
infolist: liste de dict renvoyés par le portail Apogee
|
||||
|
||||
recode les champs: paiementinscription (-> booleen), datefinalisationinscription (date)
|
||||
ajoute le champs 'paiementinscription_str' : 'ok', 'Non' ou '?'
|
||||
ajoute le champ 'paiementinscription_str' : 'ok', 'Non' ou '?'
|
||||
ajoute les champs 'etape' (= None) et 'prenom' ('') s'ils ne sont pas présents.
|
||||
ajoute le champ 'civilite_etat_civil' (=''), et 'prenom_etat_civil' (='') si non présent.
|
||||
"""
|
||||
|
@ -342,13 +342,15 @@ def _build_page(
|
||||
"\n".join(options),
|
||||
"""</select>
|
||||
""",
|
||||
""
|
||||
if read_only
|
||||
else f"""
|
||||
(
|
||||
""
|
||||
if read_only
|
||||
else f"""
|
||||
<input type="hidden" name="formsemestre_id" value="{sem['formsemestre_id']}"/>
|
||||
<input type="submit" name="submitted" value="Appliquer les modifications"/>
|
||||
<a href="#help">aide</a>
|
||||
""",
|
||||
"""
|
||||
),
|
||||
sco_inscr_passage.etuds_select_boxes(
|
||||
etuds_by_cat,
|
||||
sel_inscrits=False,
|
||||
@ -356,9 +358,11 @@ def _build_page(
|
||||
base_url=base_url,
|
||||
read_only=read_only,
|
||||
),
|
||||
""
|
||||
if read_only
|
||||
else """<p/><input type="submit" name="submitted" value="Appliquer les modifications"/>""",
|
||||
(
|
||||
""
|
||||
if read_only
|
||||
else """<p/><input type="submit" name="submitted" value="Appliquer les modifications"/>"""
|
||||
),
|
||||
formsemestre_synchro_etuds_help(sem),
|
||||
"""</form>""",
|
||||
]
|
||||
@ -420,9 +424,9 @@ def list_synch(sem, annee_apogee=None):
|
||||
log(f"XXX key2etud etudid={etudid}, type {type(etudid)}")
|
||||
etud = etuds[0]
|
||||
etud["inscrit"] = is_inscrit # checkbox state
|
||||
etud[
|
||||
"datefinalisationinscription"
|
||||
] = date_finalisation_inscr_by_nip.get(key, None)
|
||||
etud["datefinalisationinscription"] = (
|
||||
date_finalisation_inscr_by_nip.get(key, None)
|
||||
)
|
||||
if key in etudsapo_ident:
|
||||
etud["etape"] = etudsapo_ident[key].get("etape", "")
|
||||
else:
|
||||
@ -855,7 +859,7 @@ def formsemestre_import_etud_admission(
|
||||
if import_email:
|
||||
if not "mail" in data_apo:
|
||||
raise ScoValueError(
|
||||
"la réponse portail n'a pas le champs requis 'mail'"
|
||||
"la réponse portail n'a pas le champ requis 'mail'"
|
||||
)
|
||||
if (
|
||||
adresse.email != data_apo["mail"]
|
||||
|
@ -1018,7 +1018,7 @@ def flash_errors(form):
|
||||
"""Flashes form errors (version sommaire)"""
|
||||
for field, errors in form.errors.items():
|
||||
flash(
|
||||
"Erreur: voir le champs %s" % (getattr(form, field).label.text,),
|
||||
"Erreur: voir le champ %s" % (getattr(form, field).label.text,),
|
||||
"warning",
|
||||
)
|
||||
# see https://getbootstrap.com/docs/4.0/components/alerts/
|
||||
|
@ -682,9 +682,9 @@ class RowRecap(tb.Row):
|
||||
self.add_ue_modimpls_cols(ue, ue_status["is_capitalized"])
|
||||
|
||||
self.nb_ues_etud_parcours = len(res.etud_parcours_ues_ids(etud.id))
|
||||
ue_valid_txt = (
|
||||
ue_valid_txt_html
|
||||
) = f"{self.nb_ues_validables}/{self.nb_ues_etud_parcours}"
|
||||
ue_valid_txt = ue_valid_txt_html = (
|
||||
f"{self.nb_ues_validables}/{self.nb_ues_etud_parcours}"
|
||||
)
|
||||
if self.nb_ues_warning:
|
||||
ue_valid_txt_html += " " + scu.EMO_WARNING
|
||||
cell_class = ""
|
||||
@ -708,9 +708,9 @@ class RowRecap(tb.Row):
|
||||
# sous-classé par JuryRow pour ajouter les codes
|
||||
table: TableRecap = self.table
|
||||
formsemestre: FormSemestre = table.res.formsemestre
|
||||
table.group_titles[
|
||||
"col_ue"
|
||||
] = f"UEs du S{formsemestre.semestre_id} {formsemestre.annee_scolaire()}"
|
||||
table.group_titles["col_ue"] = (
|
||||
f"UEs du S{formsemestre.semestre_id} {formsemestre.annee_scolaire()}"
|
||||
)
|
||||
col_id = f"moy_ue_{ue.id}"
|
||||
val = (
|
||||
ue_status["moy"]
|
||||
@ -740,7 +740,7 @@ class RowRecap(tb.Row):
|
||||
)
|
||||
table.foot_title_row.cells[col_id].target_attrs[
|
||||
"title"
|
||||
] = f"""{ue.titre} S{ue.semestre_idx or '?'}"""
|
||||
] = f"""{ue.titre or ue.acronyme} S{ue.semestre_idx or '?'}"""
|
||||
|
||||
def add_ue_modimpls_cols(self, ue: UniteEns, is_capitalized: bool):
|
||||
"""Ajoute à row les moyennes des modules (ou ressources et SAÉs) dans l'UE"""
|
||||
|
@ -49,7 +49,7 @@ table#edt2group tbody tr.active-row {
|
||||
</div>
|
||||
|
||||
{% if ScoDocSiteConfig.get("edt_ics_group_field") %}
|
||||
<div>Les groupes sont extrait du champs <b>{{ScoDocSiteConfig.get("edt_ics_group_field")}}</b>
|
||||
<div>Les groupes sont extrait du champ <b>{{ScoDocSiteConfig.get("edt_ics_group_field")}}</b>
|
||||
à l'aide de l'expression régulière: <tt>{{ScoDocSiteConfig.get("edt_ics_group_regexp")}}</tt>
|
||||
</div>
|
||||
{% else %}
|
||||
|
@ -43,12 +43,12 @@
|
||||
<h3>Avis de poursuites d'études de la promo {{ annee_diplome }}</h3>
|
||||
|
||||
<div class="help">
|
||||
Seront (a minima) pris en compte les étudiants ayant été inscrits aux semestres suivants :
|
||||
Seront pris en compte les étudiants ayant été inscrits à l'un des semestres suivants :
|
||||
|
||||
<ul>
|
||||
{% for fid in cosemestres %}
|
||||
{% for rang in rangs_tries %}
|
||||
<li>
|
||||
{{ cosemestres[fid].titre_annee() }}
|
||||
<strong>Semestre {{rang}}</strong> : {{ cosemestres[rang] }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{# Édition liste UEs APC #}
|
||||
{% for semestre_idx in semestre_ids %}
|
||||
<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}} - {{ects_by_sem[semestre_idx] | safe}} ECTS
|
||||
</div>
|
||||
<div class="formation_list_ues_content">
|
||||
@ -9,14 +9,14 @@
|
||||
{% for ue in ues_by_sem[semestre_idx] %}
|
||||
<li class="notes_ue_list">
|
||||
{% if editable and not loop.first %}
|
||||
<a href="{{ url_for('notes.ue_move',
|
||||
<a href="{{ url_for('notes.ue_move',
|
||||
scodoc_dept=g.scodoc_dept, ue_id=ue.id, after=0 )
|
||||
}}" class="aud">{{icons.arrow_up|safe}}</a>
|
||||
{% else %}
|
||||
{{icons.arrow_none|safe}}
|
||||
{% endif %}
|
||||
{% if editable and not loop.last %}
|
||||
<a href="{{ url_for('notes.ue_move',
|
||||
<a href="{{ url_for('notes.ue_move',
|
||||
scodoc_dept=g.scodoc_dept, ue_id=ue.id, after=1 )
|
||||
}}" class="aud">{{icons.arrow_down|safe}}</a>
|
||||
{% else %}
|
||||
@ -24,7 +24,7 @@
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
<a class="smallbutton" href="{{ url_for('notes.ue_delete',
|
||||
<a class="smallbutton" href="{{ url_for('notes.ue_delete',
|
||||
scodoc_dept=g.scodoc_dept, ue_id=ue.id)
|
||||
}}">{% if editable and not ue.modules.count() %}{{icons.delete|safe}}{% else
|
||||
%}{{icons.delete_disabled|safe}}{% endif %}</a>
|
||||
@ -34,12 +34,12 @@
|
||||
ue.color if ue.color is not none else 'blue'}}"></span>
|
||||
<b>{{ue.acronyme}} <a class="discretelink" href="{{
|
||||
url_for('notes.ue_infos', scodoc_dept=g.scodoc_dept, ue_id=ue.id)}}" title="{{ue.acronyme}}: {{
|
||||
('pas de compétence associée'
|
||||
if ue.niveau_competence is none
|
||||
('pas de compétence associée'
|
||||
if ue.niveau_competence is none
|
||||
else 'compétence ' + ue.niveau_competence.annee + ' ' + ue.niveau_competence.competence.titre_long)
|
||||
if ue.type == 0
|
||||
else ''
|
||||
}}">{{ue.titre}}</a>
|
||||
}}">{{ue.titre or ue.acronyme}}</a>
|
||||
</b>
|
||||
{% set virg = joiner(", ") %}
|
||||
<span class="ue_code">(
|
||||
@ -66,7 +66,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if editable and not ue.is_locked() %}
|
||||
<a class="stdlink" href="{{ url_for('notes.ue_edit',
|
||||
<a class="stdlink" href="{{ url_for('notes.ue_edit',
|
||||
scodoc_dept=g.scodoc_dept, ue_id=ue.id)
|
||||
}}">modifier</a>
|
||||
{% endif %}
|
||||
@ -100,8 +100,8 @@
|
||||
{% if editable %}
|
||||
<ul>
|
||||
<li class="notes_ue_list notes_ue_list_add"><a class="stdlink" href="{{
|
||||
url_for('notes.ue_create',
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
url_for('notes.ue_create',
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formation_id=formation.id,
|
||||
default_semestre_idx=semestre_idx,
|
||||
)}}">ajouter une UE</a>
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
{% block app_content %}
|
||||
<!-- begin ue_infos -->
|
||||
<h2>Unité d'Enseignement {{ue.acronyme|e}} {{ue.titre}}</h2>
|
||||
<h2>Unité d'Enseignement {{ue.acronyme|e}} {{(ue.titre or '')|e}}</h2>
|
||||
<div class="ue_infos">
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
{% if loop.first %}
|
||||
<ul>
|
||||
{% endif %}
|
||||
<li><a href="{{url_for('notes.formsemestre_status',
|
||||
<li><a href="{{url_for('notes.formsemestre_status',
|
||||
scodoc_dept=g.scodoc_dept, formsemestre_id=sem.id )}}">{{sem.titre_mois()}}</a></li>
|
||||
{% if loop.last %}
|
||||
</ul>
|
||||
|
@ -2169,9 +2169,6 @@ def _module_selector_multiple(
|
||||
return render_template(
|
||||
"assiduites/widgets/moduleimpl_selector_multiple.j2",
|
||||
choices=choices,
|
||||
formsemestre_id=(
|
||||
only_form.id if only_form else list(modimpls_by_formsemestre.keys())[0]
|
||||
),
|
||||
moduleimpl_id=moduleimpl_id,
|
||||
)
|
||||
|
||||
|
@ -113,7 +113,7 @@ def table_modules_ue_coefs(formation_id, semestre_idx=None, parcours_id: int = N
|
||||
"y": 1, # 1ere ligne
|
||||
"style": "title_ue",
|
||||
"data": ue.acronyme,
|
||||
"title": ue.titre,
|
||||
"title": ue.titre or ue.acronymexs,
|
||||
}
|
||||
for (col, ue) in enumerate(ues, start=2)
|
||||
]
|
||||
@ -214,11 +214,13 @@ def edit_modules_ue_coefs():
|
||||
{lockicon}
|
||||
</h2>
|
||||
""",
|
||||
"""<span class="warning">Formation verrouilée car un ou plusieurs
|
||||
(
|
||||
"""<span class="warning">Formation verrouilée car un ou plusieurs
|
||||
semestres verrouillés l'utilisent.
|
||||
</span>"""
|
||||
if locked
|
||||
else "",
|
||||
if locked
|
||||
else ""
|
||||
),
|
||||
render_template(
|
||||
"pn/form_modules_ue_coefs.j2",
|
||||
formation=formation,
|
||||
|
@ -61,11 +61,11 @@ class DevConfig(Config):
|
||||
DEBUG = True
|
||||
TESTING = False
|
||||
SQLALCHEMY_DATABASE_URI = (
|
||||
os.environ.get("SCODOC_DATABASE_URI") or "postgresql:///SCODOC_DEV"
|
||||
os.environ.get("SCODOC_DATABASE_URI") or "postgresql:///SCODOC"
|
||||
)
|
||||
SECRET_KEY = os.environ.get("DEV_SECRET_KEY") or "bb3faec7d9a34eb68a8e3e710087d87a"
|
||||
# pour le avoir url_for dans le shell:
|
||||
# SERVER_NAME = os.environ.get("SCODOC_TEST_SERVER_NAME") or "localhost"
|
||||
# SERVER_NAME = "http://localhost:8080"
|
||||
|
||||
|
||||
class TestConfig(DevConfig):
|
||||
|
56
migrations/versions/2e4875004e12_etudiant_annotations.py
Normal file
56
migrations/versions/2e4875004e12_etudiant_annotations.py
Normal file
@ -0,0 +1,56 @@
|
||||
"""etudiant_annotations : ajoute clé externe etudiant et moduleimpl
|
||||
|
||||
Revision ID: 2e4875004e12
|
||||
Revises: 3fa988ff8970
|
||||
Create Date: 2024-02-11 12:10:36.743212
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "2e4875004e12"
|
||||
down_revision = "3fa988ff8970"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
|
||||
# Supprime les annotations orphelines
|
||||
op.execute(
|
||||
"""DELETE FROM etud_annotations
|
||||
WHERE etudid NOT IN (SELECT id FROM identite);
|
||||
"""
|
||||
)
|
||||
# Ajoute clé:
|
||||
with op.batch_alter_table("etud_annotations", schema=None) as batch_op:
|
||||
batch_op.create_foreign_key(None, "identite", ["etudid"], ["id"])
|
||||
|
||||
# Et modif liée au commit 072d013590abf715395bc987fb48de49f6750527
|
||||
with op.batch_alter_table("notes_moduleimpl", schema=None) as batch_op:
|
||||
batch_op.drop_constraint(
|
||||
"notes_moduleimpl_responsable_id_fkey", type_="foreignkey"
|
||||
)
|
||||
batch_op.create_foreign_key(
|
||||
None, "user", ["responsable_id"], ["id"], ondelete="SET NULL"
|
||||
)
|
||||
|
||||
# cet index en trop trainait depuis longtemps...
|
||||
with op.batch_alter_table("assiduites", schema=None) as batch_op:
|
||||
batch_op.drop_index("ix_assiduites_user_id")
|
||||
|
||||
|
||||
def downgrade():
|
||||
with op.batch_alter_table("notes_moduleimpl", schema=None) as batch_op:
|
||||
batch_op.drop_constraint(None, type_="foreignkey")
|
||||
batch_op.create_foreign_key(
|
||||
"notes_moduleimpl_responsable_id_fkey", "user", ["responsable_id"], ["id"]
|
||||
)
|
||||
|
||||
with op.batch_alter_table("etud_annotations", schema=None) as batch_op:
|
||||
batch_op.drop_constraint(None, type_="foreignkey")
|
||||
|
||||
with op.batch_alter_table("assiduites", schema=None) as batch_op:
|
||||
batch_op.create_index("ix_assiduites_user_id", ["user_id"], unique=False)
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.6.938"
|
||||
SCOVERSION = "9.6.939"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
@ -11,14 +11,12 @@ Usage: pytest tests/scenarios/test_scenario1_formation.py
|
||||
"""
|
||||
# code écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en août 2021
|
||||
|
||||
import random
|
||||
|
||||
from tests.unit import sco_fake_gen
|
||||
from app.scodoc import sco_edit_module
|
||||
from app.scodoc import sco_formations
|
||||
from app.scodoc import sco_moduleimpl
|
||||
|
||||
|
||||
@pytest.mark.skip # test obsolete
|
||||
def test_scenario1(test_client):
|
||||
"""Applique "scenario 1"""
|
||||
run_scenario1()
|
||||
@ -28,7 +26,9 @@ def run_scenario1():
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
|
||||
# Lecture fichier XML local:
|
||||
with open("tests/unit/formation-exemple-1.xml") as f:
|
||||
with open(
|
||||
"tests/ressources/formations/formation-exemple-1.xml", encoding="utf8"
|
||||
) as f:
|
||||
doc = f.read()
|
||||
|
||||
# --- Création de la formation
|
||||
|
Loading…
Reference in New Issue
Block a user