diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index 5e4c6fca..f9ff9e52 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -126,6 +126,12 @@ def _build_menu_stats(formsemestre_id): "args": {"formsemestre_id": formsemestre_id}, "enabled": True, }, + { + "title": "Indicateurs de suivi annuel BUT", + "endpoint": "notes.formsemestre_but_indicateurs", + "args": {"formsemestre_id": formsemestre_id}, + "enabled": True, + }, ] diff --git a/app/scodoc/sco_report_but.py b/app/scodoc/sco_report_but.py new file mode 100644 index 00000000..dbf661dd --- /dev/null +++ b/app/scodoc/sco_report_but.py @@ -0,0 +1,221 @@ +# -*- 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"""

{title}

""", + format=format, + with_html_headers=False, + ) + if format != "html": + return t + H = [ + html_sco_header.sco_header(page_title=title), + t, + """

+

""", + 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) + decisions_annee = { + etud.id: jury_but.DecisionsProposeesAnnee(etud, formsemestre) + for etud in etuds + if res.get_etud_etat(etud.id) == scu.INSCRIT + } + # 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: + indicateurs_by_bac[bac] = _indicateurs_enquete_but( + res, etuds_by_bac[bac], decisions_annee, next_sem_idx + ) + 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 diff --git a/app/views/notes.py b/app/views/notes.py index 79b1dde8..cdd0dd99 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -128,6 +128,7 @@ from app.scodoc import sco_prepajury from app.scodoc import sco_pvjury from app.scodoc import sco_recapcomplet from app.scodoc import sco_report +from app.scodoc import sco_report_but from app.scodoc import sco_saisie_notes from app.scodoc import sco_semset from app.scodoc import sco_synchro_etuds @@ -2967,6 +2968,11 @@ sco_publish( sco_report.formsemestre_graph_parcours, Permission.ScoView, ) +sco_publish( + "/formsemestre_but_indicateurs", + sco_report_but.formsemestre_but_indicateurs, + Permission.ScoView, +) sco_publish( "/formsemestre_poursuite_report", sco_poursuite_dut.formsemestre_poursuite_report,