ScoDoc/app/scodoc/sco_report_but.py

228 lines
7.8 KiB
Python

# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Emmanuel Viennet emmanuel.viennet@viennet.net
#
##############################################################################
"""Rapport sur réussite en BUT pour enquête 2022
- statistiques decisions
"""
from collections import defaultdict
from flask import request
from app.but import jury_but
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models import FormSemestre
from app.models.etudiants import Identite
import app.scodoc.sco_utils as scu
from app.scodoc import html_sco_header
from app.scodoc import sco_codes_parcours
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc import sco_preferences
import sco_version
from app.scodoc.gen_tables import GenTable
# Titres, ordonnés
INDICATEUR_NAMES = {
"nb_inscr": "Inscrits initiaux",
"nb_dem": "Démissions",
"nb_def": "Défaillants",
"nb_actifs": "Inscrits finals",
"nb_nar": "NAR",
"nb_passe_manque_rcue": "Passant avec RCUE non validé",
"nb_red_avec_rcue": "Redoublant avec au moins un RCUE validé",
"nb_red_sans_rcue": "Redoublant sans avoir validé aucun RCUE",
"nb_valide_tt_rcue": "Validant tous les RCUE de l'année",
}
def formsemestre_but_indicateurs(formsemestre_id: int, format="html"):
"""Page avec tableau indicateurs enquête ADIUT BUT 2022"""
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
indicateurs_by_bac = but_indicateurs_by_bac(formsemestre)
# finalement on fait une table avec en ligne
# les indicateurs, et en colonne les bacs
bacs = sorted(indicateurs_by_bac.keys())
rows = []
for indicateur, titre_indicateur in INDICATEUR_NAMES.items():
row = {bac: indicateurs_by_bac[bac][indicateur] for bac in bacs}
row["titre_indicateur"] = titre_indicateur
rows.append(row)
tab = GenTable(
titles={bac: bac for bac in bacs},
columns_ids=["titre_indicateur"] + bacs,
rows=rows,
html_sortable=False,
preferences=sco_preferences.SemPreferences(formsemestre_id),
filename=scu.make_filename(f"Indicateurs_BUT_{formsemestre.titre_annee()}"),
origin=f"Généré par {sco_version.SCONAME} le {scu.timedate_human_repr()}",
html_caption="Indicateurs BUT annuels.",
base_url=f"{request.base_url}?formsemestre_id={formsemestre_id}",
)
title = "Indicateurs suivi annuel BUT"
t = tab.make_page(
title=f"""<h2 class="formsemestre">{title}</h2>""",
format=format,
with_html_headers=False,
)
if format != "html":
return t
H = [
html_sco_header.sco_header(page_title=title),
t,
"""<p class="help">
</p>""",
html_sco_header.sco_footer(),
]
return "\n".join(H)
def but_indicateurs_by_bac(formsemestre: FormSemestre) -> dict[str:dict]:
"""
L'enquête ADIUT porte sur le nombre de
- inscrits
- ayant validé tous les RCUE
- passant en BUT2 sans avoir validé tous les RCUE
- redoublants avec au moins une RCUE
- redoublants sans aucune RCUE
- NAR
- défaillants
- démissionnaires,
le tout découpé en FI/FA et suivant le type de bac : général/techno/pro/autre.
Le semestre est FI ou FA, donc on ne traite pas ce point.
On suppose qu'on est sur un semestre PAIR de BUT, dont les décisions de jury
ont déjà été saisies.
"""
if not formsemestre.formation.is_apc():
raise ScoValueError(
"Ce rapport doit être généré à partir d'une formation par compétences (BUT)."
)
if formsemestre.semestre_id % 2:
raise ScoValueError("Ce rapport doit être généré à partir d'un semestre PAIR.")
# Le semestre suivant (pour compter les passages)
next_sem_idx = formsemestre.semestre_id + 1
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
etuds = formsemestre.get_inscrits(include_demdef=True)
# Ventilation par bac
etuds_by_bac = defaultdict(list) # bac : etuds
for etud in etuds:
adm = etud.admission.first()
bac = adm.get_bac().abbrev() if adm else "?"
etuds_by_bac[bac].append(etud)
indicateurs_by_bac = {}
for bac in etuds_by_bac:
decisions_annee = {
etud.id: jury_but.DecisionsProposeesAnnee(etud, formsemestre)
for etud in etuds_by_bac[bac]
if res.get_etud_etat(etud.id) == scu.INSCRIT
}
indicateurs_by_bac[bac] = _indicateurs_enquete_but(
res, etuds_by_bac[bac], decisions_annee, next_sem_idx
)
# refait pour tous
decisions_annee = {
etud.id: jury_but.DecisionsProposeesAnnee(etud, formsemestre)
for etud in etuds
if res.get_etud_etat(etud.id) == scu.INSCRIT
}
indicateurs_by_bac["Total"] = _indicateurs_enquete_but(
res, etuds, decisions_annee, next_sem_idx
)
return indicateurs_by_bac
def _indicateurs_enquete_but(
res: NotesTableCompat,
etuds: list[Identite],
decisions_annee: dict[jury_but.DecisionsProposeesAnnee],
next_sem_idx: int,
) -> dict:
"""Calcule les indicateurs de l'enquête ADIUT 2022"""
indicateurs = {
"nb_inscr": len(etuds),
"nb_actifs": len(
[etud for etud in etuds if res.get_etud_etat(etud.id) == scu.INSCRIT]
),
"nb_def": len(
[etud for etud in etuds if res.get_etud_etat(etud.id) == scu.DEF]
),
"nb_dem": len(
[etud for etud in etuds if res.get_etud_etat(etud.id) == scu.DEMISSION]
),
"nb_nar": len(
[
True
for deca in decisions_annee.values()
if deca.code_valide == sco_codes_parcours.NAR
]
),
# Redoublants sans aucune RCUE
"nb_red_sans_rcue": len(
[
True
for deca in decisions_annee.values()
if (deca.nb_rcue_valides == 0)
and (next_sem_idx not in deca.get_autorisations_passage())
]
),
# Redoublants avec au moins une RCUE
"nb_red_avec_rcue": len(
[
True
for deca in decisions_annee.values()
if (deca.nb_rcue_valides > 0)
and (next_sem_idx not in deca.get_autorisations_passage())
]
),
# Passant (en BUT2) sans avoir validé tous les RCUE
"nb_passe_manque_rcue": len(
[
True
for deca in decisions_annee.values()
if (deca.nb_rcue_valides < deca.nb_competences)
and (next_sem_idx in deca.get_autorisations_passage())
]
),
# Ayant validé tous les RCUE
"nb_valide_tt_rcue": len(
[
True
for deca in decisions_annee.values()
if (deca.nb_rcue_valides >= deca.nb_competences)
]
),
}
return indicateurs