WIP: amelioration form jury BUT

This commit is contained in:
Emmanuel Viennet 2022-06-29 16:30:01 +02:00
parent 581131f54f
commit b0e78b67ea
8 changed files with 125 additions and 73 deletions

View File

@ -36,7 +36,7 @@ from app.scodoc.sco_exceptions import ScoValueError
def formsemestre_saisie_jury_but(
formsemestre2: FormSemestre,
readonly: bool = False,
read_only: bool = False,
selected_etudid: int = None,
mode="jury",
) -> str:
@ -72,7 +72,7 @@ def formsemestre_saisie_jury_but(
)
rows, titles, column_ids = get_table_jury_but(
formsemestre2, readonly=readonly, mode=mode
formsemestre2, read_only=read_only, mode=mode
)
if not rows:
return (
@ -109,7 +109,7 @@ def formsemestre_saisie_jury_but(
"""
)
if (mode == "recap") and not readonly:
if (mode == "recap") and not read_only:
H.append(
f"""
<p><a class="stdlink" href="{url_for(
@ -333,6 +333,7 @@ class RowCollector:
+ ((" " + scu.EMO_WARNING) if deca.nb_rcues_under_8 > 0 else ""),
"col_rcue col_rcues_validables" + klass,
)
self["_rcues_validables_data"] = {"etudid": deca.etud.id}
if len(deca.rcues_annee) > 0:
# permet un tri par nb de niveaux validables + moyenne gen indicative S_pair
if deca.res_pair and deca.etud.id in deca.res_pair.etud_moy_gen:
@ -353,7 +354,7 @@ class RowCollector:
def get_table_jury_but(
formsemestre2: FormSemestre, readonly: bool = False, mode="jury"
formsemestre2: FormSemestre, read_only: bool = False, mode="jury"
) -> 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)
@ -383,7 +384,7 @@ def get_table_jury_but(
"col_code_annee",
)
# --- Le lien de saisie
if not readonly and not mode == "recap":
if mode != "recap":
row.add_cell(
"lien_saisie",
"",
@ -394,7 +395,7 @@ def get_table_jury_but(
etudid=etud.id,
formsemestre_id=formsemestre2.id,
)}" class="stdlink">
{"modif." if deca.code_valide else "saisie"}
{"voir" if read_only else ("modif." if deca.code_valide else "saisie")}
décision</a>
""",
"col_lien_saisie_but",

View File

@ -399,7 +399,7 @@ class ResultatsSemestre(ResultatsCache):
# --- TABLEAU RECAP
def get_table_recap(
self, convert_values=False, include_evaluations=False, modejury=False
self, convert_values=False, include_evaluations=False, mode_jury=False
):
"""Result: tuple avec
- rows: liste de dicts { column_id : value }
@ -550,7 +550,7 @@ class ResultatsSemestre(ResultatsCache):
titles_bot[
f"_{col_id}_target_attrs"
] = f"""title="{ue.titre} S{ue.semestre_idx or '?'}" """
if modejury:
if mode_jury:
# pas d'autre colonnes de résultats
continue
# Bonus (sport) dans cette UE ?
@ -650,7 +650,7 @@ class ResultatsSemestre(ResultatsCache):
elif nb_ues_validables < len(ues_sans_bonus):
row["_ues_validables_class"] += " moy_inf"
row["_ues_validables_order"] = nb_ues_validables # pour tri
if modejury:
if mode_jury:
dec_sem = self.validations.decisions_jury.get(etudid)
jury_code_sem = dec_sem["code"] if dec_sem else ""
idx = add_cell(

View File

@ -111,7 +111,7 @@ def formsemestre_validation_etud_form(
url_tableau = url_for(
"notes.formsemestre_recapcomplet",
scodoc_dept=g.scodoc_dept,
modejury=1,
mode_jury=1,
formsemestre_id=formsemestre_id,
selected_etudid=etudid, # va a la bonne ligne
)
@ -979,7 +979,7 @@ def do_formsemestre_validation_auto(formsemestre_id):
H.append("</ul>")
H.append(
f"""<a href="{url_for('notes.formsemestre_recapcomplet',
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, modejury=1)
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, mode_jury=1)
}">continuer</a>"""
)
H.append(html_sco_header.sco_footer())

View File

@ -501,7 +501,7 @@ def formsemestre_pvjury(formsemestre_id, format="html", publish=True):
from app.but import jury_but_recap
return jury_but_recap.formsemestre_saisie_jury_but(
formsemestre, readonly=True, mode="recap"
formsemestre, read_only=True, mode="recap"
)
# /XXX
footer = html_sco_header.sco_footer()

View File

@ -57,7 +57,7 @@ from app.scodoc import sco_preferences
def formsemestre_recapcomplet(
formsemestre_id=None,
modejury=False,
mode_jury=False,
tabformat="html",
sortcol=None,
xml_with_decisions=False,
@ -78,7 +78,7 @@ def formsemestre_recapcomplet(
xml, json : concaténation de tous les bulletins, au format demandé
pdf : NON SUPPORTE (car tableau trop grand pour générer un pdf utilisable)
modejury: cache modules, affiche lien saisie decision jury
mode_jury: cache modules, affiche lien saisie decision jury
xml_with_decisions: publie décisions de jury dans xml et json
force_publishing: publie les xml et json même si bulletins non publiés
selected_etudid: etudid sélectionné (pour scroller au bon endroit)
@ -91,14 +91,14 @@ def formsemestre_recapcomplet(
if tabformat not in supported_formats:
raise ScoValueError(f"Format non supporté: {tabformat}")
is_file = tabformat in file_formats
modejury = int(modejury)
mode_jury = int(mode_jury)
xml_with_decisions = int(xml_with_decisions)
force_publishing = int(force_publishing)
data = _do_formsemestre_recapcomplet(
formsemestre_id,
format=tabformat,
modejury=modejury,
mode_jury=mode_jury,
sortcol=sortcol,
xml_with_decisions=xml_with_decisions,
force_publishing=force_publishing,
@ -123,9 +123,9 @@ def formsemestre_recapcomplet(
<input type="hidden" name="formsemestre_id" value="{formsemestre_id}"></input>
"""
)
if modejury:
if mode_jury:
H.append(
f'<input type="hidden" name="modejury" value="{modejury}"></input>'
f'<input type="hidden" name="mode_jury" value="{mode_jury}"></input>'
)
H.append(
'<select name="tabformat" onchange="document.f.submit()" class="noprint">'
@ -163,7 +163,7 @@ def formsemestre_recapcomplet(
)
if sco_permissions_check.can_validate_sem(formsemestre_id):
H.append("<p>")
if modejury:
if mode_jury:
H.append(
f"""<a class="stdlink" href="{url_for('notes.formsemestre_validation_auto',
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id)
@ -172,7 +172,7 @@ def formsemestre_recapcomplet(
else:
H.append(
f"""<a class="stdlink" href="{url_for('notes.formsemestre_recapcomplet',
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, modejury=1)
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, mode_jury=1)
}">Saisie des décisions du jury</a>"""
)
H.append("</p>")
@ -196,7 +196,7 @@ def _do_formsemestre_recapcomplet(
formsemestre_id=None,
format="html", # html, xml, xls, xlsall, json
xml_nodate=False, # format XML sans dates (sert pour debug cache: comparaison de XML)
modejury=False, # saisie décisions jury
mode_jury=False, # saisie décisions jury
sortcol=None, # indice colonne a trier dans table T
xml_with_decisions=False,
force_publishing=True,
@ -215,7 +215,7 @@ def _do_formsemestre_recapcomplet(
formsemestre,
res,
include_evaluations=(format == "evals"),
modejury=modejury,
mode_jury=mode_jury,
filename=filename,
selected_etudid=selected_etudid,
)
@ -368,34 +368,34 @@ def gen_formsemestre_recapcomplet_html(
formsemestre: FormSemestre,
res: NotesTableCompat,
include_evaluations=False,
modejury=False,
mode_jury=False,
filename="",
selected_etudid=None,
):
"""Construit table recap pour le BUT
Cache le résultat pour le semestre (sauf en mode jury).
Si modejury, cache colonnes modules et affiche un lien vers la saisie de la décision de jury
Si mode_jury, cache colonnes modules et affiche un lien vers la saisie de la décision de jury
Return: data, filename
data est une chaine, le <div>...</div> incluant le tableau.
"""
table_html = None
if not (modejury or selected_etudid):
if not (mode_jury or selected_etudid):
if include_evaluations:
table_html = sco_cache.TableRecapWithEvalsCache.get(formsemestre.id)
else:
table_html = sco_cache.TableRecapCache.get(formsemestre.id)
if modejury or (table_html is None):
if mode_jury or (table_html is None):
table_html = _gen_formsemestre_recapcomplet_html(
formsemestre,
res,
include_evaluations,
modejury,
mode_jury,
filename,
selected_etudid=selected_etudid,
)
if not modejury:
if not mode_jury:
if include_evaluations:
sco_cache.TableRecapWithEvalsCache.set(formsemestre.id, table_html)
else:
@ -408,13 +408,15 @@ def _gen_formsemestre_recapcomplet_html(
formsemestre: FormSemestre,
res: NotesTableCompat,
include_evaluations=False,
modejury=False,
mode_jury=False,
filename: str = "",
selected_etudid=None,
) -> str:
"""Génère le html"""
rows, footer_rows, titles, column_ids = res.get_table_recap(
convert_values=True, include_evaluations=include_evaluations, modejury=modejury
convert_values=True,
include_evaluations=include_evaluations,
mode_jury=mode_jury,
)
if not rows:
return (
@ -423,7 +425,7 @@ def _gen_formsemestre_recapcomplet_html(
H = [
f"""<div class="table_recap"><table class="table_recap {
'apc' if formsemestre.formation.is_apc() else 'classic'
} {'jury' if modejury else ''}"
} {'jury' if mode_jury else ''}"
data-filename="{filename}">"""
]
# header

View File

@ -1097,6 +1097,10 @@ def gen_cell(key: str, row: dict, elt="td", with_col_class=False):
if with_col_class:
klass = key + " " + klass
attrs = f'class="{klass}"' if klass else ""
data = row.get(f"_{key}_data") # dict
if data:
for k in data:
attrs += f' data-{k}="{data[k]}"'
order = row.get(f"_{key}_order")
if order:
attrs += f' data-order="{order}"'

View File

@ -65,6 +65,13 @@
font-weight: bold;
}
.but_navigation {
padding-top: 16px;
grid-column: 1 / -1;
display: flex;
justify-content: space-between;
}
div.but_section_annee {
margin-bottom: 10px;
}
@ -73,9 +80,10 @@ div.but_settings {
margin-top: 16px;
}
span.but_explanation {
.but_explanation {
color: blueviolet;
font-style: italic;
padding-top: 12px;
}
select:disabled {

View File

@ -31,7 +31,7 @@ Module notes: issu de ScoDoc7 / ZNotes.py
Emmanuel Viennet, 2021
"""
import html
from email.policy import default
from operator import itemgetter
import time
from xml.etree import ElementTree
@ -58,14 +58,12 @@ from app import db
from app import models
from app.models import ScolarNews
from app.auth.models import User
from app.but import apc_edit_ue, bulletin_but, jury_but_recap
from app.but import apc_edit_ue, jury_but_recap
from app.decorators import (
scodoc,
scodoc7func,
permission_required,
permission_required_compat_scodoc7,
admin_required,
login_required,
)
from app.views import notes_bp as bp
@ -2217,22 +2215,24 @@ def formsemestre_validation_etud_manu(
# --- Jurys BUT
@bp.route(
"/formsemestre_validation_but/<int:formsemestre_id>/<int:etudid>",
"/formsemestre_validation_but/<int:formsemestre_id>/<etudid>",
methods=["GET", "POST"],
)
@scodoc
@permission_required(Permission.ScoView)
def formsemestre_validation_but(formsemestre_id: int, etudid: int):
def formsemestre_validation_but(
formsemestre_id: int,
etudid: int,
):
"Form. saisie décision jury semestre BUT"
if not sco_permissions_check.can_validate_sem(formsemestre_id):
return scu.confirm_dialog(
message=f"<p>Opération non autorisée pour {current_user}</h2>",
dest_url=url_for(
"notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
),
)
# la route ne donne pas le type d'etudid pour pouvoir construire des URLs
# provisoires avec NEXT et PREV
try:
etudid = int(etudid)
except:
abort(404, "invalid etudid")
read_only = not sco_permissions_check.can_validate_sem(formsemestre_id)
H = [
html_sco_header.sco_header(
page_title="Validation BUT",
@ -2270,8 +2270,9 @@ def formsemestre_validation_but(formsemestre_id: int, etudid: int):
if len(deca.rcues_annee) == 0:
raise ScoValueError("année incomplète: pas de jury BUT annuel possible")
if request.method == "POST":
deca.record_form(request.form)
flash("codes enregistrés")
if not read_only:
deca.record_form(request.form)
flash("codes enregistrés")
return flask.redirect(
url_for(
"notes.formsemestre_validation_but",
@ -2280,7 +2281,7 @@ def formsemestre_validation_but(formsemestre_id: int, etudid: int):
etudid=etudid,
)
)
if deca.code_valide:
if deca.code_valide and not read_only:
erase_span = f"""<a href="{
url_for("notes.formsemestre_jury_but_erase",
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id,
@ -2313,7 +2314,7 @@ def formsemestre_validation_but(formsemestre_id: int, etudid: int):
<span>({'non ' if deca.code_valide is None else ''}enregistrée)</span>
<span>{erase_span}</span>
</div>
<span class="but_explanation">{deca.explanation}</span>
<div class="but_explanation">{deca.explanation}</div>
</div>
<div><b>Niveaux de compétences et unités d'enseignement :</b></div>
<div class="but_annee">
@ -2339,6 +2340,7 @@ def formsemestre_validation_but(formsemestre_id: int, etudid: int):
dec_rcue.rcue.ue_1,
dec_rcue.rcue.moy_ue_1,
deca.decisions_ues[dec_rcue.rcue.ue_1.id],
disabled=read_only,
)
)
# Semestre pair
@ -2347,6 +2349,7 @@ def formsemestre_validation_but(formsemestre_id: int, etudid: int):
dec_rcue.rcue.ue_2,
dec_rcue.rcue.moy_ue_2,
deca.decisions_ues[dec_rcue.rcue.ue_2.id],
disabled=read_only,
)
)
# RCUE
@ -2366,24 +2369,58 @@ def formsemestre_validation_but(formsemestre_id: int, etudid: int):
)
H.append("</div>") # but_annee
H.append(
f"""<div class="but_settings">
<input type="checkbox" onchange="enable_manual_codes(this)">
<em>permettre la saisie manuelles des codes d'année et de niveaux.
Dans ce cas, il vous revient de vous assurer de la cohérence entre
vos codes d'UE/RCUE/Année !</em>
</input>
</div>
if read_only:
H.append(
"""<div class="but_explanation">Vous n'avez pas la permission de modifier ces décisions.
Les champs entourés en vert sont enregistrés.</div>"""
)
else:
H.append(
f"""<div class="but_settings">
<input type="checkbox" onchange="enable_manual_codes(this)">
<em>permettre la saisie manuelles des codes d'année et de niveaux.
Dans ce cas, il vous revient de vous assurer de la cohérence entre
vos codes d'UE/RCUE/Année !</em>
</input>
</div>
<div class="but_buttons">
<span><input type="submit" value="Enregistrer ces décisions"></span>
<span><a href="{url_for(
"notes.formsemestre_saisie_jury", scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id, selected_etudid=etud.id
)}">retour à la liste</a></span>
<div class="but_buttons">
<input type="submit" value="Enregistrer ces décisions">
</div>
"""
)
H.append(
f"""<div class="but_navigation">
<div class="back_list">
<a href="{url_for("notes.formsemestre_saisie_jury", scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id, selected_etudid=etud.id
)}" class="stdlink">retour à la liste</a>
</div>
"""
</div>"""
)
# # --- Navigation
# prev = f"""<a href="{url_for(
# "notes.formsemestre_validation_but", scodoc_dept=g.scodoc_dept,
# formsemestre_id=formsemestre_id, etudid="PREV"
# )} class="stdlink"">précédent</a>
# """
# H.append(
# f"""
# <div class="but_navigation">
# <div class="prev">
# {prev}
# </div>
# <div class="back_list">
# <a href="{url_for(
# "notes.formsemestre_saisie_jury", scodoc_dept=g.scodoc_dept,
# formsemestre_id=formsemestre_id, selected_etudid=etud.id
# )}" class="stdlink">retour à la liste</a>
# </div>
# <div class="next">
# next TODO
# </div>
# </div>"""
# )
H.append("</form>") # but_annee
H.append(
@ -2425,7 +2462,7 @@ def _gen_but_select(
def _gen_but_niveau_ue(
ue: UniteEns, moy_ue: float, dec_ue: jury_but.DecisionsProposeesUE
ue: UniteEns, moy_ue: float, dec_ue: jury_but.DecisionsProposeesUE, disabled=False
):
return f"""<div class="but_niveau_ue {
'recorded' if dec_ue.code_valide is not None else ''}
@ -2435,7 +2472,7 @@ def _gen_but_niveau_ue(
<div class="but_code">{
_gen_but_select("code_ue_"+str(ue.id),
dec_ue.codes,
dec_ue.code_valide
dec_ue.code_valide, disabled=disabled
)
}</div>
</div>"""
@ -2640,18 +2677,18 @@ def formsemestre_saisie_jury(formsemestre_id: int, selected_etudid: int = None):
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)
read_only = 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, selected_etudid=selected_etudid
formsemestre, read_only, selected_etudid=selected_etudid
)
return redirect(
url_for(
"notes.formsemestre_recapcomplet",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
modejury=1,
mode_jury=1,
)
)
@ -2662,14 +2699,14 @@ def formsemestre_saisie_jury(formsemestre_id: int, selected_etudid: int = None):
@scodoc7func
def formsemestre_jury_but_recap(formsemestre_id: int, selected_etudid: int = None):
"""Tableau affichage des codes"""
readonly = not sco_permissions_check.can_validate_sem(formsemestre_id)
read_only = not sco_permissions_check.can_validate_sem(formsemestre_id)
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
if not (formsemestre.formation.is_apc() and formsemestre.semestre_id % 2 == 0):
raise ScoValueError(
"formsemestre_jury_but_recap: réservé aux semestres pairs de BUT"
)
return jury_but_recap.formsemestre_saisie_jury_but(
formsemestre, readonly=readonly, selected_etudid=selected_etudid, mode="recap"
formsemestre, read_only=read_only, selected_etudid=selected_etudid, mode="recap"
)