Compare commits
13 Commits
7eb41fb2eb
...
99bb0f471b
Author | SHA1 | Date | |
---|---|---|---|
99bb0f471b | |||
35a038fd3a | |||
b46556c189 | |||
|
71f90f5261 | ||
|
1b037d6c7c | ||
60a97b7baf | |||
|
0332553587 | ||
|
958cf435c8 | ||
|
c69e9c34a0 | ||
|
17f8771b0b | ||
9003a2ca87 | |||
55ecaa45a9 | |||
ab39454a0d |
@ -393,7 +393,7 @@ class BulletinBUT:
|
|||||||
else:
|
else:
|
||||||
etud_ues_ids = res.etud_ues_ids(etud.id)
|
etud_ues_ids = res.etud_ues_ids(etud.id)
|
||||||
|
|
||||||
nbabs, nbabsjust = formsemestre.get_abs_count(etud.id)
|
nbabsnj, nbabsjust, nbabs = formsemestre.get_abs_count(etud.id)
|
||||||
etud_groups = sco_groups.get_etud_formsemestre_groups(
|
etud_groups = sco_groups.get_etud_formsemestre_groups(
|
||||||
etud, formsemestre, only_to_show=True
|
etud, formsemestre, only_to_show=True
|
||||||
)
|
)
|
||||||
@ -408,7 +408,7 @@ class BulletinBUT:
|
|||||||
}
|
}
|
||||||
if self.prefs["bul_show_abs"]:
|
if self.prefs["bul_show_abs"]:
|
||||||
semestre_infos["absences"] = {
|
semestre_infos["absences"] = {
|
||||||
"injustifie": nbabs - nbabsjust,
|
"injustifie": nbabsnj,
|
||||||
"total": nbabs,
|
"total": nbabs,
|
||||||
"metrique": {
|
"metrique": {
|
||||||
"H.": "Heure(s)",
|
"H.": "Heure(s)",
|
||||||
@ -525,7 +525,7 @@ class BulletinBUT:
|
|||||||
d["demission"] = ""
|
d["demission"] = ""
|
||||||
|
|
||||||
# --- Absences
|
# --- Absences
|
||||||
d["nbabs"], d["nbabsjust"] = self.res.formsemestre.get_abs_count(etud.id)
|
_, d["nbabsjust"], d["nbabs"] = self.res.formsemestre.get_abs_count(etud.id)
|
||||||
|
|
||||||
# --- Decision Jury
|
# --- Decision Jury
|
||||||
infos, _ = sco_bulletins.etud_descr_situation_semestre(
|
infos, _ = sco_bulletins.etud_descr_situation_semestre(
|
||||||
@ -540,9 +540,9 @@ class BulletinBUT:
|
|||||||
|
|
||||||
d.update(infos)
|
d.update(infos)
|
||||||
# --- Rangs
|
# --- Rangs
|
||||||
d["rang_nt"] = (
|
d[
|
||||||
f"{d['semestre']['rang']['value']} / {d['semestre']['rang']['total']}"
|
"rang_nt"
|
||||||
)
|
] = f"{d['semestre']['rang']['value']} / {d['semestre']['rang']['total']}"
|
||||||
d["rang_txt"] = "Rang " + d["rang_nt"]
|
d["rang_txt"] = "Rang " + d["rang_nt"]
|
||||||
|
|
||||||
d.update(sco_bulletins.make_context_dict(self.res.formsemestre, d["etud"]))
|
d.update(sco_bulletins.make_context_dict(self.res.formsemestre, d["etud"]))
|
||||||
|
@ -241,7 +241,7 @@ def bulletin_but_xml_compat(
|
|||||||
|
|
||||||
# --- Absences
|
# --- Absences
|
||||||
if sco_preferences.get_preference("bul_show_abs", formsemestre_id):
|
if sco_preferences.get_preference("bul_show_abs", formsemestre_id):
|
||||||
nbabs, nbabsjust = formsemestre.get_abs_count(etud.id)
|
_, nbabsjust, nbabs = formsemestre.get_abs_count(etud.id)
|
||||||
doc.append(Element("absences", nbabs=str(nbabs), nbabsjust=str(nbabsjust)))
|
doc.append(Element("absences", nbabs=str(nbabs), nbabsjust=str(nbabsjust)))
|
||||||
|
|
||||||
# -------- LA SUITE EST COPIEE SANS MODIF DE sco_bulletins_xml.py ---------
|
# -------- LA SUITE EST COPIEE SANS MODIF DE sco_bulletins_xml.py ---------
|
||||||
|
@ -875,7 +875,7 @@ class FormSemestre(db.Model):
|
|||||||
|
|
||||||
def get_abs_count(self, etudid):
|
def get_abs_count(self, etudid):
|
||||||
"""Les comptes d'absences de cet étudiant dans ce semestre:
|
"""Les comptes d'absences de cet étudiant dans ce semestre:
|
||||||
tuple (nb abs, nb abs justifiées)
|
tuple (nb abs non just, nb abs justifiées, nb abs total)
|
||||||
Utilise un cache.
|
Utilise un cache.
|
||||||
"""
|
"""
|
||||||
from app.scodoc import sco_assiduites
|
from app.scodoc import sco_assiduites
|
||||||
|
@ -119,7 +119,7 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
)
|
)
|
||||||
"""Compétences (triées par nom, extraites des SxTag aggrégés)"""
|
"""Compétences (triées par nom, extraites des SxTag aggrégés)"""
|
||||||
aff = pe_affichage.repr_comp_et_ues(self.acronymes_ues_to_competences)
|
aff = pe_affichage.repr_comp_et_ues(self.acronymes_ues_to_competences)
|
||||||
pe_affichage.pe_print(f"--> Compétences : {', '.join(self.competences_sorted)}")
|
pe_affichage.pe_print(f"--> Compétences : {aff}")
|
||||||
|
|
||||||
# Les tags
|
# Les tags
|
||||||
self.tags_sorted = self._do_taglist()
|
self.tags_sorted = self._do_taglist()
|
||||||
@ -134,28 +134,24 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
for tag in self.tags_sorted:
|
for tag in self.tags_sorted:
|
||||||
pe_affichage.pe_print(f"--> Moyennes du tag 👜{tag}")
|
pe_affichage.pe_print(f"--> Moyennes du tag 👜{tag}")
|
||||||
|
|
||||||
# Traitement des inscriptions aux semX(tags)
|
# Cubes d'inscription (etudids_sorted x compétences_sorted x sxstags),
|
||||||
# ******************************************
|
# de notes et de coeffs pour la moyenne générale
|
||||||
# Cube d'inscription (etudids_sorted x compétences_sorted x sxstags)
|
# en "aggrégant" les données des sxstags, compétence par compétence
|
||||||
# indiquant quel sxtag est valide pour chaque étudiant
|
(
|
||||||
inscr_df, inscr_cube = self.compute_inscriptions_comps_cube(tag)
|
inscr_df,
|
||||||
|
inscr_cube,
|
||||||
|
notes_df,
|
||||||
|
notes_cube,
|
||||||
|
coeffs_df,
|
||||||
|
coeffs_cube,
|
||||||
|
) = self.compute_cubes(tag)
|
||||||
|
|
||||||
# Traitement des notes
|
# Calcule les moyennes, et synthétise les coeffs
|
||||||
# ********************
|
(
|
||||||
# Cube de notes (etudids_sorted x compétences_sorted x sxstags)
|
moys_competences,
|
||||||
notes_df, notes_cube = self.compute_notes_comps_cube(tag)
|
matrice_coeffs_moy_gen,
|
||||||
# Calcule les moyennes sous forme d'un dataframe en les "aggrégant"
|
) = self.compute_notes_et_coeffs_competences(
|
||||||
# compétence par compétence
|
notes_cube, coeffs_cube, inscr_cube
|
||||||
moys_competences = self.compute_notes_competences(notes_cube, inscr_cube)
|
|
||||||
|
|
||||||
# Traitement des coeffs pour la moyenne générale
|
|
||||||
# ***********************************************
|
|
||||||
# Df des coeffs sur tous les SxTags aggrégés
|
|
||||||
coeffs_df, coeffs_cube = self.compute_coeffs_comps_cube(tag)
|
|
||||||
|
|
||||||
# Synthèse des coefficients à prendre en compte pour la moyenne générale
|
|
||||||
matrice_coeffs_moy_gen = self.compute_coeffs_competences(
|
|
||||||
coeffs_cube, inscr_cube, notes_cube
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Affichage des coeffs
|
# Affichage des coeffs
|
||||||
@ -186,10 +182,17 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
else:
|
else:
|
||||||
return f"{self.__class__.__name__} {self.rcs_id}"
|
return f"{self.__class__.__name__} {self.rcs_id}"
|
||||||
|
|
||||||
def compute_notes_comps_cube(self, tag):
|
def compute_cubes(self, tag):
|
||||||
"""Pour un tag donné, construit le cube de notes (etudid x competences x SxTag)
|
"""Pour un tag donné, construit les cubes de :
|
||||||
nécessaire au calcul des moyennes,
|
* d'inscriptions aux compétences (etudid x competences x SxTag)
|
||||||
en remplaçant les données d'UE (obtenus du SxTag) par les compétences
|
* de notes (etudid x competences x SxTag)
|
||||||
|
* de coeffs (etudid x competences x SxTag)
|
||||||
|
|
||||||
|
nécessaire au calcul des moyennes, en :
|
||||||
|
|
||||||
|
* transformant les données des UEs en données de compétences (changement de noms)
|
||||||
|
* fusionnant les données d'un même semestre, lorsque plusieurs UEs traitent d'une même compétence (cas des RCSx = Sx)
|
||||||
|
* aggrégeant les données de compétences sur plusieurs semestres (cas des RCSx = xA ou xS)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
tag: Le tag visé
|
tag: Le tag visé
|
||||||
@ -197,144 +200,75 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
# etudids_sorted: list[int],
|
# etudids_sorted: list[int],
|
||||||
# competences_sorted: list[str],
|
# competences_sorted: list[str],
|
||||||
# sxstags: dict[(str, int) : pe_sxtag.SxTag],
|
# sxstags: dict[(str, int) : pe_sxtag.SxTag],
|
||||||
|
|
||||||
|
inscriptions_dfs = {}
|
||||||
notes_dfs = {}
|
notes_dfs = {}
|
||||||
|
coeffs_dfs = {}
|
||||||
|
|
||||||
for sxtag_id, sxtag in self.sxstags_aggreges.items():
|
for sxtag_id, sxtag in self.sxstags_aggreges.items():
|
||||||
# Partant d'un dataframe vierge
|
# Partant de dataframes vierges
|
||||||
|
inscription_df = pd.DataFrame(
|
||||||
|
np.nan, index=self.etudids_sorted, columns=self.competences_sorted
|
||||||
|
)
|
||||||
notes_df = pd.DataFrame(
|
notes_df = pd.DataFrame(
|
||||||
np.nan, index=self.etudids_sorted, columns=self.competences_sorted
|
np.nan, index=self.etudids_sorted, columns=self.competences_sorted
|
||||||
)
|
)
|
||||||
# Charge les notes du semestre tag (copie car changement de nom de colonnes à venir)
|
coeffs_df = pd.DataFrame(
|
||||||
|
np.nan, index=self.etudids_sorted, columns=self.competences_sorted
|
||||||
|
)
|
||||||
|
|
||||||
|
# Charge les données du semestre tag (copie car changement de nom de colonnes à venir)
|
||||||
if tag in sxtag.moyennes_tags: # si le tag est présent dans le semestre
|
if tag in sxtag.moyennes_tags: # si le tag est présent dans le semestre
|
||||||
moys_tag = sxtag.moyennes_tags[tag]
|
moys_tag = sxtag.moyennes_tags[tag]
|
||||||
|
|
||||||
notes = moys_tag.matrice_notes_gen.copy() # dataframe etudids x ues
|
# Les inscr, les notes, les coeffs
|
||||||
|
acro_ues_inscr_parcours = sxtag.acro_ues_inscr_parcours
|
||||||
|
notes = moys_tag.matrice_notes_gen
|
||||||
|
coeffs = moys_tag.matrice_coeffs_moy_gen # les coeffs
|
||||||
|
|
||||||
# Traduction des acronymes d'UE en compétences
|
# Traduction des acronymes d'UE en compétences
|
||||||
|
# comp_to_ues = pe_comp.asso_comp_to_accronymes(self.acronymes_ues_to_competences)
|
||||||
acronymes_ues_columns = notes.columns
|
acronymes_ues_columns = notes.columns
|
||||||
acronymes_to_comps = [
|
for acronyme in acronymes_ues_columns:
|
||||||
self.acronymes_ues_to_competences[acro]
|
# La compétence visée
|
||||||
for acro in acronymes_ues_columns
|
competence = self.acronymes_ues_to_competences[acronyme] # La comp
|
||||||
]
|
|
||||||
notes.columns = acronymes_to_comps
|
|
||||||
|
|
||||||
# Les étudiants et les compétences communes
|
# Les étud inscrits à la comp reportés dans l'inscription au RCSemX
|
||||||
(
|
comp_inscr = acro_ues_inscr_parcours[
|
||||||
etudids_communs,
|
acro_ues_inscr_parcours.notnull()
|
||||||
comp_communes,
|
].index
|
||||||
) = pe_comp.find_index_and_columns_communs(notes_df, notes)
|
etudids_communs = list(
|
||||||
|
inscription_df.index.intersection(comp_inscr)
|
||||||
|
)
|
||||||
|
inscription_df.loc[
|
||||||
|
etudids_communs, competence
|
||||||
|
] = acro_ues_inscr_parcours.loc[etudids_communs, acronyme]
|
||||||
|
|
||||||
# Recopie des notes et des coeffs
|
# Les étud ayant une note à l'acronyme de la comp (donc à la comp)
|
||||||
notes_df.loc[etudids_communs, comp_communes] = notes.loc[
|
etuds_avec_notes = notes[notes[acronyme].notnull()].index
|
||||||
etudids_communs, comp_communes
|
etudids_communs = list(
|
||||||
]
|
notes_df.index.intersection(etuds_avec_notes)
|
||||||
|
)
|
||||||
|
notes_df.loc[etudids_communs, competence] = notes.loc[
|
||||||
|
etudids_communs, acronyme
|
||||||
|
]
|
||||||
|
|
||||||
|
# Les coeffs
|
||||||
|
etuds_avec_coeffs = coeffs[coeffs[acronyme].notnull()].index
|
||||||
|
etudids_communs = list(
|
||||||
|
coeffs_df.index.intersection(etuds_avec_coeffs)
|
||||||
|
)
|
||||||
|
coeffs_df.loc[etudids_communs, competence] = coeffs.loc[
|
||||||
|
etudids_communs, acronyme
|
||||||
|
]
|
||||||
# Supprime tout ce qui n'est pas numérique
|
# Supprime tout ce qui n'est pas numérique
|
||||||
# for col in notes_df.columns:
|
# for col in notes_df.columns:
|
||||||
# notes_df[col] = pd.to_numeric(notes_df[col], errors="coerce")
|
# notes_df[col] = pd.to_numeric(notes_df[col], errors="coerce")
|
||||||
|
|
||||||
# Stocke les dfs
|
|
||||||
notes_dfs[sxtag_id] = notes_df
|
|
||||||
|
|
||||||
"""Réunit les notes sous forme d'un cube etudids x competences x semestres"""
|
|
||||||
sxtag_x_etudids_x_comps = [
|
|
||||||
notes_dfs[sxtag_id] for sxtag_id in self.sxstags_aggreges
|
|
||||||
]
|
|
||||||
notes_etudids_x_comps_x_sxtag = np.stack(sxtag_x_etudids_x_comps, axis=-1)
|
|
||||||
|
|
||||||
return notes_dfs, notes_etudids_x_comps_x_sxtag
|
|
||||||
|
|
||||||
def compute_coeffs_comps_cube(self, tag):
|
|
||||||
"""Pour un tag donné, construit
|
|
||||||
le cube de coeffs (etudid x competences x SxTag) (traduisant les inscriptions
|
|
||||||
des étudiants aux UEs en fonction de leur parcours)
|
|
||||||
qui s'applique aux différents SxTag
|
|
||||||
en remplaçant les données d'UE (obtenus du SxTag) par les compétences
|
|
||||||
|
|
||||||
Args:
|
|
||||||
tag: Le tag visé
|
|
||||||
"""
|
|
||||||
# etudids_sorted: list[int],
|
|
||||||
# competences_sorted: list[str],
|
|
||||||
# sxstags: dict[(str, int) : pe_sxtag.SxTag],
|
|
||||||
|
|
||||||
coeffs_dfs = {}
|
|
||||||
|
|
||||||
for sxtag_id, sxtag in self.sxstags_aggreges.items():
|
|
||||||
# Partant d'un dataframe vierge
|
|
||||||
coeffs_df = pd.DataFrame(
|
|
||||||
np.nan, index=self.etudids_sorted, columns=self.competences_sorted
|
|
||||||
)
|
|
||||||
if tag in sxtag.moyennes_tags:
|
|
||||||
moys_tag = sxtag.moyennes_tags[tag]
|
|
||||||
|
|
||||||
# Charge les notes et les coeffs du semestre tag
|
|
||||||
coeffs = moys_tag.matrice_coeffs_moy_gen.copy() # les coeffs
|
|
||||||
|
|
||||||
# Traduction des acronymes d'UE en compétences
|
|
||||||
acronymes_ues_columns = coeffs.columns
|
|
||||||
acronymes_to_comps = [
|
|
||||||
self.acronymes_ues_to_competences[acro]
|
|
||||||
for acro in acronymes_ues_columns
|
|
||||||
]
|
|
||||||
coeffs.columns = acronymes_to_comps
|
|
||||||
|
|
||||||
# Les étudiants et les compétences communes
|
|
||||||
etudids_communs, comp_communes = pe_comp.find_index_and_columns_communs(
|
|
||||||
coeffs_df, coeffs
|
|
||||||
)
|
|
||||||
|
|
||||||
# Recopie des notes et des coeffs
|
|
||||||
coeffs_df.loc[etudids_communs, comp_communes] = coeffs.loc[
|
|
||||||
etudids_communs, comp_communes
|
|
||||||
]
|
|
||||||
|
|
||||||
# Stocke les dfs
|
|
||||||
coeffs_dfs[sxtag_id] = coeffs_df
|
|
||||||
|
|
||||||
"""Réunit les coeffs sous forme d'un cube etudids x competences x semestres"""
|
|
||||||
sxtag_x_etudids_x_comps = [
|
|
||||||
coeffs_dfs[sxtag_id] for sxtag_id in self.sxstags_aggreges
|
|
||||||
]
|
|
||||||
coeffs_etudids_x_comps_x_sxtag = np.stack(sxtag_x_etudids_x_comps, axis=-1)
|
|
||||||
|
|
||||||
return coeffs_dfs, coeffs_etudids_x_comps_x_sxtag
|
|
||||||
|
|
||||||
def compute_inscriptions_comps_cube(
|
|
||||||
self,
|
|
||||||
tag,
|
|
||||||
):
|
|
||||||
"""Pour un tag donné, construit
|
|
||||||
le cube etudid x competences x SxTag traduisant quels sxtags est à prendre
|
|
||||||
en compte pour chaque étudiant.
|
|
||||||
Contient des 0 et des 1 pour indiquer la prise en compte.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
tag: Le tag visé
|
|
||||||
"""
|
|
||||||
# etudids_sorted: list[int],
|
|
||||||
# competences_sorted: list[str],
|
|
||||||
# sxstags: dict[(str, int) : pe_sxtag.SxTag],
|
|
||||||
# Initialisation
|
|
||||||
inscriptions_dfs = {}
|
|
||||||
|
|
||||||
for sxtag_id, sxtag in self.sxstags_aggreges.items():
|
|
||||||
# Partant d'un dataframe vierge
|
|
||||||
inscription_df = pd.DataFrame(
|
|
||||||
0, index=self.etudids_sorted, columns=self.competences_sorted
|
|
||||||
)
|
|
||||||
|
|
||||||
# Les étudiants dont les résultats au sxtag ont été calculés
|
|
||||||
etudids_sxtag = sxtag.etudids_sorted
|
|
||||||
|
|
||||||
# Les étudiants communs
|
|
||||||
etudids_communs = sorted(set(self.etudids_sorted) & set(etudids_sxtag))
|
|
||||||
|
|
||||||
# Acte l'inscription
|
|
||||||
inscription_df.loc[etudids_communs, :] = 1
|
|
||||||
|
|
||||||
# Stocke les dfs
|
# Stocke les dfs
|
||||||
inscriptions_dfs[sxtag_id] = inscription_df
|
inscriptions_dfs[sxtag_id] = inscription_df
|
||||||
|
notes_dfs[sxtag_id] = notes_df
|
||||||
|
coeffs_dfs[sxtag_id] = coeffs_df
|
||||||
|
|
||||||
"""Réunit les inscriptions sous forme d'un cube etudids x competences x semestres"""
|
"""Réunit les inscriptions sous forme d'un cube etudids x competences x semestres"""
|
||||||
sxtag_x_etudids_x_comps = [
|
sxtag_x_etudids_x_comps = [
|
||||||
@ -344,7 +278,26 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
sxtag_x_etudids_x_comps, axis=-1
|
sxtag_x_etudids_x_comps, axis=-1
|
||||||
)
|
)
|
||||||
|
|
||||||
return inscriptions_dfs, inscriptions_etudids_x_comps_x_sxtag
|
"""Réunit les notes sous forme d'un cube etudids x competences x semestres"""
|
||||||
|
sxtag_x_etudids_x_comps = [
|
||||||
|
notes_dfs[sxtag_id] for sxtag_id in self.sxstags_aggreges
|
||||||
|
]
|
||||||
|
notes_etudids_x_comps_x_sxtag = np.stack(sxtag_x_etudids_x_comps, axis=-1)
|
||||||
|
|
||||||
|
"""Réunit les coeffs sous forme d'un cube etudids x competences x semestres"""
|
||||||
|
sxtag_x_etudids_x_comps = [
|
||||||
|
coeffs_dfs[sxtag_id] for sxtag_id in self.sxstags_aggreges
|
||||||
|
]
|
||||||
|
coeffs_etudids_x_comps_x_sxtag = np.stack(sxtag_x_etudids_x_comps, axis=-1)
|
||||||
|
|
||||||
|
return (
|
||||||
|
inscriptions_dfs,
|
||||||
|
inscriptions_etudids_x_comps_x_sxtag,
|
||||||
|
notes_dfs,
|
||||||
|
notes_etudids_x_comps_x_sxtag,
|
||||||
|
coeffs_dfs,
|
||||||
|
coeffs_etudids_x_comps_x_sxtag,
|
||||||
|
)
|
||||||
|
|
||||||
def _do_taglist(self) -> list[str]:
|
def _do_taglist(self) -> list[str]:
|
||||||
"""Synthétise les tags à partir des Sxtags aggrégés.
|
"""Synthétise les tags à partir des Sxtags aggrégés.
|
||||||
@ -370,7 +323,9 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
dict_competences |= sxtag.acronymes_ues_to_competences
|
dict_competences |= sxtag.acronymes_ues_to_competences
|
||||||
return dict_competences
|
return dict_competences
|
||||||
|
|
||||||
def compute_notes_competences(self, set_cube: np.array, inscriptions: np.array):
|
def compute_notes_et_coeffs_competences(
|
||||||
|
self, notes_cube: np.array, coeffs_cube: np.array, inscr_mask: np.array
|
||||||
|
):
|
||||||
"""Calcule la moyenne par compétences (à un tag donné) sur plusieurs semestres (partant du set_cube).
|
"""Calcule la moyenne par compétences (à un tag donné) sur plusieurs semestres (partant du set_cube).
|
||||||
|
|
||||||
La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles
|
La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles
|
||||||
@ -379,9 +334,11 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
par aggrégat de plusieurs semestres.
|
par aggrégat de plusieurs semestres.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
set_cube: notes moyennes aux compétences ndarray
|
notes_cube: notes moyennes aux compétences ndarray
|
||||||
(etuds x UEs|compétences x sxtags), des floats avec des NaN
|
(etuds x UEs|compétences x sxtags), des floats avec des NaN
|
||||||
inscriptions: inscrptions aux compétences ndarray
|
coeffs_cube: coeffs appliqués aux compétences
|
||||||
|
(etuds x UEs|compétences x sxtags), des floats avec des NaN
|
||||||
|
inscr_mask: inscrptions aux compétences ndarray
|
||||||
(etuds x UEs|compétences x sxtags), des 0 et des 1
|
(etuds x UEs|compétences x sxtags), des 0 et des 1
|
||||||
Returns:
|
Returns:
|
||||||
Un DataFrame avec pour columns les moyennes par tags,
|
Un DataFrame avec pour columns les moyennes par tags,
|
||||||
@ -389,78 +346,45 @@ class RCSemXTag(pe_tabletags.TableTag):
|
|||||||
"""
|
"""
|
||||||
# etudids_sorted: liste des étudiants (dim. 0 du cube)
|
# etudids_sorted: liste des étudiants (dim. 0 du cube)
|
||||||
# competences_sorted: list (dim. 1 du cube)
|
# competences_sorted: list (dim. 1 du cube)
|
||||||
nb_etuds, nb_comps, nb_semestres = set_cube.shape
|
nb_etuds, nb_comps, nb_semestres = notes_cube.shape
|
||||||
# assert nb_etuds == len(etudids_sorted)
|
# assert nb_etuds == len(etudids_sorted)
|
||||||
# assert nb_comps == len(competences_sorted)
|
# assert nb_comps == len(competences_sorted)
|
||||||
|
|
||||||
# Applique le masque d'inscriptions
|
# Applique le masque d'inscriptions aux notes et aux coeffs
|
||||||
set_cube_significatif = set_cube * inscriptions
|
notes_significatives = notes_cube * inscr_mask
|
||||||
|
coeffs_significatifs = coeffs_cube * inscr_mask
|
||||||
|
|
||||||
# Quelles entrées du cube contiennent des notes ?
|
# Enlève les NaN des cubes pour les entrées manquantes
|
||||||
mask = ~np.isnan(set_cube_significatif)
|
notes_no_nan = np.nan_to_num(notes_significatives, nan=0.0)
|
||||||
|
coeffs_no_nan = np.nan_to_num(coeffs_significatifs, nan=0.0)
|
||||||
# Enlève les NaN du cube de notes pour les entrées manquantes
|
|
||||||
set_cube_no_nan = np.nan_to_num(set_cube_significatif, nan=0.0)
|
|
||||||
|
|
||||||
# Les moyennes par tag
|
# Les moyennes par tag
|
||||||
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
|
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
|
||||||
etud_moy_tag = np.sum(set_cube_no_nan, axis=2) / np.sum(mask, axis=2)
|
mask = ~np.isnan(
|
||||||
|
notes_significatives
|
||||||
|
) # Quelles entrées contiennent des notes ?
|
||||||
|
etud_moy_tag = np.sum(notes_no_nan, axis=2) / np.sum(mask, axis=2)
|
||||||
|
|
||||||
|
coeffs_pris_en_compte = coeffs_no_nan * mask
|
||||||
|
coeff_tag = np.sum(coeffs_pris_en_compte, axis=2)
|
||||||
|
|
||||||
|
inscr_prise_en_compte = inscr_mask * mask
|
||||||
|
inscr_prise_en_compte = np.nan_to_num(inscr_prise_en_compte, nan=-1.0)
|
||||||
|
inscr_tag = np.max(inscr_prise_en_compte, axis=2)
|
||||||
|
inscr_tag[inscr_tag < 0] = np.NaN # fix les max non calculés (-1) -> Na?
|
||||||
|
|
||||||
# Le dataFrame des notes moyennes
|
# Le dataFrame des notes moyennes
|
||||||
|
etud_moy_tag = etud_moy_tag * inscr_tag
|
||||||
etud_moy_tag_df = pd.DataFrame(
|
etud_moy_tag_df = pd.DataFrame(
|
||||||
etud_moy_tag,
|
etud_moy_tag,
|
||||||
index=self.etudids_sorted, # les etudids
|
index=self.etudids_sorted, # les etudids
|
||||||
columns=self.competences_sorted, # les competences
|
columns=self.competences_sorted, # les competences
|
||||||
)
|
)
|
||||||
etud_moy_tag_df.fillna(np.nan)
|
|
||||||
|
|
||||||
return etud_moy_tag_df
|
|
||||||
|
|
||||||
def compute_coeffs_competences(
|
|
||||||
self,
|
|
||||||
coeff_cube: np.array,
|
|
||||||
inscriptions: np.array,
|
|
||||||
set_cube: np.array,
|
|
||||||
):
|
|
||||||
"""Calcule les coeffs à utiliser pour la moyenne générale (toutes compétences
|
|
||||||
confondues), en fonction des inscriptions.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
coeffs_cube: coeffs impliqués dans la moyenne générale (semestres par semestres)
|
|
||||||
inscriptions: inscriptions aux UES|Compétences ndarray
|
|
||||||
(etuds x UEs|compétences x sxtags), des 0 ou des 1
|
|
||||||
set_cube: les notes
|
|
||||||
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Un DataFrame de coefficients (etudids_sorted x compétences_sorted)
|
|
||||||
"""
|
|
||||||
# etudids_sorted: liste des étudiants (dim. 0 du cube)
|
|
||||||
# competences_sorted: list (dim. 1 du cube)
|
|
||||||
nb_etuds, nb_comps, nb_semestres = inscriptions.shape
|
|
||||||
# assert nb_etuds == len(etudids_sorted)
|
|
||||||
# assert nb_comps == len(competences_sorted)
|
|
||||||
|
|
||||||
# Applique le masque des inscriptions aux coeffs et aux notes
|
|
||||||
coeffs_significatifs = coeff_cube * inscriptions
|
|
||||||
|
|
||||||
# Enlève les NaN du cube de notes pour les entrées manquantes
|
|
||||||
coeffs_cube_no_nan = np.nan_to_num(coeffs_significatifs, nan=0.0)
|
|
||||||
|
|
||||||
# Quelles entrées du cube contiennent des notes ?
|
|
||||||
mask = ~np.isnan(set_cube)
|
|
||||||
|
|
||||||
# Retire les coefficients associés à des données sans notes
|
|
||||||
coeffs_cube_no_nan = coeffs_cube_no_nan * mask
|
|
||||||
|
|
||||||
# Somme les coefficients (correspondant à des notes)
|
|
||||||
coeff_tag = np.sum(coeffs_cube_no_nan, axis=2)
|
|
||||||
|
|
||||||
# Le dataFrame des coeffs
|
# Le dataFrame des coeffs
|
||||||
|
coeff_tag = coeff_tag * inscr_tag # Réapplique le masque des inscriptions
|
||||||
coeffs_df = pd.DataFrame(
|
coeffs_df = pd.DataFrame(
|
||||||
coeff_tag, index=self.etudids_sorted, columns=self.competences_sorted
|
coeff_tag, index=self.etudids_sorted, columns=self.competences_sorted
|
||||||
)
|
)
|
||||||
# Remet à Nan les coeffs à 0
|
|
||||||
coeffs_df = coeffs_df.fillna(np.nan)
|
|
||||||
|
|
||||||
return coeffs_df
|
return etud_moy_tag_df, coeffs_df
|
||||||
|
@ -98,8 +98,10 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
self.parcours += [None]
|
self.parcours += [None]
|
||||||
|
|
||||||
# Les UEs en fonction des parcours
|
# Les UEs en fonction des parcours
|
||||||
self.ues_inscr_parcours_df = self.load_ues_inscr_parcours()
|
self.ues_inscr_parcours_df = (
|
||||||
"""Inscription des étudiants aux UEs des parcours"""
|
self.load_ues_inscr_parcours()
|
||||||
|
) # peut contenir du sport
|
||||||
|
"""Inscription des étudiants aux UEs des parcours (etudids x ue_ids)"""
|
||||||
|
|
||||||
# Les acronymes des UEs
|
# Les acronymes des UEs
|
||||||
self.ues_to_acronymes = {ue.id: ue.acronyme for ue in self.ues_standards}
|
self.ues_to_acronymes = {ue.id: ue.acronyme for ue in self.ues_standards}
|
||||||
@ -144,6 +146,12 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
f"--> Moyenne générale calculée avec pour coeffs d'UEs : {profils_aff}"
|
f"--> Moyenne générale calculée avec pour coeffs d'UEs : {profils_aff}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Les inscriptions aux acronymes d'ues
|
||||||
|
self.acro_ues_inscr_parcours = self._get_acro_ues_inscr_parcours(
|
||||||
|
self.ues_inscr_parcours_df, self.ues_standards
|
||||||
|
)
|
||||||
|
"""DataFrame indiquant à quelles UEs (données par leurs acronymes) sont inscrits les étudiants)"""
|
||||||
|
|
||||||
# Les capitalisations (mask etuids x acronyme_ue valant True si capitalisée, False sinon)
|
# Les capitalisations (mask etuids x acronyme_ue valant True si capitalisée, False sinon)
|
||||||
self.capitalisations = self._get_capitalisations(self.ues_standards)
|
self.capitalisations = self._get_capitalisations(self.ues_standards)
|
||||||
"""DataFrame indiquant les UEs capitalisables d'un étudiant (etudids x )"""
|
"""DataFrame indiquant les UEs capitalisables d'un étudiant (etudids x )"""
|
||||||
@ -167,7 +175,7 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Ajoute les moyennes par UEs + la moyenne générale (but)
|
# Ajoute les moyennes par UEs + la moyenne générale (but)
|
||||||
moy_gen = self.compute_moy_gen()
|
moy_gen = self.compute_moy_gen(self.acro_ues_inscr_parcours)
|
||||||
self.moyennes_tags["but"] = pe_moytag.MoyennesTag(
|
self.moyennes_tags["but"] = pe_moytag.MoyennesTag(
|
||||||
"but",
|
"but",
|
||||||
pe_moytag.CODE_MOY_UE,
|
pe_moytag.CODE_MOY_UE,
|
||||||
@ -240,6 +248,31 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
matrice_coeffs_moy_gen = matrice_coeffs_moy_gen.sort_index(axis=1)
|
matrice_coeffs_moy_gen = matrice_coeffs_moy_gen.sort_index(axis=1)
|
||||||
return matrice_coeffs_moy_gen
|
return matrice_coeffs_moy_gen
|
||||||
|
|
||||||
|
def _get_acro_ues_inscr_parcours(
|
||||||
|
self, ues_inscr_parcours_df: pd.DataFrame, ues_standards: list[UniteEns]
|
||||||
|
) -> pd.DataFrame:
|
||||||
|
"""Renvoie un dataFrame donnant les inscriptions (Nan ou 1) des
|
||||||
|
étudiants aux UEs définies par leur acronyme, en fonction de leur parcours
|
||||||
|
(cf. ues_inscr_parcours_df) et en limitant les données aux UEs standards (hors sport=
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ues_inscr_parcours_df: Les inscriptions des étudiants aux UEs
|
||||||
|
ues_standards: Les UEs standards à prendre en compte
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Un dataFrame etudids x acronymes_UEs avec les coeffs des UEs
|
||||||
|
"""
|
||||||
|
matrice_inscription = ues_inscr_parcours_df * [
|
||||||
|
1 for ue in ues_standards # if ue.type != UE_SPORT <= déjà supprimé
|
||||||
|
]
|
||||||
|
matrice_inscription.columns = [
|
||||||
|
self.ues_to_acronymes[ue.id] for ue in ues_standards
|
||||||
|
]
|
||||||
|
# Tri par etudids (dim 0) et par acronymes (dim 1)
|
||||||
|
matrice_inscription = matrice_inscription.sort_index()
|
||||||
|
matrice_inscription = matrice_inscription.sort_index(axis=1)
|
||||||
|
return matrice_inscription
|
||||||
|
|
||||||
def _get_capitalisations(self, ues_standards) -> pd.DataFrame:
|
def _get_capitalisations(self, ues_standards) -> pd.DataFrame:
|
||||||
"""Renvoie un dataFrame résumant les UEs capitalisables par les
|
"""Renvoie un dataFrame résumant les UEs capitalisables par les
|
||||||
étudiants, d'après les décisions de jury (sous réserve qu'elles existent).
|
étudiants, d'après les décisions de jury (sous réserve qu'elles existent).
|
||||||
@ -342,6 +375,9 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
colonnes = [ue.id for ue in self.ues_standards]
|
colonnes = [ue.id for ue in self.ues_standards]
|
||||||
moyennes_ues_tag = moyennes_ues_tag[colonnes]
|
moyennes_ues_tag = moyennes_ues_tag[colonnes]
|
||||||
|
|
||||||
|
# Met à zéro les moyennes non calculées/calculables
|
||||||
|
moyennes_ues_tag.fillna(0.0, inplace=True)
|
||||||
|
|
||||||
# Applique le masque d'inscription aux UE pour ne conserver que les UE dans lequel l'étudiant est inscrit
|
# Applique le masque d'inscription aux UE pour ne conserver que les UE dans lequel l'étudiant est inscrit
|
||||||
moyennes_ues_tag = moyennes_ues_tag[colonnes] * ues_inscr_parcours_df[colonnes]
|
moyennes_ues_tag = moyennes_ues_tag[colonnes] * ues_inscr_parcours_df[colonnes]
|
||||||
|
|
||||||
@ -355,7 +391,7 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
|
|
||||||
return moyennes_ues_tag
|
return moyennes_ues_tag
|
||||||
|
|
||||||
def compute_moy_gen(self):
|
def compute_moy_gen(self, acro_ues_inscr_parcours):
|
||||||
"""Récupère les moyennes des UEs pour le calcul de la moyenne générale,
|
"""Récupère les moyennes des UEs pour le calcul de la moyenne générale,
|
||||||
en associant à chaque UE.id son acronyme (toutes UEs confondues)
|
en associant à chaque UE.id son acronyme (toutes UEs confondues)
|
||||||
"""
|
"""
|
||||||
@ -368,6 +404,12 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
acronymes = [self.ues_to_acronymes[col] for col in colonnes]
|
acronymes = [self.ues_to_acronymes[col] for col in colonnes]
|
||||||
df_ues.columns = acronymes
|
df_ues.columns = acronymes
|
||||||
|
|
||||||
|
# Met à zéro les moyennes non calculées/calculables
|
||||||
|
df_ues.fillna(0.0, inplace=True)
|
||||||
|
|
||||||
|
# Réapplique le mask d'inscription
|
||||||
|
df_ues = df_ues * acro_ues_inscr_parcours
|
||||||
|
|
||||||
# Tri par ordre aphabétique de colonnes
|
# Tri par ordre aphabétique de colonnes
|
||||||
df_ues.sort_index(axis=1)
|
df_ues.sort_index(axis=1)
|
||||||
|
|
||||||
|
@ -141,6 +141,10 @@ class SxTag(pe_tabletags.TableTag):
|
|||||||
aff = pe_affichage.repr_asso_ue_comp(self.acronymes_ues_to_competences)
|
aff = pe_affichage.repr_asso_ue_comp(self.acronymes_ues_to_competences)
|
||||||
pe_affichage.pe_print(f"--> UEs/Compétences : {aff}")
|
pe_affichage.pe_print(f"--> UEs/Compétences : {aff}")
|
||||||
|
|
||||||
|
# Les inscriptions des étudiants aux UEs (donnée par leur acronyme)
|
||||||
|
# par report de celle du ressemfinal
|
||||||
|
self.acro_ues_inscr_parcours = self.ressembuttag_final.acro_ues_inscr_parcours
|
||||||
|
|
||||||
# Les coeffs pour la moyenne générale (traduisant également l'inscription
|
# Les coeffs pour la moyenne générale (traduisant également l'inscription
|
||||||
# des étudiants aux UEs) (etudids_sorted x acronymes_ues_sorted)
|
# des étudiants aux UEs) (etudids_sorted x acronymes_ues_sorted)
|
||||||
self.matrice_coeffs_moy_gen = self.ressembuttag_final.matrice_coeffs_moy_gen
|
self.matrice_coeffs_moy_gen = self.ressembuttag_final.matrice_coeffs_moy_gen
|
||||||
@ -178,7 +182,7 @@ class SxTag(pe_tabletags.TableTag):
|
|||||||
pe_affichage.pe_print(f" > MoyTag 👜{tag}")
|
pe_affichage.pe_print(f" > MoyTag 👜{tag}")
|
||||||
|
|
||||||
# Masque des inscriptions aux UEs (extraits de la matrice de coefficients)
|
# Masque des inscriptions aux UEs (extraits de la matrice de coefficients)
|
||||||
inscr_mask: np.array = ~np.isnan(self.matrice_coeffs_moy_gen.to_numpy())
|
# inscr_mask: np.array = ~np.isnan(self.matrice_coeffs_moy_gen.to_numpy())
|
||||||
|
|
||||||
# Moyennes (tous modules confondus)
|
# Moyennes (tous modules confondus)
|
||||||
if not self.has_notes_tag(tag):
|
if not self.has_notes_tag(tag):
|
||||||
@ -194,6 +198,7 @@ class SxTag(pe_tabletags.TableTag):
|
|||||||
notes_df_gen, notes_cube_gen = self.compute_notes_ues_cube(tag)
|
notes_df_gen, notes_cube_gen = self.compute_notes_ues_cube(tag)
|
||||||
|
|
||||||
# DataFrame des moyennes (tous modules confondus)
|
# DataFrame des moyennes (tous modules confondus)
|
||||||
|
inscr_mask = self.acro_ues_inscr_parcours.to_numpy()
|
||||||
matrice_moys_ues = self.compute_notes_ues(
|
matrice_moys_ues = self.compute_notes_ues(
|
||||||
notes_cube_gen, masque_cube, inscr_mask
|
notes_cube_gen, masque_cube, inscr_mask
|
||||||
)
|
)
|
||||||
@ -289,7 +294,7 @@ class SxTag(pe_tabletags.TableTag):
|
|||||||
def compute_notes_ues(
|
def compute_notes_ues(
|
||||||
self,
|
self,
|
||||||
set_cube: np.array,
|
set_cube: np.array,
|
||||||
masque_cube: np.array,
|
cap_mask_3D: np.array,
|
||||||
inscr_mask: np.array,
|
inscr_mask: np.array,
|
||||||
) -> pd.DataFrame:
|
) -> pd.DataFrame:
|
||||||
"""Calcule la moyenne par UEs à un tag donné en prenant la note maximum (UE
|
"""Calcule la moyenne par UEs à un tag donné en prenant la note maximum (UE
|
||||||
@ -298,7 +303,8 @@ class SxTag(pe_tabletags.TableTag):
|
|||||||
Args:
|
Args:
|
||||||
set_cube: notes moyennes aux modules ndarray
|
set_cube: notes moyennes aux modules ndarray
|
||||||
(semestre_ids x etudids x UEs), des floats avec des NaN
|
(semestre_ids x etudids x UEs), des floats avec des NaN
|
||||||
masque_cube: masque indiquant si la note doit être prise en compte ndarray
|
cap_mask_3D
|
||||||
|
: masque indiquant si la note doit être prise en compte ndarray
|
||||||
(semestre_ids x etudids x UEs), des 1.0 ou des 0.0
|
(semestre_ids x etudids x UEs), des 1.0 ou des 0.0
|
||||||
inscr_mask: masque etudids x UE traduisant les inscriptions des
|
inscr_mask: masque etudids x UE traduisant les inscriptions des
|
||||||
étudiants aux UE (du semestre terminal)
|
étudiants aux UE (du semestre terminal)
|
||||||
@ -320,10 +326,7 @@ class SxTag(pe_tabletags.TableTag):
|
|||||||
set_cube = set_cube * inscr_mask_3D
|
set_cube = set_cube * inscr_mask_3D
|
||||||
|
|
||||||
# Entrées à garder en fonction des UEs capitalisées ou non
|
# Entrées à garder en fonction des UEs capitalisées ou non
|
||||||
set_cube = set_cube * masque_cube
|
set_cube = set_cube * cap_mask_3D
|
||||||
|
|
||||||
# Quelles entrées du cube contiennent des notes ?
|
|
||||||
mask = ~np.isnan(set_cube)
|
|
||||||
|
|
||||||
# Enlève les NaN du cube pour les entrées manquantes : NaN -> -1.0
|
# Enlève les NaN du cube pour les entrées manquantes : NaN -> -1.0
|
||||||
set_cube_no_nan = np.nan_to_num(set_cube, nan=-1.0)
|
set_cube_no_nan = np.nan_to_num(set_cube, nan=-1.0)
|
||||||
@ -332,9 +335,12 @@ class SxTag(pe_tabletags.TableTag):
|
|||||||
# TODO: Pour l'instant un max sans prise en compte des UE capitalisées
|
# TODO: Pour l'instant un max sans prise en compte des UE capitalisées
|
||||||
etud_moy = np.max(set_cube_no_nan, axis=2)
|
etud_moy = np.max(set_cube_no_nan, axis=2)
|
||||||
|
|
||||||
# Fix les max non calculé -1 -> NaN
|
# Fix les max non calculés (-1) -> NaN
|
||||||
etud_moy[etud_moy < 0] = np.NaN
|
etud_moy[etud_moy < 0] = np.NaN
|
||||||
|
|
||||||
|
# Réapplique le masque d'inscription (dans le doute)
|
||||||
|
etud_moy = etud_moy * inscr_mask
|
||||||
|
|
||||||
# Le dataFrame
|
# Le dataFrame
|
||||||
etud_moy_tag_df = pd.DataFrame(
|
etud_moy_tag_df = pd.DataFrame(
|
||||||
etud_moy,
|
etud_moy,
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
from flask import g
|
from flask import g
|
||||||
from app import log
|
from app import log
|
||||||
from app.pe.rcss import pe_rcs
|
from app.pe.rcss import pe_rcs
|
||||||
|
import app.pe.pe_comp as pe_comp
|
||||||
|
|
||||||
PE_DEBUG = False
|
PE_DEBUG = False
|
||||||
|
|
||||||
@ -135,23 +136,45 @@ def aff_tags_par_categories(dict_tags):
|
|||||||
aff_tags_auto = ", ".join([f"👜{nom}" for nom in noms_tags_auto])
|
aff_tags_auto = ", ".join([f"👜{nom}" for nom in noms_tags_auto])
|
||||||
return f"Tags automatiques {aff_tags_auto} (aucun tag personnalisé)"
|
return f"Tags automatiques {aff_tags_auto} (aucun tag personnalisé)"
|
||||||
|
|
||||||
# Affichage
|
|
||||||
|
def repr_jeune(etudid, etudiants):
|
||||||
|
"""Renvoie la représentation d'un étudiant"""
|
||||||
|
etat = "⛔" if etudid in etudiants.abandons_ids else "✅"
|
||||||
|
jeune = f"{etat} {etudiants.identites[etudid].nomprenom} (#{etudid})"
|
||||||
|
return jeune
|
||||||
|
|
||||||
|
|
||||||
def aff_trajectoires_suivies_par_etudiants(etudiants):
|
def aff_trajectoires_suivies_par_etudiants(etudiants):
|
||||||
"""Affiche les trajectoires (regroupement de (form)semestres)
|
"""Affiche les trajectoires (regroupement de (form)semestres)
|
||||||
amenant un étudiant du S1 à un semestre final"""
|
amenant un étudiant du S1 à un semestre final,
|
||||||
|
en regroupant les étudiants par profil de trajectoires"""
|
||||||
|
|
||||||
# Affichage pour debug
|
# Affichage pour debug
|
||||||
etudiants_ids = etudiants.etudiants_ids
|
etudiants_ids = etudiants.etudiants_ids
|
||||||
jeunes = list(enumerate(etudiants_ids))
|
jeunes = list(enumerate(etudiants_ids))
|
||||||
for no_etud, etudid in jeunes:
|
|
||||||
etat = "⛔" if etudid in etudiants.abandons_ids else "✅"
|
|
||||||
|
|
||||||
pe_print(f"--> {etat} {etudiants.identites[etudid].nomprenom} (#{etudid}) :")
|
profils_traj = {}
|
||||||
|
|
||||||
|
for no_etud, etudid in jeunes:
|
||||||
|
jeune = repr_jeune(etudid, etudiants)
|
||||||
|
|
||||||
|
# La trajectoire du jeune
|
||||||
trajectoires = etudiants.trajectoires[etudid]
|
trajectoires = etudiants.trajectoires[etudid]
|
||||||
|
profil_traj = []
|
||||||
for nom_rcs, rcs in trajectoires.items():
|
for nom_rcs, rcs in trajectoires.items():
|
||||||
if rcs:
|
if rcs:
|
||||||
pe_print(f" > RCS ⏯️{nom_rcs}: {rcs.get_repr()}")
|
profil_traj += [f" > RCS ⏯️{nom_rcs}: {rcs.get_repr()}"]
|
||||||
|
aff_profil_traj = "\n".join(profil_traj)
|
||||||
|
if aff_profil_traj not in profils_traj:
|
||||||
|
profils_traj[aff_profil_traj] = []
|
||||||
|
|
||||||
|
profils_traj[aff_profil_traj] += [jeune]
|
||||||
|
|
||||||
|
# Affichage final
|
||||||
|
for profil, jeunes in profils_traj.items():
|
||||||
|
pe_print(f"--> Trajectoire suivie par : ")
|
||||||
|
pe_print("\n".join([" " + jeune for jeune in jeunes]))
|
||||||
|
pe_print(profil)
|
||||||
|
|
||||||
|
|
||||||
def aff_semXs_suivis_par_etudiants(etudiants):
|
def aff_semXs_suivis_par_etudiants(etudiants):
|
||||||
@ -198,13 +221,11 @@ def aff_capitalisations(etuds, ressembuttags, fid_final, acronymes_sorted, masqu
|
|||||||
|
|
||||||
def repr_comp_et_ues(acronymes_ues_to_competences):
|
def repr_comp_et_ues(acronymes_ues_to_competences):
|
||||||
"""Affichage pour debug"""
|
"""Affichage pour debug"""
|
||||||
|
asso_comp_to_ues = pe_comp.asso_comp_to_accronymes(acronymes_ues_to_competences)
|
||||||
aff_comp = []
|
aff_comp = []
|
||||||
competences_sorted = sorted(acronymes_ues_to_competences.keys())
|
competences_sorted = sorted(asso_comp_to_ues.keys())
|
||||||
for comp in competences_sorted:
|
for comp in competences_sorted:
|
||||||
liste = []
|
liste = ["📍" + accro for accro in asso_comp_to_ues[comp]]
|
||||||
for acro in acronymes_ues_to_competences:
|
|
||||||
if acronymes_ues_to_competences[acro] == comp:
|
|
||||||
liste += ["📍" + acro]
|
|
||||||
aff_comp += [f" 💡{comp} (⇔ {', '.join(liste)})"]
|
aff_comp += [f" 💡{comp} (⇔ {', '.join(liste)})"]
|
||||||
return "\n".join(aff_comp)
|
return "\n".join(aff_comp)
|
||||||
|
|
||||||
|
@ -337,3 +337,21 @@ def get_dernier_semestre_en_date(semestres: dict[int, FormSemestre]) -> FormSeme
|
|||||||
dernier_semestre = semestres[fid]
|
dernier_semestre = semestres[fid]
|
||||||
return dernier_semestre
|
return dernier_semestre
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def asso_comp_to_accronymes(accro_ues_to_competences):
|
||||||
|
"""Partant d'un dictionnaire ``{nom_ue: compétence}`` associant des
|
||||||
|
accronymes d'UEs à des compétences, renvoie l'association d'une compétence
|
||||||
|
à ou aux UEs qui l'adresse : ``{competence: [liste_nom_ue]}``
|
||||||
|
|
||||||
|
Args:
|
||||||
|
accro_ues_to_competences: Dictionnaire ``{nom_ue: compétence}``
|
||||||
|
Return:
|
||||||
|
Le dictionnaire ``{competence: [liste_nom_ue]}``
|
||||||
|
"""
|
||||||
|
asso = {}
|
||||||
|
for accro, comp in accro_ues_to_competences.items():
|
||||||
|
if comp not in asso:
|
||||||
|
asso[comp] = []
|
||||||
|
asso[comp].append(accro)
|
||||||
|
return asso
|
||||||
|
@ -705,7 +705,7 @@ class JuryPE(object):
|
|||||||
tag, aggregat=aggregat, type_colonnes=False, options=self.options
|
tag, aggregat=aggregat, type_colonnes=False, options=self.options
|
||||||
)
|
)
|
||||||
if not df_groupe.empty:
|
if not df_groupe.empty:
|
||||||
aff_aggregat += [aggregat]
|
aff_aggregat += [aggregat + " (Groupe)"]
|
||||||
df = df.join(df_groupe)
|
df = df.join(df_groupe)
|
||||||
|
|
||||||
# Le dataframe du classement sur la promo
|
# Le dataframe du classement sur la promo
|
||||||
@ -718,7 +718,7 @@ class JuryPE(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not df_promo.empty:
|
if not df_promo.empty:
|
||||||
aff_aggregat += [aggregat]
|
aff_aggregat += [aggregat + " (Promo)"]
|
||||||
df = df.join(df_promo)
|
df = df.join(df_promo)
|
||||||
|
|
||||||
if aff_aggregat:
|
if aff_aggregat:
|
||||||
|
@ -15,7 +15,7 @@ from app.pe.rcss import pe_rcs, pe_trajectoires
|
|||||||
|
|
||||||
|
|
||||||
class RCSemX(pe_rcs.RCS):
|
class RCSemX(pe_rcs.RCS):
|
||||||
"""Modélise un regroupement cohérent de SemX (en même regroupant
|
"""Modélise un regroupement cohérent de SemX (en regroupant
|
||||||
des semestres Sx combinés pour former les résultats des étudiants
|
des semestres Sx combinés pour former les résultats des étudiants
|
||||||
au semestre de rang x) dans le but de synthétiser les résultats
|
au semestre de rang x) dans le but de synthétiser les résultats
|
||||||
du S1 jusqu'au semestre final ciblé par le RCSemX (dépendant de l'aggrégat
|
du S1 jusqu'au semestre final ciblé par le RCSemX (dépendant de l'aggrégat
|
||||||
|
@ -175,10 +175,9 @@ def sidebar(etudid: int = None):
|
|||||||
inscription = etud.inscription_courante()
|
inscription = etud.inscription_courante()
|
||||||
if inscription:
|
if inscription:
|
||||||
formsemestre = inscription.formsemestre
|
formsemestre = inscription.formsemestre
|
||||||
nbabs, nbabsjust = sco_assiduites.formsemestre_get_assiduites_count(
|
nbabsnj, nbabsjust, _ = sco_assiduites.formsemestre_get_assiduites_count(
|
||||||
etudid, formsemestre
|
etudid, formsemestre
|
||||||
)
|
)
|
||||||
nbabsnj = nbabs - nbabsjust
|
|
||||||
H.append(
|
H.append(
|
||||||
f"""<span title="absences du {
|
f"""<span title="absences du {
|
||||||
formsemestre.date_debut.strftime("%d/%m/%Y")
|
formsemestre.date_debut.strftime("%d/%m/%Y")
|
||||||
@ -186,7 +185,7 @@ def sidebar(etudid: int = None):
|
|||||||
formsemestre.date_fin.strftime("%d/%m/%Y")
|
formsemestre.date_fin.strftime("%d/%m/%Y")
|
||||||
}">({
|
}">({
|
||||||
sco_preferences.get_preference("assi_metrique", None)})
|
sco_preferences.get_preference("assi_metrique", None)})
|
||||||
<br>{nbabsjust:1.0f} J., {nbabsnj:1.0f} N.J.</span>"""
|
<br>{nbabsjust:1g} J., {nbabsnj:1g} N.J.</span>"""
|
||||||
)
|
)
|
||||||
H.append("<ul>")
|
H.append("<ul>")
|
||||||
if current_user.has_permission(Permission.AbsChange):
|
if current_user.has_permission(Permission.AbsChange):
|
||||||
|
@ -67,7 +67,7 @@ def abs_notify(etudid: int, date: str | datetime.datetime):
|
|||||||
if not formsemestre:
|
if not formsemestre:
|
||||||
return # non inscrit a la date, pas de notification
|
return # non inscrit a la date, pas de notification
|
||||||
|
|
||||||
nbabs, nbabsjust = sco_assiduites.get_assiduites_count_in_interval(
|
_, nbabsjust, nbabs = sco_assiduites.get_assiduites_count_in_interval(
|
||||||
etudid,
|
etudid,
|
||||||
metrique=scu.translate_assiduites_metric(
|
metrique=scu.translate_assiduites_metric(
|
||||||
sco_preferences.get_preference(
|
sco_preferences.get_preference(
|
||||||
|
@ -671,7 +671,7 @@ def create_absence_billet(
|
|||||||
# Gestion du cache
|
# Gestion du cache
|
||||||
def get_assiduites_count(etudid: int, sem: dict) -> tuple[int, int]:
|
def get_assiduites_count(etudid: int, sem: dict) -> tuple[int, int]:
|
||||||
"""Les comptes d'absences de cet étudiant dans ce semestre:
|
"""Les comptes d'absences de cet étudiant dans ce semestre:
|
||||||
tuple (nb abs non justifiées, nb abs justifiées)
|
tuple (nb abs non justifiées, nb abs justifiées, nb abs total)
|
||||||
Utilise un cache.
|
Utilise un cache.
|
||||||
"""
|
"""
|
||||||
metrique = sco_preferences.get_preference("assi_metrique", sem["formsemestre_id"])
|
metrique = sco_preferences.get_preference("assi_metrique", sem["formsemestre_id"])
|
||||||
@ -687,17 +687,17 @@ def formsemestre_get_assiduites_count(
|
|||||||
etudid: int, formsemestre: FormSemestre, moduleimpl_id: int = None
|
etudid: int, formsemestre: FormSemestre, moduleimpl_id: int = None
|
||||||
) -> tuple[int, int]:
|
) -> tuple[int, int]:
|
||||||
"""Les comptes d'absences de cet étudiant dans ce semestre:
|
"""Les comptes d'absences de cet étudiant dans ce semestre:
|
||||||
tuple (nb abs non justifiées, nb abs justifiées)
|
tuple (nb abs non justifiées, nb abs justifiées, nb abs total)
|
||||||
Utilise un cache.
|
Utilise un cache.
|
||||||
"""
|
"""
|
||||||
metrique = sco_preferences.get_preference("assi_metrique", formsemestre.id)
|
metrique = sco_preferences.get_preference("assi_metrique", formsemestre.id)
|
||||||
return get_assiduites_count_in_interval(
|
return get_assiduites_count_in_interval(
|
||||||
etudid,
|
etudid,
|
||||||
date_debut=scu.localize_datetime(
|
date_debut=scu.localize_datetime(
|
||||||
datetime.combine(formsemestre.date_debut, time(8, 0))
|
datetime.combine(formsemestre.date_debut, time(0, 0))
|
||||||
),
|
),
|
||||||
date_fin=scu.localize_datetime(
|
date_fin=scu.localize_datetime(
|
||||||
datetime.combine(formsemestre.date_fin, time(18, 0))
|
datetime.combine(formsemestre.date_fin, time(23, 0))
|
||||||
),
|
),
|
||||||
metrique=scu.translate_assiduites_metric(metrique),
|
metrique=scu.translate_assiduites_metric(metrique),
|
||||||
moduleimpl_id=moduleimpl_id,
|
moduleimpl_id=moduleimpl_id,
|
||||||
@ -714,12 +714,12 @@ def get_assiduites_count_in_interval(
|
|||||||
moduleimpl_id: int = None,
|
moduleimpl_id: int = None,
|
||||||
):
|
):
|
||||||
"""Les comptes d'absences de cet étudiant entre ces deux dates, incluses:
|
"""Les comptes d'absences de cet étudiant entre ces deux dates, incluses:
|
||||||
tuple (nb abs, nb abs justifiées)
|
tuple (nb abs non justifiées, nb abs justifiées, nb abs total)
|
||||||
On peut spécifier les dates comme datetime ou iso.
|
On peut spécifier les dates comme datetime ou iso.
|
||||||
Utilise un cache.
|
Utilise un cache.
|
||||||
"""
|
"""
|
||||||
date_debut_iso = date_debut_iso or date_debut.isoformat()
|
date_debut_iso = date_debut_iso or date_debut.strftime("%Y-%m-%d")
|
||||||
date_fin_iso = date_fin_iso or date_fin.isoformat()
|
date_fin_iso = date_fin_iso or date_fin.strftime("%Y-%m-%d")
|
||||||
key = f"{etudid}_{date_debut_iso}_{date_fin_iso}_assiduites"
|
key = f"{etudid}_{date_debut_iso}_{date_fin_iso}_assiduites"
|
||||||
|
|
||||||
r = sco_cache.AbsSemEtudCache.get(key)
|
r = sco_cache.AbsSemEtudCache.get(key)
|
||||||
@ -744,9 +744,10 @@ def get_assiduites_count_in_interval(
|
|||||||
if not ans:
|
if not ans:
|
||||||
log("warning: get_assiduites_count failed to cache")
|
log("warning: get_assiduites_count failed to cache")
|
||||||
|
|
||||||
nb_abs: dict = r["absent"][metrique]
|
nb_abs: int = r["absent"][metrique]
|
||||||
nb_abs_just: dict = r["absent_just"][metrique]
|
nb_abs_nj: int = r["absent_non_just"][metrique]
|
||||||
return (nb_abs, nb_abs_just)
|
nb_abs_just: int = r["absent_just"][metrique]
|
||||||
|
return (nb_abs_nj, nb_abs_just, nb_abs)
|
||||||
|
|
||||||
|
|
||||||
def invalidate_assiduites_count(etudid: int, sem: dict):
|
def invalidate_assiduites_count(etudid: int, sem: dict):
|
||||||
|
@ -196,7 +196,7 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
|||||||
pid = partition["partition_id"]
|
pid = partition["partition_id"]
|
||||||
partitions_etud_groups[pid] = sco_groups.get_etud_groups_in_partition(pid)
|
partitions_etud_groups[pid] = sco_groups.get_etud_groups_in_partition(pid)
|
||||||
# --- Absences
|
# --- Absences
|
||||||
I["nbabs"], I["nbabsjust"] = sco_assiduites.get_assiduites_count(etudid, nt.sem)
|
_, I["nbabsjust"], I["nbabs"] = sco_assiduites.get_assiduites_count(etudid, nt.sem)
|
||||||
|
|
||||||
# --- Decision Jury
|
# --- Decision Jury
|
||||||
infos, dpv = etud_descr_situation_semestre(
|
infos, dpv = etud_descr_situation_semestre(
|
||||||
@ -471,7 +471,7 @@ def _ue_mod_bulletin(
|
|||||||
) # peut etre 'NI'
|
) # peut etre 'NI'
|
||||||
is_malus = mod["module"]["module_type"] == ModuleType.MALUS
|
is_malus = mod["module"]["module_type"] == ModuleType.MALUS
|
||||||
if bul_show_abs_modules:
|
if bul_show_abs_modules:
|
||||||
nbabs, nbabsjust = sco_assiduites.get_assiduites_count(etudid, sem)
|
_, nbabsjust, nbabs = sco_assiduites.get_assiduites_count(etudid, sem)
|
||||||
mod_abs = [nbabs, nbabsjust]
|
mod_abs = [nbabs, nbabsjust]
|
||||||
mod["mod_abs_txt"] = scu.fmt_abs(mod_abs)
|
mod["mod_abs_txt"] = scu.fmt_abs(mod_abs)
|
||||||
else:
|
else:
|
||||||
|
@ -296,7 +296,7 @@ def formsemestre_bulletinetud_published_dict(
|
|||||||
|
|
||||||
# --- Absences
|
# --- Absences
|
||||||
if prefs["bul_show_abs"]:
|
if prefs["bul_show_abs"]:
|
||||||
nbabs, nbabsjust = sco_assiduites.get_assiduites_count(etudid, sem)
|
_, nbabsjust, nbabs = sco_assiduites.get_assiduites_count(etudid, sem)
|
||||||
d["absences"] = dict(nbabs=nbabs, nbabsjust=nbabsjust)
|
d["absences"] = dict(nbabs=nbabs, nbabsjust=nbabsjust)
|
||||||
|
|
||||||
# --- Décision Jury
|
# --- Décision Jury
|
||||||
|
@ -260,7 +260,7 @@ def make_xml_formsemestre_bulletinetud(
|
|||||||
numero=str(mod["numero"]),
|
numero=str(mod["numero"]),
|
||||||
titre=quote_xml_attr(mod["titre"]),
|
titre=quote_xml_attr(mod["titre"]),
|
||||||
abbrev=quote_xml_attr(mod["abbrev"]),
|
abbrev=quote_xml_attr(mod["abbrev"]),
|
||||||
code_apogee=quote_xml_attr(mod["code_apogee"])
|
code_apogee=quote_xml_attr(mod["code_apogee"]),
|
||||||
# ects=ects ects des modules maintenant inutilisés
|
# ects=ects ects des modules maintenant inutilisés
|
||||||
)
|
)
|
||||||
x_ue.append(x_mod)
|
x_ue.append(x_mod)
|
||||||
@ -347,7 +347,7 @@ def make_xml_formsemestre_bulletinetud(
|
|||||||
|
|
||||||
# --- Absences
|
# --- Absences
|
||||||
if sco_preferences.get_preference("bul_show_abs", formsemestre_id):
|
if sco_preferences.get_preference("bul_show_abs", formsemestre_id):
|
||||||
nbabs, nbabsjust = sco_assiduites.get_assiduites_count(etudid, sem)
|
_, nbabsjust, nbabs = sco_assiduites.get_assiduites_count(etudid, sem)
|
||||||
doc.append(Element("absences", nbabs=str(nbabs), nbabsjust=str(nbabsjust)))
|
doc.append(Element("absences", nbabs=str(nbabs), nbabsjust=str(nbabsjust)))
|
||||||
# --- Decision Jury
|
# --- Decision Jury
|
||||||
if (
|
if (
|
||||||
|
@ -722,8 +722,8 @@ def formsemestre_recap_parcours_table(
|
|||||||
f"""<td class="rcp_moy">{scu.fmt_note(nt.get_etud_moy_gen(etudid))}</td>"""
|
f"""<td class="rcp_moy">{scu.fmt_note(nt.get_etud_moy_gen(etudid))}</td>"""
|
||||||
)
|
)
|
||||||
# Absences (nb d'abs non just. dans ce semestre)
|
# Absences (nb d'abs non just. dans ce semestre)
|
||||||
nbabs, nbabsjust = sco_assiduites.get_assiduites_count(etudid, sem)
|
nbabsnj = sco_assiduites.get_assiduites_count(etudid, sem)[0]
|
||||||
H.append(f"""<td class="rcp_abs">{nbabs - nbabsjust}</td>""")
|
H.append(f"""<td class="rcp_abs">{nbabsnj}</td>""")
|
||||||
|
|
||||||
# UEs
|
# UEs
|
||||||
for ue in ues:
|
for ue in ues:
|
||||||
|
@ -105,7 +105,9 @@ def etud_get_poursuite_info(sem: dict, etud: dict) -> dict:
|
|||||||
rangs.append(["rang_" + code_module, rang_module])
|
rangs.append(["rang_" + code_module, rang_module])
|
||||||
|
|
||||||
# Absences
|
# Absences
|
||||||
nbabs, nbabsjust = sco_assiduites.get_assiduites_count(etudid, nt.sem)
|
nbabsnj, nbabsjust, _ = sco_assiduites.get_assiduites_count(
|
||||||
|
etudid, nt.sem
|
||||||
|
)
|
||||||
# En BUT, prend tout, sinon ne prend que les semestre validés par le jury
|
# En BUT, prend tout, sinon ne prend que les semestre validés par le jury
|
||||||
if nt.is_apc or (
|
if nt.is_apc or (
|
||||||
dec
|
dec
|
||||||
@ -125,7 +127,7 @@ def etud_get_poursuite_info(sem: dict, etud: dict) -> dict:
|
|||||||
("date_debut", s["date_debut"]),
|
("date_debut", s["date_debut"]),
|
||||||
("date_fin", s["date_fin"]),
|
("date_fin", s["date_fin"]),
|
||||||
("periode", "%s - %s" % (s["mois_debut"], s["mois_fin"])),
|
("periode", "%s - %s" % (s["mois_debut"], s["mois_fin"])),
|
||||||
("AbsNonJust", nbabs - nbabsjust),
|
("AbsNonJust", nbabsnj),
|
||||||
("AbsJust", nbabsjust),
|
("AbsJust", nbabsjust),
|
||||||
]
|
]
|
||||||
# ajout des 2 champs notes des modules et classement dans chaque module
|
# ajout des 2 champs notes des modules et classement dans chaque module
|
||||||
|
@ -620,7 +620,7 @@ class RowRecap(tb.Row):
|
|||||||
def add_abs(self):
|
def add_abs(self):
|
||||||
"Ajoute les colonnes absences"
|
"Ajoute les colonnes absences"
|
||||||
# Absences (nb d'abs non just. dans ce semestre)
|
# Absences (nb d'abs non just. dans ce semestre)
|
||||||
nbabs, nbabsjust = self.table.res.formsemestre.get_abs_count(self.etud.id)
|
_, nbabsjust, nbabs = self.table.res.formsemestre.get_abs_count(self.etud.id)
|
||||||
self.add_cell("nbabs", "Abs", f"{nbabs:1.0f}", "abs", raw_content=nbabs)
|
self.add_cell("nbabs", "Abs", f"{nbabs:1.0f}", "abs", raw_content=nbabs)
|
||||||
self.add_cell(
|
self.add_cell(
|
||||||
"nbabsjust", "Just.", f"{nbabsjust:1.0f}", "abs", raw_content=nbabsjust
|
"nbabsjust", "Just.", f"{nbabsjust:1.0f}", "abs", raw_content=nbabsjust
|
||||||
|
@ -154,50 +154,25 @@ div.submit > input {
|
|||||||
{% include "sco_timepicker.j2" %}
|
{% include "sco_timepicker.j2" %}
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
// Suppression d'un fichier justificatif
|
|
||||||
function delete_file(justif_id, fileName, liElement) {
|
|
||||||
// Construct the URL
|
|
||||||
var url = "{{url_for('apiweb.justif_remove', justif_id=-1, scodoc_dept=g.scodoc_dept)}}".replace('-1', justif_id);
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
"remove": "list",
|
|
||||||
"filenames" : [ fileName ],
|
|
||||||
}
|
|
||||||
// Send API request
|
|
||||||
fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(payload)
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
if (response.ok) {
|
|
||||||
// Hide the <li> element on successful deletion
|
|
||||||
liElement.style.display = 'none';
|
|
||||||
sco_message("fichier supprimé");
|
|
||||||
} else {
|
|
||||||
// Handle non-successful responses here
|
|
||||||
console.error('Deletion failed:', response.statusText);
|
|
||||||
sco_error_message("erreur lors de la suppression du fichier");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error:', error);
|
|
||||||
sco_error_message("erreur lors de la suppression du fichier (2)");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add event listeners to all elements with class 'suppr_fichier_just'
|
// Add event listeners to all elements with class 'suppr_fichier_just'
|
||||||
var deleteButtons = document.querySelectorAll('.suppr_fichier_just');
|
var deleteButtons = document.querySelectorAll('.suppr_fichier_just');
|
||||||
|
const form = document.getElementById('ajout-justificatif-etud');
|
||||||
deleteButtons.forEach(function(button) {
|
deleteButtons.forEach(function(button) {
|
||||||
button.addEventListener('click', function() {
|
button.addEventListener('click', function() {
|
||||||
// Get the text content of the next sibling node
|
// Get the text content of the next sibling node
|
||||||
var justif_id = this.dataset.justif_id;
|
var justif_id = this.dataset.justif_id;
|
||||||
var fileName = this.nextSibling.nodeValue.trim();
|
var fileName = this.nextSibling.nodeValue.trim();
|
||||||
var liElement = this.parentNode; // Get the parent <li> element
|
var liElement = this.parentNode; // Get the parent <li> element
|
||||||
delete_file(justif_id, fileName, liElement);
|
|
||||||
|
// Create a hidden input element to store the file name
|
||||||
|
var input = document.createElement('input');
|
||||||
|
input.type = 'hidden';
|
||||||
|
input.name = 'suppr_fichier_just';
|
||||||
|
input.value = fileName;
|
||||||
|
form.appendChild(input);
|
||||||
|
|
||||||
|
liElement.remove();
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -174,7 +174,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('load', ()=>{
|
window.addEventListener('load', ()=>{
|
||||||
const table_columns = [...document.querySelectorAll('.external-sort')];
|
const table_columns = [...document.querySelectorAll('th.external-sort')];
|
||||||
table_columns.forEach((e)=>e.addEventListener('click', ()=>{
|
table_columns.forEach((e)=>e.addEventListener('click', ()=>{
|
||||||
|
|
||||||
// récupération de l'ordre "ascending" / "descending"
|
// récupération de l'ordre "ascending" / "descending"
|
||||||
|
@ -56,9 +56,9 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<b>Absences</b>
|
<b>Absences</b>
|
||||||
{% if sco.etud_cur_sem %}
|
{% if sco.etud_cur_sem %}
|
||||||
<span title="absences du {{ sco.etud_cur_sem['date_debut'] }}
|
<span title="absences du {{ sco.etud_cur_sem['date_debut'].strftime('%d/%m/%Y') }}
|
||||||
au {{ sco.etud_cur_sem['date_fin'] }}">({{sco.prefs["assi_metrique"]}})
|
au {{ sco.etud_cur_sem['date_fin'].strftime('%d/%m/%Y') }}">({{sco.prefs["assi_metrique"]}})
|
||||||
<br />{{'%1.0f'|format(sco.nbabsjust)}} J., {{'%1.0f'|format(sco.nbabsnj)}} N.J.</span>
|
<br />{{'%1g'|format(sco.nbabsjust)}} J., {{'%1g'|format(sco.nbabsnj)}} N.J.</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<ul>
|
<ul>
|
||||||
{% if current_user.has_permission(sco.Permission.AbsChange) %}
|
{% if current_user.has_permission(sco.Permission.AbsChange) %}
|
||||||
|
@ -74,8 +74,9 @@ class ScoData:
|
|||||||
if ins:
|
if ins:
|
||||||
self.etud_cur_sem = ins.formsemestre
|
self.etud_cur_sem = ins.formsemestre
|
||||||
(
|
(
|
||||||
self.nbabs,
|
self.nbabsnj,
|
||||||
self.nbabsjust,
|
self.nbabsjust,
|
||||||
|
self.nbabs,
|
||||||
) = sco_assiduites.get_assiduites_count_in_interval(
|
) = sco_assiduites.get_assiduites_count_in_interval(
|
||||||
etud.id,
|
etud.id,
|
||||||
self.etud_cur_sem.date_debut.isoformat(),
|
self.etud_cur_sem.date_debut.isoformat(),
|
||||||
@ -84,7 +85,6 @@ class ScoData:
|
|||||||
sco_preferences.get_preference("assi_metrique")
|
sco_preferences.get_preference("assi_metrique")
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.nbabsnj = self.nbabs - self.nbabsjust
|
|
||||||
else:
|
else:
|
||||||
self.etud_cur_sem = None
|
self.etud_cur_sem = None
|
||||||
else:
|
else:
|
||||||
|
@ -658,6 +658,8 @@ def edit_justificatif_etud(justif_id: int):
|
|||||||
etudid=justif.etudiant.id,
|
etudid=justif.etudiant.id,
|
||||||
)
|
)
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
|
if form.cancel.data: # cancel button
|
||||||
|
return redirect(redirect_url)
|
||||||
if _record_justificatif_etud(justif.etudiant, form, justif):
|
if _record_justificatif_etud(justif.etudiant, form, justif):
|
||||||
return redirect(redirect_url)
|
return redirect(redirect_url)
|
||||||
|
|
||||||
@ -757,7 +759,6 @@ def _record_justificatif_etud(
|
|||||||
dt_fin_tz_server,
|
dt_fin_tz_server,
|
||||||
dt_entry_date_tz_server,
|
dt_entry_date_tz_server,
|
||||||
) = _get_dates_from_assi_form(form, all_day=True)
|
) = _get_dates_from_assi_form(form, all_day=True)
|
||||||
|
|
||||||
if not ok:
|
if not ok:
|
||||||
log("_record_justificatif_etud: dates invalides")
|
log("_record_justificatif_etud: dates invalides")
|
||||||
form.set_error("Erreur: dates invalides")
|
form.set_error("Erreur: dates invalides")
|
||||||
@ -793,6 +794,14 @@ def _record_justificatif_etud(
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
message = "Pas de modification"
|
message = "Pas de modification"
|
||||||
|
fichier_suppr: list[str] = request.form.getlist("suppr_fichier_just")
|
||||||
|
|
||||||
|
if len(fichier_suppr) > 0 and justif.fichier is not None:
|
||||||
|
archiver: JustificatifArchiver = JustificatifArchiver()
|
||||||
|
for fichier in fichier_suppr:
|
||||||
|
archiver.delete_justificatif(etud, justif.fichier, fichier)
|
||||||
|
flash(f"Fichier {fichier} supprimé")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
justif = Justificatif.create_justificatif(
|
justif = Justificatif.create_justificatif(
|
||||||
etud,
|
etud,
|
||||||
|
@ -1187,14 +1187,18 @@ def view_module_abs(moduleimpl_id, fmt="html"):
|
|||||||
|
|
||||||
rows = []
|
rows = []
|
||||||
for etud in inscrits:
|
for etud in inscrits:
|
||||||
nb_abs, nb_abs_just = sco_assiduites.formsemestre_get_assiduites_count(
|
(
|
||||||
|
nb_abs_nj,
|
||||||
|
nb_abs_just,
|
||||||
|
nb_abs,
|
||||||
|
) = sco_assiduites.formsemestre_get_assiduites_count(
|
||||||
etud.id, modimpl.formsemestre, moduleimpl_id=modimpl.id
|
etud.id, modimpl.formsemestre, moduleimpl_id=modimpl.id
|
||||||
)
|
)
|
||||||
rows.append(
|
rows.append(
|
||||||
{
|
{
|
||||||
"nomprenom": etud.nomprenom,
|
"nomprenom": etud.nomprenom,
|
||||||
"just": nb_abs_just,
|
"just": nb_abs_just,
|
||||||
"nojust": nb_abs - nb_abs_just,
|
"nojust": nb_abs_nj,
|
||||||
"total": nb_abs,
|
"total": nb_abs,
|
||||||
"_nomprenom_target": url_for(
|
"_nomprenom_target": url_for(
|
||||||
"scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etud.id
|
"scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etud.id
|
||||||
|
@ -72,8 +72,6 @@ def test_general(test_client):
|
|||||||
verifier_comptage_et_filtrage_assiduites(etuds, moduleimpls[:4], formsemestres)
|
verifier_comptage_et_filtrage_assiduites(etuds, moduleimpls[:4], formsemestres)
|
||||||
verifier_filtrage_justificatifs(etuds[0], justificatifs)
|
verifier_filtrage_justificatifs(etuds[0], justificatifs)
|
||||||
|
|
||||||
essais_cache(etuds[0].etudid, formsemestres[:2], moduleimpls)
|
|
||||||
|
|
||||||
editer_supprimer_assiduites(etuds, moduleimpls)
|
editer_supprimer_assiduites(etuds, moduleimpls)
|
||||||
editer_supprimer_justificatif(etuds[0])
|
editer_supprimer_justificatif(etuds[0])
|
||||||
|
|
||||||
@ -402,54 +400,6 @@ def _get_justi(
|
|||||||
).first()
|
).first()
|
||||||
|
|
||||||
|
|
||||||
def essais_cache(etudid, sems: tuple[FormSemestre], moduleimpls: list[ModuleImpl]):
|
|
||||||
"""Vérification des fonctionnalités du cache"""
|
|
||||||
# TODO faire un test séparé du test_general
|
|
||||||
# voir test_calcul_assiduites pour faire
|
|
||||||
|
|
||||||
date_deb: str = "2022-09-01T07:00"
|
|
||||||
date_fin: str = "2023-01-31T19:00"
|
|
||||||
|
|
||||||
assiduites_count_no_cache = scass.get_assiduites_count_in_interval(
|
|
||||||
etudid, date_deb, date_fin
|
|
||||||
)
|
|
||||||
assiduites_count_cache = scass.get_assiduites_count_in_interval(
|
|
||||||
etudid, date_deb, date_fin
|
|
||||||
)
|
|
||||||
|
|
||||||
assert (
|
|
||||||
assiduites_count_cache == assiduites_count_no_cache == (2, 1)
|
|
||||||
), "Erreur cache classique"
|
|
||||||
|
|
||||||
assert scass.formsemestre_get_assiduites_count(etudid, sems[0]) == (
|
|
||||||
2,
|
|
||||||
1,
|
|
||||||
), "Erreur formsemestre_get_assiduites_count (sans module) A"
|
|
||||||
assert scass.formsemestre_get_assiduites_count(etudid, sems[1]) == (
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
), "Erreur formsemestre_get_assiduites_count (sans module) B"
|
|
||||||
|
|
||||||
assert scass.formsemestre_get_assiduites_count(
|
|
||||||
etudid, sems[0], moduleimpl_id=moduleimpls[0].id
|
|
||||||
) == (
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
), "Erreur formsemestre_get_assiduites_count (avec module) A"
|
|
||||||
assert scass.formsemestre_get_assiduites_count(
|
|
||||||
etudid, sems[0], moduleimpl_id=moduleimpls[1].id
|
|
||||||
) == (
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
), "Erreur formsemestre_get_assiduites_count (avec module) A"
|
|
||||||
assert scass.formsemestre_get_assiduites_count(
|
|
||||||
etudid, sems[0], moduleimpl_id=moduleimpls[2].id
|
|
||||||
) == (
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
), "Erreur formsemestre_get_assiduites_count (avec module) A"
|
|
||||||
|
|
||||||
|
|
||||||
def ajouter_justificatifs(etud):
|
def ajouter_justificatifs(etud):
|
||||||
"""test de l'ajout des justificatifs"""
|
"""test de l'ajout des justificatifs"""
|
||||||
|
|
||||||
@ -1414,6 +1364,7 @@ def test_cas_justificatifs(test_client):
|
|||||||
Tests de certains cas particuliers des justificatifs
|
Tests de certains cas particuliers des justificatifs
|
||||||
- Création du justificatif avant ou après assiduité
|
- Création du justificatif avant ou après assiduité
|
||||||
- Assiduité complétement couverte ou non
|
- Assiduité complétement couverte ou non
|
||||||
|
- Modification de la couverture (edition du justificatif)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data = _setup_fake_db(
|
data = _setup_fake_db(
|
||||||
@ -1503,3 +1454,279 @@ def test_cas_justificatifs(test_client):
|
|||||||
assert (
|
assert (
|
||||||
len(scass.justifies(justif_4)) == 0
|
len(scass.justifies(justif_4)) == 0
|
||||||
), "Justification complète non prise en compte (c2)"
|
), "Justification complète non prise en compte (c2)"
|
||||||
|
|
||||||
|
# <- Vérification modification de la couverture ->
|
||||||
|
|
||||||
|
# Deux assiduités, 8/01/2024 de 8h à 10h et 14h à 16h
|
||||||
|
|
||||||
|
assi_2: Assiduite = Assiduite.create_assiduite(
|
||||||
|
etud=etud_1,
|
||||||
|
date_debut=scu.is_iso_formated("2024-01-08T08:00", True),
|
||||||
|
date_fin=scu.is_iso_formated("2024-01-08T10:00", True),
|
||||||
|
etat=scu.EtatAssiduite.ABSENT,
|
||||||
|
)
|
||||||
|
assi_3: Assiduite = Assiduite.create_assiduite(
|
||||||
|
etud=etud_1,
|
||||||
|
date_debut=scu.is_iso_formated("2024-01-08T14:00", True),
|
||||||
|
date_fin=scu.is_iso_formated("2024-01-08T16:00", True),
|
||||||
|
etat=scu.EtatAssiduite.ABSENT,
|
||||||
|
)
|
||||||
|
|
||||||
|
# <=>Justification complète<=>
|
||||||
|
# les deux assiduités sont couvertes
|
||||||
|
|
||||||
|
justif_5: Justificatif = Justificatif.create_justificatif(
|
||||||
|
etudiant=etud_1,
|
||||||
|
date_debut=scu.is_iso_formated("2024-01-08T00:00:00", True),
|
||||||
|
date_fin=scu.is_iso_formated("2024-01-08T23:59:59", True),
|
||||||
|
etat=scu.EtatJustificatif.VALIDE,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Justification des assiduités
|
||||||
|
assi_ids: list[int] = justif_5.justifier_assiduites()
|
||||||
|
assert len(assi_ids) == 2, "Vérification Modification couverture (d1)"
|
||||||
|
assert assi_2.assiduite_id in assi_ids, "Vérification Modification couverture (d2)"
|
||||||
|
assert assi_3.assiduite_id in assi_ids, "Vérification Modification couverture (d3)"
|
||||||
|
assert assi_2.est_just is True, "Vérification Modification couverture (d4)"
|
||||||
|
assert assi_3.est_just is True, "Vérification Modification couverture (d5)"
|
||||||
|
|
||||||
|
# Déjustification des assiduités
|
||||||
|
justif_5.dejustifier_assiduites()
|
||||||
|
assi_ids: list[int] = justif_5.dejustifier_assiduites()
|
||||||
|
assert len(assi_ids) == 2, "Vérification Modification couverture (d6)"
|
||||||
|
assert assi_2.assiduite_id in assi_ids, "Vérification Modification couverture (d7)"
|
||||||
|
assert assi_3.assiduite_id in assi_ids, "Vérification Modification couverture (d8)"
|
||||||
|
assert assi_2.est_just is False, "Vérification Modification couverture (d9)"
|
||||||
|
assert assi_3.est_just is False, "Vérification Modification couverture (d10)"
|
||||||
|
|
||||||
|
# <=>Justification Partielle<=>
|
||||||
|
# Seule la première assiduité est couverte
|
||||||
|
|
||||||
|
justif_5.date_fin = scu.is_iso_formated("2024-01-08T11:00", True)
|
||||||
|
db.session.add(justif_5)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
assi_ids: list[int] = justif_5.justifier_assiduites()
|
||||||
|
assert len(assi_ids) == 1, "Vérification Modification couverture (e1)"
|
||||||
|
assert assi_2.assiduite_id in assi_ids, "Vérification Modification couverture (e2)"
|
||||||
|
assert (
|
||||||
|
assi_3.assiduite_id not in assi_ids
|
||||||
|
), "Vérification Modification couverture (e3)"
|
||||||
|
assert assi_2.est_just is True, "Vérification Modification couverture (e4)"
|
||||||
|
assert assi_3.est_just is False, "Vérification Modification couverture (e5)"
|
||||||
|
|
||||||
|
assi_ids: list[int] = justif_5.dejustifier_assiduites()
|
||||||
|
assert len(assi_ids) == 1, "Vérification Modification couverture (e6)"
|
||||||
|
assert assi_2.assiduite_id in assi_ids, "Vérification Modification couverture (e7)"
|
||||||
|
assert (
|
||||||
|
assi_3.assiduite_id not in assi_ids
|
||||||
|
), "Vérification Modification couverture (e3)"
|
||||||
|
assert assi_2.est_just is False, "Vérification Modification couverture (e8)"
|
||||||
|
assert assi_3.est_just is False, "Vérification Modification couverture (e9)"
|
||||||
|
|
||||||
|
# <=>Justification Multiple<=>
|
||||||
|
# Deux justificatifs couvrent une même assiduité
|
||||||
|
|
||||||
|
# on justifie la première assiduité avec le premier justificatif
|
||||||
|
justif_5.justifier_assiduites()
|
||||||
|
|
||||||
|
# deuxième justificatif
|
||||||
|
justif_6: Justificatif = Justificatif.create_justificatif(
|
||||||
|
etudiant=etud_1,
|
||||||
|
date_debut=scu.is_iso_formated("2024-01-08T08:00", True),
|
||||||
|
date_fin=scu.is_iso_formated("2024-01-08T10:00", True),
|
||||||
|
etat=scu.EtatJustificatif.VALIDE,
|
||||||
|
)
|
||||||
|
|
||||||
|
assi_ids: list[int] = justif_6.justifier_assiduites()
|
||||||
|
assert len(assi_ids) == 1, "Vérification Modification couverture (f1)"
|
||||||
|
assert assi_2.assiduite_id in assi_ids, "Vérification Modification couverture (f2)"
|
||||||
|
assert (
|
||||||
|
assi_3.assiduite_id not in assi_ids
|
||||||
|
), "Vérification Modification couverture (f3)"
|
||||||
|
assert assi_2.est_just is True, "Vérification Modification couverture (f4)"
|
||||||
|
assert assi_3.est_just is False, "Vérification Modification couverture (f5)"
|
||||||
|
|
||||||
|
# on déjustifie le justificatif 5
|
||||||
|
justif_5.etat = scu.EtatJustificatif.NON_VALIDE
|
||||||
|
db.session.add(justif_5)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
assi_ids: list[int] = justif_5.dejustifier_assiduites()
|
||||||
|
assert len(assi_ids) == 0, "Vérification Modification couverture (f6)"
|
||||||
|
assert (
|
||||||
|
assi_2.assiduite_id not in assi_ids
|
||||||
|
), "Vérification Modification couverture (f7)"
|
||||||
|
assert assi_2.est_just is True, "Vérification Modification couverture (f8)"
|
||||||
|
|
||||||
|
# on déjustifie le justificatif 6
|
||||||
|
justif_6.etat = scu.EtatJustificatif.NON_VALIDE
|
||||||
|
db.session.add(justif_6)
|
||||||
|
db.session.commit()
|
||||||
|
assi_ids: list[int] = justif_6.dejustifier_assiduites()
|
||||||
|
assert len(assi_ids) == 1, "Vérification Modification couverture (f9)"
|
||||||
|
assert assi_2.assiduite_id in assi_ids, "Vérification Modification couverture (f10)"
|
||||||
|
assert assi_2.est_just is False, "Vérification Modification couverture (f11)"
|
||||||
|
|
||||||
|
# <=>Justification Chevauchée<=>
|
||||||
|
# 1 justificatif chevauche une assiduité (8h -> 10h) (9h -> 11h)
|
||||||
|
|
||||||
|
justif_7: Justificatif = Justificatif.create_justificatif(
|
||||||
|
etudiant=etud_1,
|
||||||
|
date_debut=scu.is_iso_formated("2024-01-08T09:00", True),
|
||||||
|
date_fin=scu.is_iso_formated("2024-01-08T11:00", True),
|
||||||
|
etat=scu.EtatJustificatif.VALIDE,
|
||||||
|
)
|
||||||
|
|
||||||
|
assi_ids: list[int] = justif_7.justifier_assiduites()
|
||||||
|
assert len(assi_ids) == 0, "Vérification Modification couverture (g1)"
|
||||||
|
assert (
|
||||||
|
assi_2.assiduite_id not in assi_ids
|
||||||
|
), "Vérification Modification couverture (g2)"
|
||||||
|
assert assi_2.est_just is False, "Vérification Modification couverture (g3)"
|
||||||
|
|
||||||
|
# Modification pour correspondre à l'assiduité
|
||||||
|
justif_7.date_debut = scu.is_iso_formated("2024-01-08T08:00", True)
|
||||||
|
db.session.add(justif_7)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
assi_ids: list[int] = justif_7.justifier_assiduites()
|
||||||
|
assert len(assi_ids) == 1, "Vérification Modification couverture (g4)"
|
||||||
|
assert assi_2.assiduite_id in assi_ids, "Vérification Modification couverture (g5)"
|
||||||
|
assert assi_2.est_just is True, "Vérification Modification couverture (g6)"
|
||||||
|
|
||||||
|
|
||||||
|
def test_cache_assiduites(test_client):
|
||||||
|
"""Vérification du bon fonctionnement du cache des assiduités"""
|
||||||
|
|
||||||
|
data = _setup_fake_db(
|
||||||
|
[("2024-01-01", "2024-06-30"), ("2024-07-01", "2024-12-31")],
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
|
||||||
|
formsemestre1: FormSemestre = data["formsemestres"][0]
|
||||||
|
formsemestre2: FormSemestre = data["formsemestres"][1]
|
||||||
|
|
||||||
|
moduleimpl: ModuleImpl = data["moduleimpls"][0]
|
||||||
|
etud: Identite = data["etuds"][0]
|
||||||
|
|
||||||
|
# Création des assiduités
|
||||||
|
assiduites: list[dict] = [
|
||||||
|
# Semestre 1
|
||||||
|
{
|
||||||
|
"date_debut": "2024-01-08T08:00",
|
||||||
|
"date_fin": "2024-01-08T10:00",
|
||||||
|
"moduleimpl": moduleimpl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date_debut": "2024-01-08T14:00",
|
||||||
|
"date_fin": "2024-01-08T16:00",
|
||||||
|
"moduleimpl": moduleimpl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date_debut": "2024-01-09T08:00",
|
||||||
|
"date_fin": "2024-01-09T10:00",
|
||||||
|
"moduleimpl": None,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date_debut": "2024-01-09T14:00",
|
||||||
|
"date_fin": "2024-01-09T16:00",
|
||||||
|
"moduleimpl": None,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date_debut": "2024-01-10T08:00",
|
||||||
|
"date_fin": "2024-01-10T10:00",
|
||||||
|
"moduleimpl": None,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date_debut": "2024-01-10T14:00",
|
||||||
|
"date_fin": "2024-01-10T16:00",
|
||||||
|
"moduleimpl": moduleimpl,
|
||||||
|
},
|
||||||
|
# Semestre 2
|
||||||
|
{
|
||||||
|
"date_debut": "2024-07-09T14:00",
|
||||||
|
"date_fin": "2024-07-09T16:00",
|
||||||
|
"moduleimpl": None,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date_debut": "2024-07-10T08:00",
|
||||||
|
"date_fin": "2024-07-10T10:00",
|
||||||
|
"moduleimpl": None,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date_debut": "2024-07-10T14:00",
|
||||||
|
"date_fin": "2024-07-10T16:00",
|
||||||
|
"moduleimpl": None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
justificatifs: list[dict] = [
|
||||||
|
{
|
||||||
|
"date_debut": "2024-01-10T00:00",
|
||||||
|
"date_fin": "2024-01-10T23:59",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date_debut": "2024-07-09T00:00",
|
||||||
|
"date_fin": "2024-07-09T23:59",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
# On ajoute les assiduités et les justificatifs
|
||||||
|
|
||||||
|
for assi in assiduites:
|
||||||
|
Assiduite.create_assiduite(
|
||||||
|
etud=etud,
|
||||||
|
date_debut=scu.is_iso_formated(assi["date_debut"], True),
|
||||||
|
date_fin=scu.is_iso_formated(assi["date_fin"], True),
|
||||||
|
moduleimpl=assi["moduleimpl"],
|
||||||
|
etat=scu.EtatAssiduite.ABSENT,
|
||||||
|
)
|
||||||
|
|
||||||
|
for justi in justificatifs:
|
||||||
|
Justificatif.create_justificatif(
|
||||||
|
etudiant=etud,
|
||||||
|
date_debut=scu.is_iso_formated(justi["date_debut"], True),
|
||||||
|
date_fin=scu.is_iso_formated(justi["date_fin"], True),
|
||||||
|
etat=scu.EtatJustificatif.VALIDE,
|
||||||
|
).justifier_assiduites()
|
||||||
|
|
||||||
|
# Premier semestre 4nj / 2j / 6t
|
||||||
|
assert scass.get_assiduites_count(etud.id, formsemestre1.to_dict()) == (4, 2, 6)
|
||||||
|
assert scass.formsemestre_get_assiduites_count(etud.id, formsemestre1) == (4, 2, 6)
|
||||||
|
|
||||||
|
# ModuleImpl 2nj / 1j / 3t
|
||||||
|
assert scass.formsemestre_get_assiduites_count(
|
||||||
|
etud.id, formsemestre1, moduleimpl.id
|
||||||
|
) == (2, 1, 3)
|
||||||
|
# Deuxième semestre 2nj / 1j / 3t
|
||||||
|
assert scass.get_assiduites_count(etud.id, formsemestre2.to_dict()) == (2, 1, 3)
|
||||||
|
|
||||||
|
# On supprime la première assiduité (sans invalider le cache)
|
||||||
|
assi: Assiduite = Assiduite.query.filter_by(etudid=etud.id).first()
|
||||||
|
db.session.delete(assi)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Premier semestre 4nj / 2j / 6t (Identique car cache)
|
||||||
|
assert scass.get_assiduites_count(etud.id, formsemestre1.to_dict()) == (4, 2, 6)
|
||||||
|
assert scass.formsemestre_get_assiduites_count(etud.id, formsemestre1) == (4, 2, 6)
|
||||||
|
# ModuleImpl 1nj / 1j / 2t (Change car non cache)
|
||||||
|
assert scass.formsemestre_get_assiduites_count(
|
||||||
|
etud.id, formsemestre1, moduleimpl.id
|
||||||
|
) == (1, 1, 2)
|
||||||
|
# Deuxième semestre 2nj / 1j / 3t (Identique car cache et non modifié)
|
||||||
|
assert scass.get_assiduites_count(etud.id, formsemestre2.to_dict()) == (2, 1, 3)
|
||||||
|
|
||||||
|
# On invalide maintenant le cache
|
||||||
|
scass.invalidate_assiduites_count(etud.id, formsemestre1.to_dict())
|
||||||
|
|
||||||
|
# Premier semestre 3nj / 2j / 5t (Change car cache invalidé)
|
||||||
|
assert scass.get_assiduites_count(etud.id, formsemestre1.to_dict()) == (3, 2, 5)
|
||||||
|
assert scass.formsemestre_get_assiduites_count(etud.id, formsemestre1) == (3, 2, 5)
|
||||||
|
# ModuleImpl 1nj / 1j / 2t (Ne change pas car pas de changement)
|
||||||
|
assert scass.formsemestre_get_assiduites_count(
|
||||||
|
etud.id, formsemestre1, moduleimpl.id
|
||||||
|
) == (1, 1, 2)
|
||||||
|
# Deuxième semestre 2nj / 1j / 3t (Identique car cache et non modifié)
|
||||||
|
assert scass.get_assiduites_count(etud.id, formsemestre2.to_dict()) == (2, 1, 3)
|
||||||
|
@ -191,7 +191,7 @@ def run_sco_basic(verbose=False, dept=None) -> FormSemestre:
|
|||||||
etudid = etuds[0]["etudid"]
|
etudid = etuds[0]["etudid"]
|
||||||
|
|
||||||
_signal_absences_justificatifs(etudid)
|
_signal_absences_justificatifs(etudid)
|
||||||
nbabs, nbabsjust = scass.get_assiduites_count(etudid, sem)
|
_, nbabsjust, nbabs = scass.get_assiduites_count(etudid, sem)
|
||||||
assert nbabs == 6, f"incorrect nbabs ({nbabs})"
|
assert nbabs == 6, f"incorrect nbabs ({nbabs})"
|
||||||
assert nbabsjust == 2, f"incorrect nbabsjust ({nbabsjust})"
|
assert nbabsjust == 2, f"incorrect nbabsjust ({nbabsjust})"
|
||||||
|
|
||||||
|
@ -396,7 +396,7 @@ def ajouter_assiduites_justificatifs(formsemestre: FormSemestre):
|
|||||||
|
|
||||||
for etud in formsemestre.etuds:
|
for etud in formsemestre.etuds:
|
||||||
base_date = datetime.datetime(
|
base_date = datetime.datetime(
|
||||||
2022, 9, [5, 12, 19, 26][random.randint(0, 3)], 8, 0, 0
|
2021, 9, [6, 13, 20, 27][random.randint(0, 3)], 8, 0, 0
|
||||||
)
|
)
|
||||||
base_date = localize_datetime(base_date)
|
base_date = localize_datetime(base_date)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user