WIP: jury BUT: début table recap (inachevée)

This commit is contained in:
Emmanuel Viennet 2022-06-23 06:33:03 +02:00
parent 0939feb9fc
commit cdef38b62b
4 changed files with 219 additions and 10 deletions

View File

@ -188,14 +188,21 @@ class DecisionsProposeesAnnee(DecisionsProposees):
"le 1er semestre de l'année scolaire considérée (S1, S3, S5)" "le 1er semestre de l'année scolaire considérée (S1, S3, S5)"
self.formsemestre_pair = formsemestre_pair self.formsemestre_pair = formsemestre_pair
"le second formsemestre de la même année scolaire (S2, S4, S6)" "le second formsemestre de la même année scolaire (S2, S4, S6)"
self.annee_but = formsemestre_impair.semestre_id // 2 + 1 self.annee_but = (
formsemestre_impair.semestre_id // 2 + 1
if formsemestre_impair
else formsemestre_pair.semestre_id // 2
)
"le rang de l'année dans le BUT: 1, 2, 3" "le rang de l'année dans le BUT: 1, 2, 3"
assert self.annee_but in (1, 2, 3) assert self.annee_but in (1, 2, 3)
if self.formsemestre_impair is not None:
self.validation = ApcValidationAnnee.query.filter_by( self.validation = ApcValidationAnnee.query.filter_by(
etudid=self.etud.id, etudid=self.etud.id,
formsemestre_id=formsemestre_impair.id, formsemestre_id=formsemestre_impair.id,
ordre=self.annee_but, ordre=self.annee_but,
).first() ).first()
else:
self.validation = None
if self.validation is not None: if self.validation is not None:
self.code_valide = self.validation.code self.code_valide = self.validation.code
self.parcour = None self.parcour = None

179
app/but/jury_but_recap.py Normal file
View File

@ -0,0 +1,179 @@
##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""Jury BUT: table recap annuelle et liens saisie
"""
import time
from flask import g, url_for
from app import db
from app.but import jury_but
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.models.ues import UniteEns
from app.scodoc import sco_formsemestre_status
from app.scodoc import html_sco_header
from app.scodoc import sco_utils as scu
from app.scodoc.sco_exceptions import ScoException, ScoValueError
class RowCollector:
def __init__(self, cells: dict = None, titles: dict = None):
self.titles = titles
self.row = cells or {} # col_id : str
self.idx = 0
def add_cell(
self,
col_id: str,
title: str,
content: str,
classes: str = "",
idx: int = None,
):
"Add a row to our table. classes is a list of css class names"
self.idx = idx if idx is not None else self.idx
self.row[col_id] = content
if classes:
self.row[f"_{col_id}_class"] = classes + f" c{self.idx}"
if not col_id in self.titles:
self.titles[col_id] = title
self.titles[f"_{col_id}_col_order"] = self.idx
if classes:
self.titles[f"_{col_id}_class"] = classes
self.idx += 1
def __setitem__(self, key, value):
self.row[key] = value
def __getitem__(self, key):
return self.row[key]
def formsemestre_saisie_jury_but(
formsemestre2: FormSemestre, readonly: bool = False
) -> str:
"""formsemestre est un semestre PAIR
Si readonly, ne montre pas le lien "saisir la décision"
=> page html complète
"""
# Quick & Dirty
# pour chaque etud de res2 trié
# S1: UE1, ..., UEn
# S2: UE1, ..., UEn
#
# UE1_s1, UE1_s2, moy_rcue, UE2... , Nbrcue_validables, Nbrcue<8, passage_de_droit, valide_moitie_rcue
#
# Pour chaque etud de res2 trié
# DecisionsProposeesAnnee(etud, formsemestre2)
# Pour le 1er etud, faire un check_ues_ready_jury(self) -> page d'erreur
# -> rcue .ue_1, .ue_2 -> stroe moy ues, rcue.moy_rcue, etc
if formsemestre2.semestre_id % 2 != 0:
raise ScoValueError("Cette page ne fonctionne que sur les semestres pairs")
rows, titles, column_ids = get_table_jury_but(formsemestre2, readonly=readonly)
if not rows:
return (
'<div class="table_recap"><div class="message">aucun étudiant !</div></div>'
)
filename = scu.sanitize_filename(
f"""jury-but-{formsemestre2.titre_num()}-{time.strftime("%Y-%m-%d")}"""
)
table_html = build_table_jury_but_html(filename, rows, titles, column_ids)
H = [
html_sco_header.sco_header(
page_title=f"{formsemestre2.sem_modalite()}: moyennes",
no_side_bar=True,
init_qtip=True,
javascripts=["js/etud_info.js", "js/table_recap.js"],
),
sco_formsemestre_status.formsemestre_status_head(
formsemestre_id=formsemestre2.id
),
]
H.append(table_html)
H.append(html_sco_header.sco_footer())
return "\n".join(H)
def build_table_jury_but_html(filename: str, rows, titles, column_ids) -> str:
"""assemble la table html"""
footer_rows = [] # inutile pour l'instant, à voir XXX
selected_etudid = None # inutile pour l'instant, à voir XXX
H = [
f"""<div class="table_recap"><table class="table_recap apc jury"
data-filename="{filename}">"""
]
# header
H.append(
f"""
<thead>
{scu.gen_row(column_ids, titles, "th")}
</thead>
"""
)
# body
H.append("<tbody>")
for row in rows:
H.append(f"{scu.gen_row(column_ids, row, selected_etudid=selected_etudid)}\n")
H.append("</tbody>\n")
# footer
H.append("<tfoot>")
idx_last = len(footer_rows) - 1
for i, row in enumerate(footer_rows):
H.append(f'{scu.gen_row(column_ids, row, "th" if i == idx_last else "td")}\n')
H.append(
"""
</tfoot>
</table>
</div>
"""
)
return "".join(H)
def get_table_jury_but(
formsemestre2: FormSemestre, readonly: bool = False
) -> 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)
rcues = []
titles = {} # column_id : title
rows = []
for etudid in formsemestre2.etuds_inscriptions:
etud = Identite.query.get(etudid)
deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre2)
row = RowCollector(titles=titles)
# --- Codes (seront cachés, mais exportés en excel)
row.add_cell("etudid", "etudid", etudid, "codes")
row.add_cell("code_nip", "code_nip", etud.code_nip or "", "codes")
# --- Identité étudiant (adapté de res_comon/get_table_recap, à factoriser)
row.add_cell("civilite_str", "Civ.", etud.civilite_str, "identite_detail")
row.add_cell("nom_disp", "Nom", etud.nom_disp(), "identite_detail")
row["_nom_disp_order"] = etud.sort_key
row.add_cell("prenom", "Prénom", etud.prenom, "identite_detail")
row.add_cell("nom_short", "Nom", etud.nom_short, "identite_court")
row["_nom_short_order"] = etud.sort_key
row["_nom_short_target"] = url_for(
"notes.formsemestre_bulletinetud",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre2.id,
etudid=etudid,
)
row["_nom_short_target_attrs"] = f'class="etudinfo" id="{etudid}"'
row["_nom_disp_target"] = row["_nom_short_target"]
row["_nom_disp_target_attrs"] = row["_nom_short_target_attrs"]
rows.append(row.row)
column_ids = [title for title in titles if not title.startswith("_")]
column_ids.sort(key=lambda col_id: titles.get("_" + col_id + "_col_order", 1000))
return rows, titles, column_ids

View File

@ -410,10 +410,9 @@ def formsemestre_status_menubar(sem):
}, },
{ {
"title": "Saisie des décisions du jury", "title": "Saisie des décisions du jury",
"endpoint": "notes.formsemestre_recapcomplet", "endpoint": "notes.formsemestre_saisie_jury",
"args": { "args": {
"formsemestre_id": formsemestre_id, "formsemestre_id": formsemestre_id,
"modejury": 1,
}, },
"enabled": sco_permissions_check.can_validate_sem(formsemestre_id), "enabled": sco_permissions_check.can_validate_sem(formsemestre_id),
}, },

View File

@ -40,7 +40,6 @@ import flask
from flask import abort, flash, jsonify, redirect, render_template, url_for from flask import abort, flash, jsonify, redirect, render_template, url_for
from flask import current_app, g, request from flask import current_app, g, request
from flask_login import current_user from flask_login import current_user
from werkzeug.utils import redirect
from app.but import jury_but from app.but import jury_but
from app.comp import res_sem from app.comp import res_sem
@ -57,7 +56,7 @@ from app import db
from app import models from app import models
from app.models import ScolarNews from app.models import ScolarNews
from app.auth.models import User from app.auth.models import User
from app.but import apc_edit_ue, bulletin_but from app.but import apc_edit_ue, bulletin_but, jury_but_recap
from app.decorators import ( from app.decorators import (
scodoc, scodoc,
scodoc7func, scodoc7func,
@ -2535,6 +2534,31 @@ def formsemestre_validation_suppress_etud(
# ------------- PV de JURY et archives # ------------- PV de JURY et archives
sco_publish("/formsemestre_pvjury", sco_pvjury.formsemestre_pvjury, Permission.ScoView) sco_publish("/formsemestre_pvjury", sco_pvjury.formsemestre_pvjury, Permission.ScoView)
@bp.route("/formsemestre_saisie_jury")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func
def formsemestre_saisie_jury(formsemestre_id: int):
"""Page de saisie: liste des étudiants et lien vers page jury
en semestres pairs de BUT, table spécifique avec l'année
sinon, redirect vers page recap en mode jury
"""
readonly = not sco_permissions_check.can_validate_sem(formsemestre_id)
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
if formsemestre.formation.is_apc() and formsemestre.semestre_id % 2 == 0:
return jury_but_recap.formsemestre_saisie_jury_but(formsemestre, readonly)
return redirect(
url_for(
"notes.formsemestre_recapcomplet",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
modejury=1,
)
)
sco_publish( sco_publish(
"/formsemestre_lettres_individuelles", "/formsemestre_lettres_individuelles",
sco_pvjury.formsemestre_lettres_individuelles, sco_pvjury.formsemestre_lettres_individuelles,