comptes croisés: ajout nb RCUE et code annuel

This commit is contained in:
Emmanuel Viennet 2022-09-26 20:57:52 +02:00
parent 1c30d4d926
commit 692d7b5fe0
6 changed files with 85 additions and 34 deletions

View File

@ -296,6 +296,10 @@ class DecisionsProposeesAnnee(DecisionsProposees):
[rcue for rcue in rcues_avec_niveau if rcue.est_validable()] [rcue for rcue in rcues_avec_niveau if rcue.est_validable()]
) )
"le nombre de comp. validables (éventuellement par compensation)" "le nombre de comp. validables (éventuellement par compensation)"
self.nb_rcue_valides = len(
[rcue for rcue in rcues_avec_niveau if rcue.code_valide()]
)
"le nombre de niveaux validés (déc. jury prise)"
self.nb_rcues_under_8 = len( self.nb_rcues_under_8 = len(
[rcue for rcue in rcues_avec_niveau if not rcue.est_suffisant()] [rcue for rcue in rcues_avec_niveau if not rcue.est_suffisant()]
) )
@ -393,7 +397,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
<li>RCUEs: {html.escape(str(self.rcues_annee))}</li> <li>RCUEs: {html.escape(str(self.rcues_annee))}</li>
<li>nb_competences: {getattr(self, "nb_competences", "-")}</li> <li>nb_competences: {getattr(self, "nb_competences", "-")}</li>
<li>nb_nb_validables: {getattr(self, "nb_validables", "-")}</li> <li>nb_validables: {getattr(self, "nb_validables", "-")}</li>
<li>codes: {self.codes}</li> <li>codes: {self.codes}</li>
<li>explanation: {self.explanation}</li> <li>explanation: {self.explanation}</li>
</ul> </ul>

View File

@ -299,7 +299,7 @@ class NotesTableCompat(ResultatsSemestre):
return sum([d.get("ects", 0.0) for d in decisions_ues.values()]) return sum([d.get("ects", 0.0) for d in decisions_ues.values()])
def get_etud_decision_sem(self, etudid: int) -> dict: def get_etud_decision_sem(self, etudid: int) -> dict:
"""Decision du jury prise pour cet etudiant, ou None s'il n'y en pas eu. """Decision du jury semestre prise pour cet etudiant, ou None s'il n'y en pas eu.
{ 'code' : None|ATT|..., 'assidu' : 0|1, 'event_date' : , compense_formsemestre_id } { 'code' : None|ATT|..., 'assidu' : 0|1, 'event_date' : , compense_formsemestre_id }
Si état défaillant, force le code a DEF Si état défaillant, force le code a DEF
""" """

View File

@ -196,8 +196,8 @@ class RegroupementCoherentUE:
) )
def est_validable(self) -> bool: def est_validable(self) -> bool:
"""Vrai si ce RCU satisfait les conditions pour être validé """Vrai si ce RCUE satisfait les conditions pour être validé,
Pour cela, il suffit que la moyenne des UE qui le constitue soit > 10 c'est à dire que la moyenne des UE qui le constituent soit > 10
""" """
return (self.moy_rcue is not None) and ( return (self.moy_rcue is not None) and (
self.moy_rcue > sco_codes.BUT_BARRE_RCUE self.moy_rcue > sco_codes.BUT_BARRE_RCUE

View File

@ -610,7 +610,7 @@ def log_unknown_etud():
log(f"unknown student: args={etud_args}") log(f"unknown student: args={etud_args}")
def get_etud_info(etudid=False, code_nip=False, filled=False) -> list: def get_etud_info(etudid=False, code_nip=False, filled=False) -> list[dict]:
"""infos sur un etudiant (API). If not found, returns empty list. """infos sur un etudiant (API). If not found, returns empty list.
On peut spécifier etudid ou code_nip On peut spécifier etudid ou code_nip
ou bien cherche dans les arguments de la requête courante: ou bien cherche dans les arguments de la requête courante:

View File

@ -167,9 +167,9 @@ def _formsemestre_enrich(sem):
sem["semestre_id"], sem["semestre_id"],
) # eg "DUT Informatique semestre 2" ) # eg "DUT Informatique semestre 2"
sem["dateord"] = ndb.DateDMYtoISO(sem["date_debut"])
sem["date_debut_iso"] = ndb.DateDMYtoISO(sem["date_debut"]) sem["date_debut_iso"] = ndb.DateDMYtoISO(sem["date_debut"])
sem["date_fin_iso"] = ndb.DateDMYtoISO(sem["date_fin"]) sem["date_fin_iso"] = ndb.DateDMYtoISO(sem["date_fin"])
sem["dateord"] = sem["date_debut_iso"] # pour les tris
try: try:
mois_debut, annee_debut = sem["date_debut"].split("/")[1:] mois_debut, annee_debut = sem["date_debut"].split("/")[1:]
except: except:

View File

@ -39,12 +39,14 @@ from operator import itemgetter
from flask import url_for, g, request from flask import url_for, g, request
import pydot import pydot
from app.but import jury_but
from app.comp import res_sem from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat from app.comp.res_compat import NotesTableCompat
from app.models import FormSemestre, ScolarAutorisationInscription from app.models import FormSemestre, ScolarAutorisationInscription
from app.models import FormationModalite
from app.models.etudiants import Identite
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.models import FormationModalite
from app.scodoc import notesdb as ndb from app.scodoc import notesdb as ndb
from app.scodoc import html_sco_header from app.scodoc import html_sco_header
from app.scodoc import sco_codes_parcours from app.scodoc import sco_codes_parcours
@ -52,6 +54,7 @@ from app.scodoc import sco_etud
from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre
from app.scodoc import sco_formsemestre_inscriptions from app.scodoc import sco_formsemestre_inscriptions
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_pvjury
import sco_version import sco_version
from app.scodoc.gen_tables import GenTable from app.scodoc.gen_tables import GenTable
from app import log from app import log
@ -59,17 +62,28 @@ from app.scodoc.sco_codes_parcours import code_semestre_validant
MAX_ETUD_IN_DESCR = 20 MAX_ETUD_IN_DESCR = 20
LEGENDES_CODES_BUT = {
"Nb_rcue_valides": "nb RCUE validés",
"decision_annee": "code jury annuel BUT",
}
def formsemestre_etuds_stats(sem, only_primo=False):
def formsemestre_etuds_stats(sem: dict, only_primo=False):
"""Récupère liste d'etudiants avec etat et decision.""" """Récupère liste d'etudiants avec etat et decision."""
formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"]) formsemestre: FormSemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"])
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
T = nt.get_table_moyennes_triees() T = nt.get_table_moyennes_triees()
# Décisions de jury BUT pour les semestres pairs seulement
jury_but_mode = (
formsemestre.formation.is_apc() and formsemestre.semestre_id % 2 == 0
)
# Construit liste d'étudiants du semestre avec leur decision # Construit liste d'étudiants du semestre avec leur decision
etuds = [] etuds = []
for t in T: for t in T:
etudid = t[-1] etudid = t[-1]
etudiant: Identite = Identite.query.get(etudid)
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
decision = nt.get_etud_decision_sem(etudid) decision = nt.get_etud_decision_sem(etudid)
if decision: if decision:
@ -87,6 +101,11 @@ def formsemestre_etuds_stats(sem, only_primo=False):
autorisations.sort() autorisations.sort()
autorisations_str = ", ".join(autorisations) autorisations_str = ", ".join(autorisations)
etud["devenir"] = autorisations_str etud["devenir"] = autorisations_str
# Décisions de jury BUT (APC)
if jury_but_mode:
deca = jury_but.DecisionsProposeesAnnee(etudiant, formsemestre)
etud["nb_rcue_valides"] = deca.nb_rcue_valides
etud["decision_annee"] = deca.code_valide
# Ajout clé 'bac-specialite' # Ajout clé 'bac-specialite'
bs = [] bs = []
if etud["bac"]: if etud["bac"]:
@ -100,13 +119,22 @@ def formsemestre_etuds_stats(sem, only_primo=False):
return etuds return etuds
def is_primo_etud(etud, sem): def is_primo_etud(etud: dict, sem: dict):
"""Determine si un (filled) etud a ete inscrit avant ce semestre. """Determine si un (filled) etud a été inscrit avant ce semestre.
Regarde la liste des semestres dans lesquels l'étudiant est inscrit Regarde la liste des semestres dans lesquels l'étudiant est inscrit.
Si semestre pair, considère comme primo-entrants ceux qui étaient
primo dans le précédent (S_{2n-1}).
""" """
now = sem["dateord"] debut_cur = sem["date_debut_iso"]
# si semestre impair et sem. précédent contigu, recule date debut
if (
(len(etud["sems"]) > 1)
and (sem["semestre_id"] % 2 == 0)
and (etud["sems"][1]["semestre_id"] == (sem["semestre_id"] - 1))
):
debut_cur = etud["sems"][1]["date_debut_iso"]
for s in etud["sems"]: # le + recent d'abord for s in etud["sems"]: # le + recent d'abord
if s["dateord"] < now: if s["date_debut_iso"] < debut_cur:
return False return False
return True return True
@ -176,21 +204,20 @@ def _results_by_category(
# ajout titre ligne: # ajout titre ligne:
for (cat, l) in zip(categories, C): for (cat, l) in zip(categories, C):
l["row_title"] = cat or "?" l["row_title"] = cat if cat is not None else "?"
# #
codes.append("sum") codes.append("sum")
codes.append("sumpercent") codes.append("sumpercent")
# on veut { ADM : ADM, ... }, était peu elegant en python 2.3: # on veut { ADM : ADM, ... }
# titles = {}
# map( lambda x,titles=titles: titles.__setitem__(x[0],x[1]), zip(codes,codes) )
# Version moderne:
titles = {x: x for x in codes} titles = {x: x for x in codes}
# sauf pour
titles.update(LEGENDES_CODES_BUT)
titles["sum"] = "Total" titles["sum"] = "Total"
titles["sumpercent"] = "%" titles["sumpercent"] = "%"
titles["DEM"] = "Dém." # démissions titles["DEM"] = "Dém." # démissions
titles["row_title"] = category_name titles["row_title"] = titles.get(category_name, category_name)
return GenTable( return GenTable(
titles=titles, titles=titles,
columns_ids=codes, columns_ids=codes,
@ -234,14 +261,11 @@ def formsemestre_report(
# #
tab.filename = scu.make_filename("stats " + sem["titreannee"]) tab.filename = scu.make_filename("stats " + sem["titreannee"])
tab.origin = ( tab.origin = f"Généré par {sco_version.SCONAME} le {scu.timedate_human_repr()}"
"Généré par %s le " % sco_version.SCONAME + scu.timedate_human_repr() + "" tab.caption = (
f"Répartition des résultats par {category_name}, semestre {sem['titreannee']}"
) )
tab.caption = "Répartition des résultats par %s, semestre %s" % ( tab.html_caption = f"Répartition des résultats par {category_name}."
category_name,
sem["titreannee"],
)
tab.html_caption = "Répartition des résultats par %s." % category_name
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id) tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
if only_primo: if only_primo:
tab.base_url += "&only_primo=on" tab.base_url += "&only_primo=on"
@ -265,17 +289,32 @@ def formsemestre_report(
def formsemestre_report_counts( def formsemestre_report_counts(
formsemestre_id, formsemestre_id: int,
format="html", format="html",
category="bac", category: str = "bac",
result="codedecision", result: str = None,
allkeys=False, allkeys: bool = False,
only_primo=False, only_primo: bool = False,
): ):
""" """
Tableau comptage avec choix des categories Tableau comptage avec choix des categories
category: attribut en lignes
result: attribut en colonnes
only_primo: restreint aux primo-entrants (= non redoublants)
allkeys: pour le menu du choix de l'attribut en colonnes:
si vrai, toutes les valeurs présentes dans les données
sinon liste prédéfinie (voir ci-dessous)
""" """
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
# Décisions de jury BUT pour les semestres pairs seulement
jury_but_mode = (
formsemestre.formation.is_apc() and formsemestre.semestre_id % 2 == 0
)
if result is None:
result = "statut" if formsemestre.formation.is_apc() else "codedecision"
category_name = category.capitalize() category_name = category.capitalize()
title = "Comptages " + category_name title = "Comptages " + category_name
etuds = formsemestre_etuds_stats(sem, only_primo=only_primo) etuds = formsemestre_etuds_stats(sem, only_primo=only_primo)
@ -311,6 +350,8 @@ def formsemestre_report_counts(
"type_admission", "type_admission",
"boursier_prec", "boursier_prec",
] ]
if jury_but_mode:
keys += ["nb_rcue_valides", "decision_annee"]
keys.sort(key=scu.heterogeneous_sorting_key) keys.sort(key=scu.heterogeneous_sorting_key)
F = [ F = [
"""<form name="f" method="get" action="%s"><p> """<form name="f" method="get" action="%s"><p>
@ -322,7 +363,10 @@ def formsemestre_report_counts(
selected = "selected" selected = "selected"
else: else:
selected = "" selected = ""
F.append('<option value="%s" %s>%s</option>' % (k, selected, k)) F.append(
'<option value="%s" %s>%s</option>'
% (k, selected, LEGENDES_CODES_BUT.get(k, k))
)
F.append("</select>") F.append("</select>")
F.append(' Lignes: <select name="category" onchange="document.f.submit()">') F.append(' Lignes: <select name="category" onchange="document.f.submit()">')
for k in keys: for k in keys:
@ -330,7 +374,10 @@ def formsemestre_report_counts(
selected = "selected" selected = "selected"
else: else:
selected = "" selected = ""
F.append('<option value="%s" %s>%s</option>' % (k, selected, k)) F.append(
'<option value="%s" %s>%s</option>'
% (k, selected, LEGENDES_CODES_BUT.get(k, k))
)
F.append("</select>") F.append("</select>")
if only_primo: if only_primo:
checked = 'checked="1"' checked = 'checked="1"'