diff --git a/app/api/jury.py b/app/api/jury.py index 6a911c6c..a8f0c92b 100644 --- a/app/api/jury.py +++ b/app/api/jury.py @@ -1,40 +1,35 @@ -#################################################### Jury ############################################################# -# from flask import jsonify +############################################################################## +# ScoDoc +# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# See LICENSE +############################################################################## -# from app import models -# from app.api import api_bp as bp -# from app.api.errors import error_response -# from app.scodoc.sco_prepajury import feuille_preparation_jury -# from app.scodoc.sco_pvjury import formsemestre_pvjury +""" + ScoDoc 9 API : jury +""" + +from flask import g, jsonify, request +from flask_login import login_required + +import app +from app import db, log +from app.api import api_bp as bp, api_web_bp +from app.decorators import scodoc, permission_required +from app.api.errors import error_response +from app.but import jury_but_recap +from app.models import FormSemestre, FormSemestreInscription, Identite +from app.scodoc.sco_permissions import Permission -# # @bp.route("/jury/formsemestre//preparation_jury", methods=["GET"]) -# # @token_permission_required(Permission.?) -# def jury_preparation(formsemestre_id: int): -# """ -# Retourne la feuille de préparation du jury - -# formsemestre_id : l'id d'un formsemestre -# """ -# # Fonction utilisée : app.scodoc.sco_prepajury.feuille_preparation_jury() - -# # Utilisation de la fonction feuille_preparation_jury -# prepa_jury = feuille_preparation_jury(formsemestre_id) - -# return error_response(501, message="Not implemented") - - -# # @bp.route("/jury/formsemestre//decisions_jury", methods=["GET"]) -# # @token_permission_required(Permission.?) -# def jury_decisions(formsemestre_id: int): -# """ -# Retourne les décisions du jury suivant un formsemestre donné - -# formsemestre_id : l'id d'un formsemestre -# """ -# # Fonction utilisée : app.scodoc.sco_pvjury.formsemestre_pvjury() - -# # Utilisation de la fonction formsemestre_pvjury -# decision_jury = formsemestre_pvjury(formsemestre_id) - -# return error_response(501, message="Not implemented") +@bp.route("/formsemestre//decisions_jury") +@api_web_bp.route("/formsemestre//decisions_jury") +@login_required +@scodoc +@permission_required(Permission.ScoView) +def decisions_jury(formsemestre_id: int): + """Décisions du jury des étudiants du formsemestre.""" + # APC, pair: + formsemestre: FormSemestre = FormSemestre.query.get(formsemestre_id) + app.set_sco_dept(formsemestre.departement.acronym) + rows = jury_but_recap.get_jury_but_results(formsemestre) + return jsonify(rows) diff --git a/app/but/jury_but_recap.py b/app/but/jury_but_recap.py index ada5280b..20d560e1 100644 --- a/app/but/jury_but_recap.py +++ b/app/but/jury_but_recap.py @@ -21,7 +21,7 @@ from app.comp.res_but import ResultatsSemestreBUT from app.comp import res_sem from app.models.etudiants import Identite from app.models.formsemestre import FormSemestre - +from app.scodoc import html_sco_header from app.scodoc.sco_codes_parcours import ( BUT_BARRE_RCUE, BUT_BARRE_UE, @@ -29,7 +29,7 @@ from app.scodoc.sco_codes_parcours import ( BUT_RCUE_SUFFISANT, ) from app.scodoc import sco_formsemestre_status -from app.scodoc import html_sco_header +from app.scodoc import sco_pvjury from app.scodoc import sco_utils as scu from app.scodoc.sco_exceptions import ScoValueError @@ -71,7 +71,7 @@ def formsemestre_saisie_jury_but( """ ) - rows, titles, column_ids = get_table_jury_but( + rows, titles, column_ids = get_jury_but_table( formsemestre2, read_only=read_only, mode=mode ) if not rows: @@ -255,7 +255,9 @@ class RowCollector: self.column_classes[col_id] = column_class self.idx += 1 - def add_etud_cells(self, etud: Identite, formsemestre: FormSemestre): + def add_etud_cells( + self, etud: Identite, formsemestre: FormSemestre, with_links=True + ): "Les cells code, nom, prénom etc." # --- Codes (seront cachés, mais exportés en excel) self.add_cell("etudid", "etudid", etud.id, "codes") @@ -266,16 +268,17 @@ class RowCollector: self["_nom_disp_order"] = etud.sort_key self.add_cell("prenom", "Prénom", etud.prenom, "identite_detail") self.add_cell("nom_short", "Nom", etud.nom_short, "identite_court") - self["_nom_short_order"] = etud.sort_key - self["_nom_short_target"] = url_for( - "notes.formsemestre_bulletinetud", - scodoc_dept=g.scodoc_dept, - formsemestre_id=formsemestre.id, - etudid=etud.id, - ) - self["_nom_short_target_attrs"] = f'class="etudinfo" id="{etud.id}"' - self["_nom_disp_target"] = self["_nom_short_target"] - self["_nom_disp_target_attrs"] = self["_nom_short_target_attrs"] + if with_links: + self["_nom_short_order"] = etud.sort_key + self["_nom_short_target"] = url_for( + "notes.formsemestre_bulletinetud", + scodoc_dept=g.scodoc_dept, + formsemestre_id=formsemestre.id, + etudid=etud.id, + ) + self["_nom_short_target_attrs"] = f'class="etudinfo" id="{etud.id}"' + self["_nom_disp_target"] = self["_nom_short_target"] + self["_nom_disp_target_attrs"] = self["_nom_short_target_attrs"] self.last_etud_cell_idx = self.idx def add_ue_cells(self, dec_ue: DecisionsProposeesUE): @@ -372,8 +375,8 @@ class RowCollector: ] = f"{deca.nb_validables:04d}-00000-{deca.etud.sort_key}" -def get_table_jury_but( - formsemestre2: FormSemestre, read_only: bool = False, mode="jury" +def get_jury_but_table( + formsemestre2: FormSemestre, read_only: bool = False, mode="jury", with_links=True ) -> tuple[list[dict], list[str], list[str]]: """Construit la table des résultats annuels pour le jury BUT""" res2: ResultatsSemestreBUT = res_sem.load_formsemestre_results(formsemestre2) @@ -384,7 +387,7 @@ def get_table_jury_but( etud: Identite = Identite.query.get(etudid) deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre2) row = RowCollector(titles=titles, column_classes=column_classes) - row.add_etud_cells(etud, formsemestre2) + row.add_etud_cells(etud, formsemestre2, with_links=with_links) row.idx = 100 # laisse place pour les colonnes de groupes # --- Nombre de niveaux row.add_nb_rcues_cell(deca) @@ -415,7 +418,7 @@ def get_table_jury_but( "col_code_annee", ) # --- Le lien de saisie - if mode != "recap": + if mode != "recap" and with_links: row.add_cell( "lien_saisie", "", @@ -441,3 +444,75 @@ def get_table_jury_but( column_ids.sort(key=lambda col_id: titles.get("_" + col_id + "_col_order", 1000)) rows_dict.sort(key=lambda row: row["_nom_disp_order"]) return rows_dict, titles, column_ids + + +def get_jury_but_results(formsemestre: FormSemestre) -> list[dict]: + """Liste des résultats jury BUT sous forme de dict, pour API""" + dpv = sco_pvjury.dict_pvjury(formsemestre.id) + rows = [] + for etudid in formsemestre.etuds_inscriptions: + rows.append(get_jury_but_etud_result(formsemestre, dpv, etudid)) + return rows + + +def get_jury_but_etud_result( + formsemestre: FormSemestre, dpv: dict, etudid: int +) -> dict: + """Résultats de jury d'un étudiant sur un semestre pair de BUT""" + etud: Identite = Identite.query.get(etudid) + dec_etud = dpv["decisions_dict"][etudid] + if formsemestre.formation.is_apc(): + deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre) + else: + deca = None + row = { + "etudid": etud.id, + "code_nip": etud.code_nip, + "code_ine": etud.code_ine, + "is_apc": dpv["is_apc"], # BUT ou classic ? + "etat": dec_etud["etat"], # I ou D ou DEF + "nb_competences": deca.nb_competences if deca else 0, + } + # --- Les RCUEs + rcue_list = [] + if deca: + for rcue in deca.rcues_annee: + dec_rcue = deca.dec_rcue_by_ue.get(rcue.ue_1.id) + if dec_rcue is not None: # None si l'UE n'est pas associée à un niveau + dec_ue1 = deca.decisions_ues[rcue.ue_1.id] + dec_ue2 = deca.decisions_ues[rcue.ue_2.id] + rcue_dict = { + "ue_1": { + "ue_id": rcue.ue_1.id, + "moy": dec_ue1.moy_ue, + "code": dec_ue1.code_valide, + }, + "ue_2": { + "ue_id": rcue.ue_2.id, + "moy": dec_ue2.moy_ue, + "code": dec_ue2.code_valide, + }, + "moy": rcue.moy_rcue, + "code": dec_rcue.code_valide, + } + rcue_list.append(rcue_dict) + row["rcues"] = rcue_list + # --- Les UEs + ue_list = [] + if dec_etud["decisions_ue"]: + for ue_id, ue_dec in dec_etud["decisions_ue"].items(): + ue_dict = { + "ue_id": ue_id, + "code": ue_dec["code"], + "ects": ue_dec["ects"], + } + ue_list.append(ue_dict) + row["ues"] = ue_list + # --- Le semestre (pour les formations classiques) + if dec_etud["decision_sem"]: + row["semestre"] = {"code": dec_etud["decision_sem"].get("code")} + else: + row["semestre"] = {} # APC, ... + # --- Autorisations + row["autorisations"] = dec_etud["autorisations"] + return row diff --git a/tests/api/exemple-api-basic.py b/tests/api/exemple-api-basic.py index 1c9b67cb..bd624806 100644 --- a/tests/api/exemple-api-basic.py +++ b/tests/api/exemple-api-basic.py @@ -207,7 +207,7 @@ POST_JSON(f"/group/{group_1}/remove_etudiant/{etudid}") # 6- affecte au groupe G2 partition = GET(f"/partition/{partition_id}") assert len(partition["groups"]) == 3 -group_2 = [g for g in partition["groups"].values() if g["name"] == "G2"][0]["id"] +group_2 = [g for g in partition["groups"].values() if g["group_name"] == "G2"][0]["id"] POST_JSON(f"/group/{group_2}/set_etudiant/{etudid}") # 7- Membres du groupe @@ -216,7 +216,7 @@ assert len(etuds_g2) == 1 assert etuds_g2[0]["id"] == etudid # 8- Ordres des groupes -group_3 = [g for g in partition["groups"].values() if g["name"] == "G3"][0]["id"] +group_3 = [g for g in partition["groups"].values() if g["group_name"] == "G3"][0]["id"] POST_JSON( f"/partition/{partition_id}/groups/order",