forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -56,6 +56,7 @@ class EvaluationEtat:
|
||||
|
||||
evaluation_id: int
|
||||
nb_attente: int
|
||||
nb_notes: int # nb notes d'étudiants inscrits au semestre et au modimpl
|
||||
is_complete: bool
|
||||
|
||||
def to_dict(self):
|
||||
@ -168,13 +169,15 @@ class ModuleImplResults:
|
||||
# NULL en base => ABS (= -999)
|
||||
eval_df.fillna(scu.NOTES_ABSENCE, inplace=True)
|
||||
# Ce merge ne garde que les étudiants inscrits au module
|
||||
# et met à NULL les notes non présentes
|
||||
# et met à NULL (NaN) les notes non présentes
|
||||
# (notes non saisies ou etuds non inscrits au module):
|
||||
evals_notes = evals_notes.merge(
|
||||
eval_df, how="left", left_index=True, right_index=True
|
||||
)
|
||||
# Notes en attente: (ne prend en compte que les inscrits, non démissionnaires)
|
||||
eval_notes_inscr = evals_notes[str(evaluation.id)][list(inscrits_module)]
|
||||
# Nombre de notes (non vides, incluant ATT etc) des inscrits:
|
||||
nb_notes = eval_notes_inscr.notna().sum()
|
||||
eval_etudids_attente = set(
|
||||
eval_notes_inscr.iloc[
|
||||
(eval_notes_inscr == scu.NOTES_ATTENTE).to_numpy()
|
||||
@ -184,6 +187,7 @@ class ModuleImplResults:
|
||||
self.evaluations_etat[evaluation.id] = EvaluationEtat(
|
||||
evaluation_id=evaluation.id,
|
||||
nb_attente=len(eval_etudids_attente),
|
||||
nb_notes=nb_notes,
|
||||
is_complete=is_complete,
|
||||
)
|
||||
# au moins une note en ATT dans ce modimpl:
|
||||
|
@ -9,12 +9,13 @@
|
||||
|
||||
from collections import Counter, defaultdict
|
||||
from collections.abc import Generator
|
||||
import datetime
|
||||
from functools import cached_property
|
||||
from operator import attrgetter
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
import sqlalchemy as sa
|
||||
from flask import g, url_for
|
||||
|
||||
from app import db
|
||||
@ -22,14 +23,19 @@ from app.comp import res_sem
|
||||
from app.comp.res_cache import ResultatsCache
|
||||
from app.comp.jury import ValidationsSemestre
|
||||
from app.comp.moy_mod import ModuleImplResults
|
||||
from app.models import FormSemestre, FormSemestreUECoef
|
||||
from app.models import Identite
|
||||
from app.models import ModuleImpl, ModuleImplInscription
|
||||
from app.models import ScolarAutorisationInscription
|
||||
from app.models.ues import UniteEns
|
||||
from app.models import (
|
||||
Evaluation,
|
||||
FormSemestre,
|
||||
FormSemestreUECoef,
|
||||
Identite,
|
||||
ModuleImpl,
|
||||
ModuleImplInscription,
|
||||
ScolarAutorisationInscription,
|
||||
UniteEns,
|
||||
)
|
||||
from app.scodoc.sco_cache import ResultatsSemestreCache
|
||||
from app.scodoc.codes_cursus import UE_SPORT
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
from app.scodoc.sco_exceptions import ScoValueError, ScoTemporaryError
|
||||
from app.scodoc import sco_utils as scu
|
||||
|
||||
|
||||
@ -192,16 +198,80 @@ class ResultatsSemestre(ResultatsCache):
|
||||
*[mr.etudids_attente for mr in self.modimpls_results.values()]
|
||||
)
|
||||
|
||||
# # Etat des évaluations
|
||||
# # (se substitue à do_evaluation_etat, sans les moyennes par groupes)
|
||||
# def get_evaluations_etats(evaluation_id: int) -> dict:
|
||||
# """Renvoie dict avec les clés:
|
||||
# last_modif
|
||||
# nb_evals_completes
|
||||
# nb_evals_en_cours
|
||||
# nb_evals_vides
|
||||
# attente
|
||||
# """
|
||||
# Etat des évaluations
|
||||
def get_evaluation_etat(self, evaluation: Evaluation) -> dict:
|
||||
"""État d'une évaluation
|
||||
{
|
||||
"coefficient" : float, # 0 si None
|
||||
"description" : str, # de l'évaluation, "" si None
|
||||
"etat" {
|
||||
"evalcomplete" : bool,
|
||||
"last_modif" : datetime.datetime | None, # saisie de note la plus récente
|
||||
"nb_notes" : int, # nb notes d'étudiants inscrits
|
||||
},
|
||||
"jour" : datetime.datetime, # e.date_debut or datetime.datetime(1900, 1, 1)
|
||||
"publish_incomplete" : bool,
|
||||
}
|
||||
"""
|
||||
mod_results = self.modimpls_results.get(evaluation.moduleimpl_id)
|
||||
if mod_results is None:
|
||||
raise ScoTemporaryError() # argh !
|
||||
etat = mod_results.evaluations_etat.get(evaluation.id)
|
||||
if etat is None:
|
||||
raise ScoTemporaryError() # argh !
|
||||
# Date de dernière saisie de note
|
||||
cursor = db.session.execute(
|
||||
sa.text(
|
||||
"SELECT MAX(date) FROM notes_notes WHERE evaluation_id = :evaluation_id"
|
||||
),
|
||||
{"evaluation_id": evaluation.id},
|
||||
)
|
||||
date_modif = cursor.one_or_none()
|
||||
last_modif = date_modif[0] if date_modif else None
|
||||
return {
|
||||
"coefficient": evaluation.coefficient or 0.0,
|
||||
"description": evaluation.description or "",
|
||||
"jour": evaluation.date_debut or datetime.datetime(1900, 1, 1),
|
||||
"publish_incomplete": evaluation.publish_incomplete,
|
||||
"etat": {
|
||||
"evalcomplete": etat.is_complete,
|
||||
"nb_notes": etat.nb_notes,
|
||||
"last_modif": last_modif,
|
||||
},
|
||||
}
|
||||
|
||||
def get_mod_evaluation_etat_list(self, modimpl: ModuleImpl) -> list[dict]:
|
||||
"""Liste des états des évaluations de ce module
|
||||
[ evaluation_etat, ... ] (voir get_evaluation_etat)
|
||||
trié par (numero desc, date_debut desc)
|
||||
"""
|
||||
# nouvelle version 2024-02-02
|
||||
return list(
|
||||
reversed(
|
||||
[
|
||||
self.get_evaluation_etat(evaluation)
|
||||
for evaluation in modimpl.evaluations
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
# modernisation de get_mod_evaluation_etat_list
|
||||
# utilisé par:
|
||||
# sco_evaluations.do_evaluation_etat_in_mod
|
||||
# e["etat"]["evalcomplete"]
|
||||
# e["etat"]["nb_notes"]
|
||||
# e["etat"]["last_modif"]
|
||||
#
|
||||
# sco_formsemestre_status.formsemestre_description_table
|
||||
# "jour" (qui est e.date_debut or datetime.date(1900, 1, 1))
|
||||
# "description"
|
||||
# "coefficient"
|
||||
# e["etat"]["evalcomplete"]
|
||||
# publish_incomplete
|
||||
#
|
||||
# sco_formsemestre_status.formsemestre_tableau_modules
|
||||
# e["etat"]["nb_notes"]
|
||||
#
|
||||
|
||||
# --- JURY...
|
||||
def get_formsemestre_validations(self) -> ValidationsSemestre:
|
||||
|
@ -423,30 +423,37 @@ class NotesTableCompat(ResultatsSemestre):
|
||||
)
|
||||
return evaluations
|
||||
|
||||
def get_evaluations_etats(self) -> list[dict]:
|
||||
"""Liste de toutes les évaluations du semestre
|
||||
[ {...evaluation et son etat...} ]"""
|
||||
# TODO: à moderniser (voir dans ResultatsSemestre)
|
||||
# utilisé par
|
||||
# do_evaluation_etat_in_sem
|
||||
def get_evaluations_etats(self) -> dict[int, dict]:
|
||||
""" "état" de chaque évaluation du semestre
|
||||
{
|
||||
evaluation_id : {
|
||||
"evalcomplete" : bool,
|
||||
"last_modif" : datetime | None
|
||||
"nb_notes" : int,
|
||||
}, ...
|
||||
}
|
||||
"""
|
||||
# utilisé par do_evaluation_etat_in_sem
|
||||
evaluations_etats = {}
|
||||
for modimpl in self.formsemestre.modimpls_sorted:
|
||||
for evaluation in modimpl.evaluations:
|
||||
evaluation_etat = self.get_evaluation_etat(evaluation)
|
||||
evaluations_etats[evaluation.id] = evaluation_etat["etat"]
|
||||
return evaluations_etats
|
||||
|
||||
from app.scodoc import sco_evaluations
|
||||
|
||||
if not hasattr(self, "_evaluations_etats"):
|
||||
self._evaluations_etats = sco_evaluations.do_evaluation_list_in_sem(
|
||||
self.formsemestre.id
|
||||
)
|
||||
|
||||
return self._evaluations_etats
|
||||
|
||||
def get_mod_evaluation_etat_list(self, moduleimpl_id) -> list[dict]:
|
||||
"""Liste des états des évaluations de ce module"""
|
||||
# XXX TODO à moderniser: lent, recharge des données que l'on a déjà...
|
||||
return [
|
||||
e
|
||||
for e in self.get_evaluations_etats()
|
||||
if e["moduleimpl_id"] == moduleimpl_id
|
||||
]
|
||||
# ancienne version < 2024-02-02
|
||||
# def get_mod_evaluation_etat_list(self, moduleimpl_id) -> list[dict]:
|
||||
# """Liste des états des évaluations de ce module
|
||||
# ordonnée selon (numero desc, date_debut desc)
|
||||
# """
|
||||
# # à moderniser: lent, recharge des données que l'on a déjà...
|
||||
# # remplacemé par ResultatsSemestre.get_mod_evaluation_etat_list
|
||||
# #
|
||||
# return [
|
||||
# e
|
||||
# for e in self.get_evaluations_etats()
|
||||
# if e["moduleimpl_id"] == moduleimpl_id
|
||||
# ]
|
||||
|
||||
def get_moduleimpls_attente(self):
|
||||
"""Liste des modimpls du semestre ayant des notes en attente"""
|
||||
|
@ -40,7 +40,7 @@ from app import db
|
||||
from app.auth.models import User
|
||||
from app.comp import res_sem
|
||||
from app.comp.res_compat import NotesTableCompat
|
||||
from app.models import Evaluation, FormSemestre
|
||||
from app.models import Evaluation, FormSemestre, ModuleImpl
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
@ -280,82 +280,14 @@ def do_evaluation_etat(
|
||||
}
|
||||
|
||||
|
||||
def do_evaluation_list_in_sem(formsemestre_id, with_etat=True):
|
||||
"""Liste les évaluations de tous les modules de ce semestre.
|
||||
Triée par module, numero desc, date_debut desc
|
||||
Donne pour chaque eval son état (voir do_evaluation_etat)
|
||||
{ evaluation_id,nb_inscrits, nb_notes, nb_abs, nb_neutre, moy, median, last_modif ... }
|
||||
|
||||
Exemple:
|
||||
[ {
|
||||
'coefficient': 1.0,
|
||||
'description': 'QCM et cas pratiques',
|
||||
'etat': {
|
||||
'evalattente': False,
|
||||
'evalcomplete': True,
|
||||
'evaluation_id': 'GEAEVAL82883',
|
||||
'gr_incomplets': [],
|
||||
'gr_moyennes': [{
|
||||
'gr_median': '12.00', # sur 20
|
||||
'gr_moy': '11.88',
|
||||
'gr_nb_att': 0,
|
||||
'gr_nb_notes': 166,
|
||||
'group_id': 'GEAG266762',
|
||||
'group_name': None
|
||||
}],
|
||||
'groups': {'GEAG266762': {'etudid': 'GEAEID80603',
|
||||
'group_id': 'GEAG266762',
|
||||
'group_name': None,
|
||||
'partition_id': 'GEAP266761'}
|
||||
},
|
||||
'last_modif': datetime.datetime(2015, 12, 3, 15, 15, 16),
|
||||
'median': '12.00',
|
||||
'moy': '11.84',
|
||||
'nb_abs': 2,
|
||||
'nb_att': 0,
|
||||
'nb_inscrits': 166,
|
||||
'nb_neutre': 0,
|
||||
'nb_notes': 168,
|
||||
'nb_notes_total': 169
|
||||
},
|
||||
'evaluation_id': 'GEAEVAL82883',
|
||||
'evaluation_type': 0,
|
||||
'heure_debut': datetime.time(8, 0),
|
||||
'heure_fin': datetime.time(9, 30),
|
||||
'jour': datetime.date(2015, 11, 3), // vide => 1/1/1900
|
||||
'moduleimpl_id': 'GEAMIP80490',
|
||||
'note_max': 20.0,
|
||||
'numero': 0,
|
||||
'publish_incomplete': 0,
|
||||
'visibulletin': 1} ]
|
||||
|
||||
"""
|
||||
req = """SELECT E.id AS evaluation_id, E.*
|
||||
FROM notes_evaluation E, notes_moduleimpl MI
|
||||
WHERE MI.formsemestre_id = %(formsemestre_id)s
|
||||
and MI.id = E.moduleimpl_id
|
||||
ORDER BY MI.id, numero desc, date_debut desc
|
||||
"""
|
||||
cnx = ndb.GetDBConnexion()
|
||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
cursor.execute(req, {"formsemestre_id": formsemestre_id})
|
||||
res = cursor.dictfetchall()
|
||||
# etat de chaque evaluation:
|
||||
for r in res:
|
||||
if with_etat:
|
||||
r["etat"] = do_evaluation_etat(r["evaluation_id"])
|
||||
r["jour"] = r["date_debut"] or datetime.date(1900, 1, 1)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def _eval_etat(evals):
|
||||
"""evals: list of mappings (etats)
|
||||
def _summarize_evals_etats(evals: list[dict]) -> dict:
|
||||
"""Synthétise les états d'une liste d'évaluations
|
||||
evals: list of mappings (etats),
|
||||
utilise e["etat"]["evalcomplete"], e["etat"]["nb_notes"], e["etat"]["last_modif"]
|
||||
-> nb_eval_completes, nb_evals_en_cours,
|
||||
nb_evals_vides, date derniere modif
|
||||
|
||||
Une eval est "complete" ssi tous les etudiants *inscrits* ont une note.
|
||||
|
||||
"""
|
||||
nb_evals_completes, nb_evals_en_cours, nb_evals_vides = 0, 0, 0
|
||||
dates = []
|
||||
@ -370,11 +302,8 @@ def _eval_etat(evals):
|
||||
if last_modif is not None:
|
||||
dates.append(e["etat"]["last_modif"])
|
||||
|
||||
if dates:
|
||||
dates = scu.sort_dates(dates)
|
||||
last_modif = dates[-1] # date de derniere modif d'une note dans un module
|
||||
else:
|
||||
last_modif = ""
|
||||
# date de derniere modif d'une note dans un module
|
||||
last_modif = sorted(dates)[-1] if dates else ""
|
||||
|
||||
return {
|
||||
"nb_evals_completes": nb_evals_completes,
|
||||
@ -384,37 +313,42 @@ def _eval_etat(evals):
|
||||
}
|
||||
|
||||
|
||||
def do_evaluation_etat_in_sem(formsemestre_id):
|
||||
"""-> nb_eval_completes, nb_evals_en_cours, nb_evals_vides,
|
||||
date derniere modif, attente
|
||||
|
||||
XXX utilisé par
|
||||
- formsemestre_status_head
|
||||
- gen_formsemestre_recapcomplet_xml
|
||||
- gen_formsemestre_recapcomplet_json
|
||||
|
||||
"nb_evals_completes"
|
||||
"nb_evals_en_cours"
|
||||
"nb_evals_vides"
|
||||
"date_derniere_note"
|
||||
"last_modif"
|
||||
"attente"
|
||||
def do_evaluation_etat_in_sem(formsemestre: FormSemestre) -> dict:
|
||||
"""-> { nb_eval_completes, nb_evals_en_cours, nb_evals_vides,
|
||||
date derniere modif, attente }
|
||||
"""
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
# Note: utilisé par
|
||||
# - formsemestre_status_head
|
||||
# nb_evals_completes, nb_evals_en_cours, nb_evals_vides, last_modif
|
||||
# pour la ligne
|
||||
# Évaluations: 20 ok, 8 en cours, 5 vides (dernière note saisie le 11/01/2024 à 19h49)
|
||||
# attente
|
||||
#
|
||||
# - gen_formsemestre_recapcomplet_xml
|
||||
# - gen_formsemestre_recapcomplet_json
|
||||
# nb_evals_completes, nb_evals_en_cours, nb_evals_vides, last_modif
|
||||
#
|
||||
# "nb_evals_completes"
|
||||
# "nb_evals_en_cours"
|
||||
# "nb_evals_vides"
|
||||
# "last_modif"
|
||||
# "attente"
|
||||
|
||||
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||
evals = nt.get_evaluations_etats()
|
||||
etat = _eval_etat(evals)
|
||||
evaluations_etats = nt.get_evaluations_etats()
|
||||
# raccordement moche...
|
||||
etat = _summarize_evals_etats([{"etat": v} for v in evaluations_etats.values()])
|
||||
# Ajoute information sur notes en attente
|
||||
etat["attente"] = len(nt.get_moduleimpls_attente()) > 0
|
||||
return etat
|
||||
|
||||
|
||||
def do_evaluation_etat_in_mod(nt, moduleimpl_id):
|
||||
def do_evaluation_etat_in_mod(nt, modimpl: ModuleImpl):
|
||||
"""état des évaluations dans ce module"""
|
||||
evals = nt.get_mod_evaluation_etat_list(moduleimpl_id)
|
||||
etat = _eval_etat(evals)
|
||||
evals = nt.get_mod_evaluation_etat_list(modimpl)
|
||||
etat = _summarize_evals_etats(evals)
|
||||
# Il y a-t-il des notes en attente dans ce module ?
|
||||
etat["attente"] = nt.modimpls_results[moduleimpl_id].en_attente
|
||||
etat["attente"] = nt.modimpls_results[modimpl.id].en_attente
|
||||
return etat
|
||||
|
||||
|
||||
|
@ -230,3 +230,15 @@ class APIInvalidParams(Exception):
|
||||
|
||||
class ScoFormationConflict(Exception):
|
||||
"""Conflit cohérence formation (APC)"""
|
||||
|
||||
|
||||
class ScoTemporaryError(ScoValueError):
|
||||
"""Erreurs temporaires rarissimes (caches ?)"""
|
||||
|
||||
def __init__(self, msg: str = ""):
|
||||
msg = """
|
||||
<p>"Erreur temporaire</p>
|
||||
<p>Veuillez ré-essayer. Si le problème persiste, merci de contacter l'assistance ScoDoc
|
||||
</p>
|
||||
"""
|
||||
super().__init__(msg)
|
||||
|
@ -627,9 +627,7 @@ def formsemestre_description_table(
|
||||
# car l'UE de rattachement n'a pas d'intérêt en BUT
|
||||
rows.append(ue_info)
|
||||
|
||||
mod_inscrits = sco_moduleimpl.do_moduleimpl_inscription_list(
|
||||
moduleimpl_id=modimpl.id
|
||||
)
|
||||
mod_nb_inscrits = nt.modimpls_results[modimpl.id].nb_inscrits_module
|
||||
enseignants = ", ".join(ens.get_prenomnom() for ens in modimpl.enseignants)
|
||||
|
||||
row = {
|
||||
@ -638,7 +636,7 @@ def formsemestre_description_table(
|
||||
"Code": modimpl.module.code or "",
|
||||
"Module": modimpl.module.abbrev or modimpl.module.titre,
|
||||
"_Module_class": "scotext",
|
||||
"Inscrits": len(mod_inscrits),
|
||||
"Inscrits": mod_nb_inscrits,
|
||||
"Responsable": sco_users.user_info(modimpl.responsable_id)["nomprenom"],
|
||||
"_Responsable_class": "scotext",
|
||||
"Enseignants": enseignants,
|
||||
@ -680,7 +678,7 @@ def formsemestre_description_table(
|
||||
|
||||
if with_evals:
|
||||
# Ajoute lignes pour evaluations
|
||||
evals = nt.get_mod_evaluation_etat_list(modimpl.id)
|
||||
evals = nt.get_mod_evaluation_etat_list(modimpl)
|
||||
evals.reverse() # ordre chronologique
|
||||
# Ajoute etat:
|
||||
eval_rows = []
|
||||
@ -942,10 +940,10 @@ def html_expr_diagnostic(diagnostics):
|
||||
|
||||
def formsemestre_status_head(formsemestre_id: int = None, page_title: str = None):
|
||||
"""En-tête HTML des pages "semestre" """
|
||||
sem: FormSemestre = db.session.get(FormSemestre, formsemestre_id)
|
||||
if not sem:
|
||||
formsemestre: FormSemestre = db.session.get(FormSemestre, formsemestre_id)
|
||||
if not formsemestre:
|
||||
raise ScoValueError("Semestre inexistant (il a peut être été supprimé ?)")
|
||||
formation: Formation = sem.formation
|
||||
formation: Formation = formsemestre.formation
|
||||
parcours = formation.get_cursus()
|
||||
|
||||
page_title = page_title or "Modules de "
|
||||
@ -957,25 +955,25 @@ def formsemestre_status_head(formsemestre_id: int = None, page_title: str = None
|
||||
f"""<table>
|
||||
<tr><td class="fichetitre2">Formation: </td><td>
|
||||
<a href="{url_for('notes.ue_table',
|
||||
scodoc_dept=g.scodoc_dept, formation_id=sem.formation.id)}"
|
||||
scodoc_dept=g.scodoc_dept, formation_id=formsemestre.formation.id)}"
|
||||
class="discretelink" title="Formation {
|
||||
formation.acronyme}, v{formation.version}">{formation.titre}</a>
|
||||
""",
|
||||
]
|
||||
if sem.semestre_id >= 0:
|
||||
H.append(f", {parcours.SESSION_NAME} {sem.semestre_id}")
|
||||
if sem.modalite:
|
||||
H.append(f" en {sem.modalite}")
|
||||
if sem.etapes:
|
||||
if formsemestre.semestre_id >= 0:
|
||||
H.append(f", {parcours.SESSION_NAME} {formsemestre.semestre_id}")
|
||||
if formsemestre.modalite:
|
||||
H.append(f" en {formsemestre.modalite}")
|
||||
if formsemestre.etapes:
|
||||
H.append(
|
||||
f""" (étape <b><tt>{
|
||||
sem.etapes_apo_str() or "-"
|
||||
formsemestre.etapes_apo_str() or "-"
|
||||
}</tt></b>)"""
|
||||
)
|
||||
H.append("</td></tr>")
|
||||
if formation.is_apc():
|
||||
# Affiche les parcours BUT cochés. Si aucun, tous ceux du référentiel.
|
||||
sem_parcours = sem.get_parcours_apc()
|
||||
sem_parcours = formsemestre.get_parcours_apc()
|
||||
H.append(
|
||||
f"""
|
||||
<tr><td class="fichetitre2">Parcours: </td>
|
||||
@ -984,7 +982,7 @@ def formsemestre_status_head(formsemestre_id: int = None, page_title: str = None
|
||||
"""
|
||||
)
|
||||
|
||||
evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre_id)
|
||||
evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre)
|
||||
H.append(
|
||||
'<tr><td class="fichetitre2">Évaluations: </td><td> %(nb_evals_completes)s ok, %(nb_evals_en_cours)s en cours, %(nb_evals_vides)s vides'
|
||||
% evals
|
||||
@ -1002,11 +1000,11 @@ def formsemestre_status_head(formsemestre_id: int = None, page_title: str = None
|
||||
"""<span class="fontred">Il y a des notes en attente !</span>
|
||||
Le classement des étudiants n'a qu'une valeur indicative."""
|
||||
)
|
||||
if sem.bul_hide_xml:
|
||||
if formsemestre.bul_hide_xml:
|
||||
warnings.append("""Bulletins non publiés sur la passerelle.""")
|
||||
if sem.block_moyennes:
|
||||
if formsemestre.block_moyennes:
|
||||
warnings.append("Calcul des moyennes bloqué !")
|
||||
if sem.semestre_id >= 0 and not sem.est_sur_une_annee():
|
||||
if formsemestre.semestre_id >= 0 and not formsemestre.est_sur_une_annee():
|
||||
warnings.append("""<em>Ce semestre couvre plusieurs années scolaires !</em>""")
|
||||
if warnings:
|
||||
H += [
|
||||
@ -1028,18 +1026,14 @@ def formsemestre_status(formsemestre_id=None, check_parcours=True):
|
||||
# S'assure que les groupes de parcours sont à jour:
|
||||
if int(check_parcours):
|
||||
formsemestre.setup_parcours_groups()
|
||||
modimpls = sco_moduleimpl.moduleimpl_withmodule_list(
|
||||
formsemestre_id=formsemestre_id
|
||||
)
|
||||
modimpls = formsemestre.modimpls_sorted
|
||||
nt = res_sem.load_formsemestre_results(formsemestre)
|
||||
|
||||
# Construit la liste de tous les enseignants de ce semestre:
|
||||
mails_enseignants = set(u.email for u in formsemestre.responsables)
|
||||
for modimpl in modimpls:
|
||||
mails_enseignants.add(sco_users.user_info(modimpl["responsable_id"])["email"])
|
||||
mails_enseignants |= set(
|
||||
[sco_users.user_info(m["ens_id"])["email"] for m in modimpl["ens"]]
|
||||
)
|
||||
mails_enseignants.add(sco_users.user_info(modimpl.responsable_id)["email"])
|
||||
mails_enseignants |= {u.email for u in modimpl.enseignants if u.email}
|
||||
|
||||
can_edit = formsemestre.can_be_edited_by(current_user)
|
||||
can_change_all_notes = current_user.has_permission(Permission.EditAllNotes) or (
|
||||
@ -1089,13 +1083,13 @@ def formsemestre_status(formsemestre_id=None, check_parcours=True):
|
||||
if nt.parcours.APC_SAE:
|
||||
# BUT: tableau ressources puis SAE
|
||||
ressources = [
|
||||
m for m in modimpls if m["module"]["module_type"] == ModuleType.RESSOURCE
|
||||
m for m in modimpls if m.module.module_type == ModuleType.RESSOURCE
|
||||
]
|
||||
saes = [m for m in modimpls if m["module"]["module_type"] == ModuleType.SAE]
|
||||
saes = [m for m in modimpls if m.module.module_type == ModuleType.SAE]
|
||||
autres = [
|
||||
m
|
||||
for m in modimpls
|
||||
if m["module"]["module_type"] not in (ModuleType.RESSOURCE, ModuleType.SAE)
|
||||
if m.module.module_type not in (ModuleType.RESSOURCE, ModuleType.SAE)
|
||||
]
|
||||
H += [
|
||||
f"""
|
||||
@ -1136,7 +1130,7 @@ def formsemestre_status(formsemestre_id=None, check_parcours=True):
|
||||
modimpls_classic = [
|
||||
m
|
||||
for m in modimpls
|
||||
if m["module"]["module_type"] not in (ModuleType.RESSOURCE, ModuleType.SAE)
|
||||
if m.module.module_type not in (ModuleType.RESSOURCE, ModuleType.SAE)
|
||||
]
|
||||
H += [
|
||||
"<p>",
|
||||
@ -1168,8 +1162,10 @@ def formsemestre_status(formsemestre_id=None, check_parcours=True):
|
||||
adrlist = list(mails_enseignants - {None, ""})
|
||||
if adrlist:
|
||||
H.append(
|
||||
'<p><a class="stdlink" href="mailto:?cc=%s">Courrier aux %d enseignants du semestre</a></p>'
|
||||
% (",".join(adrlist), len(adrlist))
|
||||
f"""<p>
|
||||
<a class="stdlink" href="mailto:?cc={','.join(adrlist)}">Courrier aux {
|
||||
len(adrlist)} enseignants du semestre</a>
|
||||
</p>"""
|
||||
)
|
||||
return "".join(H) + html_sco_header.sco_footer()
|
||||
|
||||
@ -1189,7 +1185,7 @@ _TABLEAU_MODULES_FOOT = """</table>"""
|
||||
|
||||
|
||||
def formsemestre_tableau_modules(
|
||||
modimpls: list[dict],
|
||||
modimpls: list[ModuleImpl],
|
||||
nt,
|
||||
formsemestre: FormSemestre,
|
||||
can_edit=True,
|
||||
@ -1200,11 +1196,11 @@ def formsemestre_tableau_modules(
|
||||
H = []
|
||||
prev_ue_id = None
|
||||
for modimpl in modimpls:
|
||||
mod: Module = db.session.get(Module, modimpl["module_id"])
|
||||
mod: Module = modimpl.module
|
||||
moduleimpl_status_url = url_for(
|
||||
"notes.moduleimpl_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
moduleimpl_id=modimpl["moduleimpl_id"],
|
||||
moduleimpl_id=modimpl.id,
|
||||
)
|
||||
mod_descr = "Module " + (mod.titre or "")
|
||||
if mod.is_apc():
|
||||
@ -1221,48 +1217,45 @@ def formsemestre_tableau_modules(
|
||||
mod_descr += " (pas de coefficients) "
|
||||
else:
|
||||
mod_descr += ", coef. " + str(mod.coefficient)
|
||||
mod_ens = sco_users.user_info(modimpl["responsable_id"])["nomcomplet"]
|
||||
if modimpl["ens"]:
|
||||
mod_ens = sco_users.user_info(modimpl.responsable_id)["nomcomplet"]
|
||||
if modimpl.enseignants.count():
|
||||
mod_ens += " (resp.), " + ", ".join(
|
||||
[sco_users.user_info(e["ens_id"])["nomcomplet"] for e in modimpl["ens"]]
|
||||
[u.get_nomcomplet() for u in modimpl.enseignants]
|
||||
)
|
||||
mod_inscrits = sco_moduleimpl.do_moduleimpl_inscription_list(
|
||||
moduleimpl_id=modimpl["moduleimpl_id"]
|
||||
)
|
||||
|
||||
ue = modimpl["ue"]
|
||||
if show_ues and (prev_ue_id != ue["ue_id"]):
|
||||
prev_ue_id = ue["ue_id"]
|
||||
titre = ue["titre"]
|
||||
mod_nb_inscrits = nt.modimpls_results[modimpl.id].nb_inscrits_module
|
||||
ue = modimpl.module.ue
|
||||
if show_ues and (prev_ue_id != ue.id):
|
||||
prev_ue_id = ue.id
|
||||
titre = ue.titre
|
||||
if use_ue_coefs:
|
||||
titre += f""" <b>(coef. {ue["coefficient"] or 0.0})</b>"""
|
||||
titre += f""" <b>(coef. {ue.coefficient or 0.0})</b>"""
|
||||
H.append(
|
||||
f"""<tr class="formsemestre_status_ue"><td colspan="4">
|
||||
<span class="status_ue_acro">{ue["acronyme"]}</span>
|
||||
<span class="status_ue_acro">{ue.acronyme}</span>
|
||||
<span class="status_ue_title">{titre}</span>
|
||||
</td><td colspan="2">"""
|
||||
)
|
||||
|
||||
expr = sco_compute_moy.get_ue_expression(
|
||||
formsemestre.id, ue["ue_id"], html_quote=True
|
||||
formsemestre.id, ue.id, html_quote=True
|
||||
)
|
||||
if expr:
|
||||
H.append(
|
||||
f""" <span class="formula" title="mode de calcul de la moyenne d'UE">{expr}</span>
|
||||
<span class="warning">formule inutilisée en 9.2: <a href="{
|
||||
url_for("notes.delete_ue_expr", scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id, ue_id=ue["ue_id"] )
|
||||
url_for("notes.delete_ue_expr", scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id, ue_id=ue.id )
|
||||
}
|
||||
">supprimer</a></span>"""
|
||||
)
|
||||
|
||||
H.append("</td></tr>")
|
||||
|
||||
if modimpl["ue"]["type"] != codes_cursus.UE_STANDARD:
|
||||
if ue.type != codes_cursus.UE_STANDARD:
|
||||
fontorange = " fontorange" # style css additionnel
|
||||
else:
|
||||
fontorange = ""
|
||||
|
||||
etat = sco_evaluations.do_evaluation_etat_in_mod(nt, modimpl["moduleimpl_id"])
|
||||
etat = sco_evaluations.do_evaluation_etat_in_mod(nt, modimpl)
|
||||
# if nt.parcours.APC_SAE:
|
||||
# tbd style si module non conforme
|
||||
if (
|
||||
@ -1282,10 +1275,10 @@ def formsemestre_tableau_modules(
|
||||
<td class="scotext"><a href="{moduleimpl_status_url}" title="{mod_descr}"
|
||||
class="formsemestre_status_link">{mod.abbrev or mod.titre or ""}</a>
|
||||
</td>
|
||||
<td class="formsemestre_status_inscrits">{len(mod_inscrits)}</td>
|
||||
<td class="formsemestre_status_inscrits">{mod_nb_inscrits}</td>
|
||||
<td class="resp scotext">
|
||||
<a class="discretelink" href="{moduleimpl_status_url}" title="{mod_ens}">{
|
||||
sco_users.user_info(modimpl["responsable_id"])["prenomnom"]
|
||||
sco_users.user_info(modimpl.responsable_id)["prenomnom"]
|
||||
}</a>
|
||||
</td>
|
||||
<td>
|
||||
@ -1339,10 +1332,7 @@ def formsemestre_tableau_modules(
|
||||
)
|
||||
elif mod.module_type == ModuleType.MALUS:
|
||||
nb_malus_notes = sum(
|
||||
[
|
||||
e["etat"]["nb_notes"]
|
||||
for e in nt.get_mod_evaluation_etat_list(modimpl["moduleimpl_id"])
|
||||
]
|
||||
e["etat"]["nb_notes"] for e in nt.get_mod_evaluation_etat_list(modimpl)
|
||||
)
|
||||
H.append(
|
||||
f"""<td class="malus">
|
||||
|
@ -367,7 +367,7 @@ def gen_formsemestre_recapcomplet_xml(
|
||||
doc = ElementTree.Element(
|
||||
"recapsemestre", formsemestre_id=str(formsemestre_id), date=docdate
|
||||
)
|
||||
evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre_id)
|
||||
evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre)
|
||||
doc.append(
|
||||
ElementTree.Element(
|
||||
"evals_info",
|
||||
@ -408,7 +408,7 @@ def gen_formsemestre_recapcomplet_json(
|
||||
docdate = ""
|
||||
else:
|
||||
docdate = datetime.datetime.now().isoformat()
|
||||
evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre_id)
|
||||
evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre)
|
||||
js_data = {
|
||||
"docdate": docdate,
|
||||
"formsemestre_id": formsemestre_id,
|
||||
|
@ -1440,17 +1440,6 @@ EMO_PREV_ARROW = "❮"
|
||||
EMO_NEXT_ARROW = "❯"
|
||||
|
||||
|
||||
def sort_dates(L, reverse=False):
|
||||
"""Return sorted list of dates, allowing None items (they are put at the beginning)"""
|
||||
mindate = datetime.datetime(datetime.MINYEAR, 1, 1)
|
||||
try:
|
||||
return sorted(L, key=lambda x: x or mindate, reverse=reverse)
|
||||
except:
|
||||
# Helps debugging
|
||||
log("sort_dates( %s )" % L)
|
||||
raise
|
||||
|
||||
|
||||
def heterogeneous_sorting_key(x):
|
||||
"key to sort non homogeneous sequences"
|
||||
return (float(x), "") if isinstance(x, (bool, float, int)) else (-1e34, str(x))
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.6.90"
|
||||
SCOVERSION = "9.6.91"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user