Adaptation multi-select groupes

This commit is contained in:
Emmanuel Viennet 2024-08-25 07:23:36 +02:00
parent 65e2ead1b4
commit 6c044dd4dd
13 changed files with 216 additions and 113 deletions

View File

@ -40,6 +40,7 @@ def TrivialFormulator(
submitbuttonattributes=None,
top_buttons=False, # place buttons at top of form
bottom_buttons=True, # buttons after form
html_head_markup="",
html_foot_markup="",
readonly=False,
is_submitted=False,
@ -116,6 +117,7 @@ def TrivialFormulator(
submitbuttonattributes=submitbuttonattributes or [],
top_buttons=top_buttons,
bottom_buttons=bottom_buttons,
html_head_markup=html_head_markup,
html_foot_markup=html_foot_markup,
readonly=readonly,
is_submitted=is_submitted,
@ -152,6 +154,7 @@ class TF(object):
submitbuttonattributes=None,
top_buttons=False, # place buttons at top of form
bottom_buttons=True, # buttons after form
html_head_markup="", # html snippet put at the beginning, just before the table
html_foot_markup="", # html snippet put at the end, just after the table
readonly=False,
is_submitted=False,
@ -178,6 +181,7 @@ class TF(object):
self.submitbuttonattributes = submitbuttonattributes or []
self.top_buttons = top_buttons
self.bottom_buttons = bottom_buttons
self.html_head_markup = html_head_markup
self.html_foot_markup = html_foot_markup
self.title = title
self.after_table = after_table
@ -469,6 +473,7 @@ class TF(object):
if self.top_buttons:
R.append(buttons_markup + "<p></p>")
R.append(self.before_table.format(title=self.title))
R.append(self.html_head_markup)
R.append('<table class="tf">')
for field, descr in self.formdescription:
if descr.get("readonly", False):

View File

@ -289,7 +289,7 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement.
]
menu_choix_groupe = (
"""<div class="group_ids_sel_menu">Groupes d'étudiants à lister: """
+ sco_groups_view.menu_groups_choice(groups_infos)
+ sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True)
+ """(pour les PV et lettres)</div>"""
)

View File

@ -732,7 +732,9 @@ def evaluation_describe(evaluation_id="", edit_in_place=True, link_saisie=True)
H.append(
f"""
<a style="margin-left: 12px;" class="stdlink" href="{url_for(
"notes.saisie_notes", scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id)
"notes.form_saisie_notes",
scodoc_dept=g.scodoc_dept,
evaluation_id=evaluation_id)
}">saisie des notes</a>
"""
)

View File

@ -72,7 +72,7 @@ def moduleimpl_evaluation_menu(evaluation: Evaluation, nbnotes: int = 0) -> str:
menu_eval = [
{
"title": "Saisir les notes",
"endpoint": "notes.saisie_notes",
"endpoint": "notes.form_saisie_notes",
"args": {
"evaluation_id": evaluation_id,
},
@ -745,7 +745,7 @@ def _ligne_evaluation(
)
if can_edit_notes:
H.append(
f"""<a class="smallbutton" href="{url_for('notes.saisie_notes',
f"""<a class="smallbutton" href="{url_for('notes.form_saisie_notes',
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">{scu.icontag("notes_img", alt="saisie notes", title="Saisie des notes")}</a>"""
)
@ -824,7 +824,7 @@ def _ligne_evaluation(
)
else:
H.append(
f"""<a class="redlink" href="{url_for('notes.saisie_notes',
f"""<a class="redlink" href="{url_for('notes.form_saisie_notes',
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">saisir notes</a>
"""
@ -880,7 +880,7 @@ def _ligne_evaluation(
H.append("""[<font color="red">""")
if can_edit_notes:
H.append(
f"""<a class="redlink" href="{url_for('notes.saisie_notes',
f"""<a class="redlink" href="{url_for('notes.form_saisie_notes',
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id,
**{'group_ids:list': gr_moyenne["group_id"]})
}">incomplet&nbsp;: terminer saisie</a></font>]"""
@ -891,9 +891,9 @@ def _ligne_evaluation(
H.append("""<span class="redboldtext">&nbsp; """)
if can_edit_notes:
H.append(
f"""<a class="redlink" href="{url_for('notes.saisie_notes',
f"""<a class="redlink" href="{url_for('notes.form_saisie_notes',
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id,
**{'group_ids:list': gr_moyenne["group_id"]})
**{'group_ids': gr_moyenne["group_id"]})
}">"""
)
H.append("pas de notes")

View File

@ -50,6 +50,7 @@ from app.scodoc import sco_pdf
from app.scodoc import sco_preferences
from app.scodoc import sco_pv_pdf
from app.scodoc import sco_pv_lettres_inviduelles
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.gen_tables import GenTable
from app.scodoc.codes_cursus import NO_SEMESTRE_ID
from app.scodoc.sco_pdf import PDFLOCK
@ -336,12 +337,20 @@ def formsemestre_pvjury(formsemestre_id, fmt="html", publish=True):
# ---------------------------------------------------------------------------
def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid=None):
"""Generation PV jury en PDF: saisie des paramètres
Si etudid, PV pour un seul etudiant. Sinon, tout les inscrits au groupe indiqué.
def formsemestre_pvjury_pdf(formsemestre_id, etudid=None):
"""Génération PV jury en PDF: saisie des paramètres
Si etudid, PV pour un seul etudiant.
Sinon, tout les inscrits au(x) groupe(s) indiqué(s).
"""
group_ids = group_ids or []
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
if request.method == "POST":
group_ids = request.form.getlist("group_ids")
else:
group_ids = request.args.getlist("group_ids")
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
formsemestre: FormSemestre = FormSemestre.get_formsemestre(formsemestre_id)
# Mise à jour des groupes d'étapes:
sco_groups.create_etapes_partition(formsemestre_id)
groups_infos = None
@ -361,7 +370,8 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid
etudids = [m["etudid"] for m in groups_infos.members]
H = [
f"""<div class="help">Utiliser cette page pour éditer des versions provisoires des PV.
f"""<div class="help space-after-24">
Utiliser cette page pour éditer des versions provisoires des PV.
<span class="fontred">Il est recommandé d'archiver les versions définitives:
<a class="stdlink" href="{url_for(
'notes.formsemestre_archive',
@ -381,7 +391,7 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid
if groups_infos:
menu_choix_groupe = (
"""<div class="group_ids_sel_menu">Groupes d'étudiants à lister sur le PV: """
+ sco_groups_view.menu_groups_choice(groups_infos)
+ sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True)
+ """</div>"""
)
else:
@ -394,24 +404,28 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid
submitlabel="Générer document",
name="tf",
formid="group_selector",
html_foot_markup=menu_choix_groupe,
html_head_markup=menu_choix_groupe,
)
if tf[0] == 0:
info_etud = (
f"""de <a class="discretelink" href="{
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
}">{etud.nomprenom}</a>"""
if etud
else ""
)
return render_template(
"sco_page.j2",
title=f"Édition du PV de jury {('de ' + etud.nom_prenom()) if etud else ''}",
content=f"""<h2 class="formsemestre">Édition du PV de jury
de <a class="discretelink" href="{
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
}">{etud.nomprenom}</a></h2>"""
{info_etud}</h2>"""
+ "\n".join(H)
+ "\n"
+ tf[1]
+ "\n".join(F),
javascripts=sco_groups_view.JAVASCRIPTS,
cssstyles=sco_groups_view.CSSSTYLES,
javascripts=["js/groups_view.js"],
)
elif tf[0] == -1:
if tf[0] == -1:
return flask.redirect(
url_for(
"notes.formsemestre_pvjury",
@ -419,34 +433,34 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid
formsemestre_id=formsemestre_id,
)
)
# submit
tf[2]["show_title"] = bool(tf[2]["show_title"])
tf[2]["anonymous"] = bool(tf[2]["anonymous"])
try:
PDFLOCK.acquire()
pdfdoc = sco_pv_pdf.pvjury_pdf(
formsemestre,
etudids,
numero_arrete=tf[2]["numero_arrete"],
code_vdi=tf[2]["code_vdi"],
date_commission=tf[2]["date_commission"],
date_jury=tf[2]["date_jury"],
show_title=tf[2]["show_title"],
pv_title_session=tf[2]["pv_title_session"],
pv_title=tf[2]["pv_title"],
with_paragraph_nom=tf[2]["with_paragraph_nom"],
anonymous=tf[2]["anonymous"],
)
finally:
PDFLOCK.release()
date_iso = time.strftime("%Y-%m-%d")
if groups_infos:
groups_filename = "-" + groups_infos.groups_filename
else:
# submit
tf[2]["show_title"] = bool(tf[2]["show_title"])
tf[2]["anonymous"] = bool(tf[2]["anonymous"])
try:
PDFLOCK.acquire()
pdfdoc = sco_pv_pdf.pvjury_pdf(
formsemestre,
etudids,
numero_arrete=tf[2]["numero_arrete"],
code_vdi=tf[2]["code_vdi"],
date_commission=tf[2]["date_commission"],
date_jury=tf[2]["date_jury"],
show_title=tf[2]["show_title"],
pv_title_session=tf[2]["pv_title_session"],
pv_title=tf[2]["pv_title"],
with_paragraph_nom=tf[2]["with_paragraph_nom"],
anonymous=tf[2]["anonymous"],
)
finally:
PDFLOCK.release()
date_iso = time.strftime("%Y-%m-%d")
if groups_infos:
groups_filename = "-" + groups_infos.groups_filename
else:
groups_filename = ""
filename = f"""PV-{formsemestre.titre_num()}{groups_filename}-{date_iso}.pdf"""
return scu.sendPDFFile(pdfdoc, filename)
groups_filename = ""
filename = f"""PV-{formsemestre.titre_num()}{groups_filename}-{date_iso}.pdf"""
return scu.sendPDFFile(pdfdoc, filename)
def descrform_pvjury(formsemestre: FormSemestre):
@ -542,9 +556,17 @@ def descrform_pvjury(formsemestre: FormSemestre):
]
def formsemestre_lettres_individuelles(formsemestre_id, group_ids=()):
def formsemestre_lettres_individuelles(formsemestre_id):
"Lettres avis jury en PDF"
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
if request.method == "POST":
group_ids = request.form.getlist("group_ids")
else:
group_ids = request.args.getlist("group_ids")
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
if not group_ids:
# tous les inscrits du semestre
group_ids = [sco_groups.get_default_group(formsemestre_id)]
@ -556,20 +578,22 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=()):
H = [
f"""
<h2 class="formsemestre">Édition des lettres individuelles</h2>
<p class="help">Utiliser cette page pour éditer des versions provisoires des PV.
<div class="help space-after-24">
Utiliser cette page pour éditer des versions provisoires des PV.
<span class="fontred">Il est recommandé d'archiver les versions définitives: <a
href="{url_for(
"notes.formsemestre_archive",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
)}"
>voir cette page</a></span></p>
>voir cette page</a></span>
</div>
""",
]
descr = descrform_lettres_individuelles()
menu_choix_groupe = (
"""<div class="group_ids_sel_menu">Groupes d'étudiants à lister: """
+ sco_groups_view.menu_groups_choice(groups_infos)
+ sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True)
+ """</div>"""
)
@ -581,15 +605,14 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=()):
submitlabel="Générer document",
name="tf",
formid="group_selector",
html_foot_markup=menu_choix_groupe,
html_head_markup=menu_choix_groupe,
)
if tf[0] == 0:
return render_template(
"sco_page.j2",
title="Édition des lettres individuelles",
content="\n".join(H) + "\n" + tf[1],
javascripts=sco_groups_view.JAVASCRIPTS,
cssstyles=sco_groups_view.CSSSTYLES,
javascripts=["js/groups_view.js"],
)
elif tf[0] == -1:
return flask.redirect(

View File

@ -296,6 +296,14 @@ def formsemestre_report_counts(
sinon liste prédéfinie (voir ci-dessous)
"""
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
if request.method == "POST":
group_ids = request.form.getlist("group_ids")
else:
group_ids = request.args.getlist("group_ids")
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
groups_infos = sco_groups_view.DisplayedGroupsInfos(
group_ids,
formsemestre_id=formsemestre.id,
@ -420,8 +428,7 @@ def formsemestre_report_counts(
]
return render_template(
"sco_page.j2",
cssstyles=sco_groups_view.CSSSTYLES,
javascripts=sco_groups_view.JAVASCRIPTS,
javascripts=["js/groups_view.js"],
title=title,
content="\n".join(H),
)
@ -740,7 +747,6 @@ def table_suivi_cohorte(
def formsemestre_suivi_cohorte(
formsemestre_id,
fmt="html",
group_ids: list[int] = None, # si indiqué, ne prend que ces groupes
percent=1,
bac="",
bacspecialite="",
@ -759,6 +765,14 @@ def formsemestre_suivi_cohorte(
raise ScoValueError("formsemestre_suivi_cohorte: argument invalide") from exc
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
if request.method == "POST":
group_ids = request.form.getlist("group_ids")
else:
group_ids = request.args.getlist("group_ids")
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
groups_infos = sco_groups_view.DisplayedGroupsInfos(
group_ids,
formsemestre_id=formsemestre.id,
@ -850,8 +864,7 @@ def formsemestre_suivi_cohorte(
]
return render_template(
"sco_page.j2",
cssstyles=sco_groups_view.CSSSTYLES,
javascripts=sco_groups_view.JAVASCRIPTS,
javascripts=["js/groups_view.js"],
title=tab.page_title,
content="\n".join(H),
)
@ -1629,7 +1642,6 @@ def graph_cursus(
def formsemestre_graph_cursus(
formsemestre_id,
group_ids: list[int] = None, # si indiqué, ne prend que ces groupes
fmt="html",
only_primo=False,
bac="", # selection sur type de bac
@ -1644,6 +1656,15 @@ def formsemestre_graph_cursus(
annee_bac = str(annee_bac or "")
annee_admission = str(annee_admission or "")
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
if request.method == "POST":
group_ids = request.form.getlist("group_ids")
else:
group_ids = request.args.getlist("group_ids")
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
groups_infos = sco_groups_view.DisplayedGroupsInfos(
group_ids,
formsemestre_id=formsemestre.id,
@ -1781,8 +1802,7 @@ def formsemestre_graph_cursus(
]
return render_template(
"sco_page.j2",
cssstyles=sco_groups_view.CSSSTYLES,
javascripts=sco_groups_view.JAVASCRIPTS,
javascripts=["js/groups_view.js"],
page_title=f"Graphe cursus de {sem['titreannee']}",
no_sidebar=True,
content="\n".join(H),

View File

@ -68,7 +68,9 @@ from app.views import ScoData
FONT_NAME = "Arial"
def excel_feuille_saisie(evaluation: "Evaluation", rows: list[dict]) -> AnyStr:
def excel_feuille_saisie(
evaluation: "Evaluation", rows: list[dict], groups_titles: str = ""
) -> AnyStr:
"""Génère feuille excel pour saisie des notes dans l'evaluation
- evaluation
- rows: liste de dict
@ -77,7 +79,9 @@ def excel_feuille_saisie(evaluation: "Evaluation", rows: list[dict]) -> AnyStr:
"""
ws = ScoExcelSheet("Saisie notes")
styles = _build_styles()
nb_lines_titles = _insert_top_title(ws, styles, evaluation=evaluation)
nb_lines_titles = _insert_top_title(
ws, styles, evaluation=evaluation, groups_titles=groups_titles
)
_insert_line_titles(
ws,
@ -263,6 +267,7 @@ def _insert_top_title(
evaluation: Evaluation | None = None,
formsemestre: FormSemestre | None = None,
description="",
groups_titles: str = "",
) -> int:
"""Insère les lignes de titre de la feuille (suivies d'une ligne blanche).
Si evaluation, indique son titre.
@ -298,7 +303,7 @@ def _insert_top_title(
evaluation.moduleimpl.formsemestre.titre_annee()
if evaluation
else (formsemestre.titre_annee() if formsemestre else "")
)
) + ((" - " + groups_titles) if groups_titles else "")
ws.append_single_cell_row(
scu.unescape_html(titre_annee), styles["titres"], prefix=[""]
)
@ -372,15 +377,16 @@ def _insert_bottom_help(ws, styles: dict):
)
def feuille_saisie_notes(
evaluation_id, group_ids: list[int] = None
): # TODO ré-écrire et passer dans notes.py
def feuille_saisie_notes(evaluation_id: int): # TODO ré-écrire et passer dans notes.py
"""Vue: document Excel pour saisie notes dans l'évaluation et les groupes indiqués"""
evaluation = Evaluation.get_evaluation(evaluation_id)
group_ids = group_ids or []
group_ids = request.args.getlist("group_ids") or []
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
modimpl = evaluation.moduleimpl
formsemestre = modimpl.formsemestre
if evaluation.date_debut:
indication_date = evaluation.date_debut.date().isoformat()
else:
@ -430,7 +436,9 @@ def feuille_saisie_notes(
eval_name = f"{evaluation.moduleimpl.module.code}-{indication_date}"
filename = f"notes_{eval_name}_{gr_title_filename}"
xls = excel_feuille_saisie(evaluation, rows=rows)
xls = excel_feuille_saisie(
evaluation, rows=rows, groups_titles=groups_infos.groups_titles
)
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE)
@ -962,9 +970,14 @@ def _get_sheet_evaluations(
raise ValueError("_get_sheet_evaluations")
def saisie_notes_tableur(evaluation_id: int, group_ids=()):
def saisie_notes_tableur(evaluation_id: int):
"""Saisie des notes via un fichier Excel"""
evaluation = Evaluation.query.get_or_404(evaluation_id)
group_ids = request.args.getlist("group_ids")
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
evaluation = Evaluation.get_evaluation(evaluation_id)
moduleimpl_id = evaluation.moduleimpl.id
formsemestre_id = evaluation.moduleimpl.formsemestre_id
if not evaluation.moduleimpl.can_edit_notes(current_user):
@ -1004,18 +1017,20 @@ def saisie_notes_tableur(evaluation_id: int, group_ids=()):
# Menu choix groupe:
H.append("""<div id="group-tabs"><table><tr><td>""")
H.append(sco_groups_view.form_groups_choice(groups_infos))
H.append(sco_groups_view.form_groups_choice(groups_infos, submit_on_change=True))
H.append("</td></tr></table></div>")
H.append(
f"""<div class="saisienote_etape1">
<span class="titredivsaisienote">Étape 1 : </span>
<ul>
<li><a class="stdlink" href="feuille_saisie_notes?evaluation_id={evaluation_id}&{
<li><a class="stdlink" href="{
url_for('notes.feuille_saisie_notes',
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id)}&{
groups_infos.groups_query_args}"
id="lnk_feuille_saisie">obtenir le fichier tableur à remplir</a>
</li>
<li>ou <a class="stdlink" href="{url_for("notes.saisie_notes",
<li>ou <a class="stdlink" href="{url_for("notes.form_saisie_notes",
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id)
}">aller au formulaire de saisie</a></li>
</ul>
@ -1085,7 +1100,7 @@ def saisie_notes_tableur(evaluation_id: int, group_ids=()):
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">Charger un autre fichier de notes</a>
&nbsp;&nbsp;&nbsp;
<a class="stdlink" href="{url_for("notes.saisie_notes",
<a class="stdlink" href="{url_for("notes.form_saisie_notes",
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">Formulaire de saisie des notes</a>
</div>"""
@ -1113,7 +1128,9 @@ def saisie_notes_tableur(evaluation_id: int, group_ids=()):
<div>
<ul>
<li>
<form action="do_evaluation_set_missing" method="POST">
<form action="{
url_for("notes.do_evaluation_set_missing", scodoc_dept=g.scodoc_dept)
}" method="POST">
Mettre toutes les notes manquantes à <input type="text" size="5" name="value"/>
<input type="submit" value="OK"/>
<input type="hidden" name="evaluation_id" value="{evaluation_id}"/>
@ -1129,7 +1146,7 @@ def saisie_notes_tableur(evaluation_id: int, group_ids=()):
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id)
}">Revenir au module</a>
</li>
<li><a class="stdlink" href="{url_for("notes.saisie_notes",
<li><a class="stdlink" href="{url_for("notes.form_saisie_notes",
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">Revenir au formulaire de saisie</a>
</li>
@ -1176,8 +1193,7 @@ def saisie_notes_tableur(evaluation_id: int, group_ids=()):
"sco_page.j2",
content="\n".join(H),
page_title=page_title,
javascripts=sco_groups_view.JAVASCRIPTS,
cssstyles=sco_groups_view.CSSSTYLES,
javascripts=["js/groups_view.js"],
)

View File

@ -54,7 +54,6 @@ from app.scodoc.sco_exceptions import (
AccessDenied,
NoteProcessError,
ScoException,
ScoInvalidParamError,
ScoValueError,
)
from app.scodoc import htmlutils
@ -71,6 +70,7 @@ from app.scodoc.TrivialFormulator import TF
import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import json_error
from app.scodoc.sco_utils import ModuleType
from app.views import ScoData
def convert_note_from_string(
@ -212,7 +212,7 @@ def do_evaluation_set_missing(
evaluation_id, value, dialog_confirmed=False, group_ids_str: str = ""
):
"""Initialisation des notes manquantes"""
evaluation = Evaluation.query.get_or_404(evaluation_id)
evaluation = Evaluation.get_evaluation(evaluation_id)
modimpl = evaluation.moduleimpl
# Check access
# (admin, respformation, and responsable_id)
@ -222,8 +222,12 @@ def do_evaluation_set_missing(
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
if not group_ids_str:
groups = None
groups_infos = None
else:
group_ids = [int(x) for x in str(group_ids_str).split(",")]
groups_infos = sco_groups_view.DisplayedGroupsInfos(
group_ids, formsemestre_id=modimpl.formsemestre.id
)
groups = sco_groups.listgroups(group_ids)
etudid_etats = sco_groups.do_evaluation_listeetuds_groups(
@ -240,7 +244,9 @@ def do_evaluation_set_missing(
# Convert and check values
valid_notes, invalids, _, _, _ = check_notes(notes, evaluation)
dest_url = url_for(
"notes.saisie_notes", scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id
"notes.form_saisie_notes",
scodoc_dept=g.scodoc_dept,
evaluation_id=evaluation_id,
)
diag = ""
if len(invalids) > 0:
@ -260,12 +266,17 @@ def do_evaluation_set_missing(
plural = len(valid_notes) > 1
return scu.confirm_dialog(
f"""<h2>Mettre toutes les notes manquantes de l'évaluation
à la valeur {value} ?</h2>
à la valeur <span class="fontred">{value} / {evaluation.note_max:g}</span> ?</h2>
<p>Seuls les étudiants pour lesquels aucune note (ni valeur, ni ABS, ni EXC)
n'a été rentrée seront affectés.</p>
<p><b>{len(valid_notes)} étudiant{"s" if plural else ""} concerné{"s" if plural else ""}
<div>
<b>Groupes: {groups_infos.groups_titles if groups_infos else "tous"},
dont
<span class="fontred">
{len(valid_notes)} étudiant{"s" if plural else ""} concerné{"s" if plural else ""}
</span>
par ce changement de note.</b>
</p>
</div>
""",
dest_url="",
cancel_url=dest_url,
@ -624,14 +635,8 @@ def _record_note(
# Nouveau formulaire saisie notes (2016)
def saisie_notes(evaluation_id: int, group_ids: list = None):
def saisie_notes(evaluation: Evaluation, group_ids: list[int] | tuple[int] = ()):
"""Formulaire saisie notes d'une évaluation pour un groupe"""
if not isinstance(evaluation_id, int):
raise ScoInvalidParamError()
group_ids = [int(group_id) for group_id in (group_ids or [])]
evaluation: Evaluation = db.session.get(Evaluation, evaluation_id)
if evaluation is None:
raise ScoValueError("évaluation inexistante")
modimpl = evaluation.moduleimpl
moduleimpl_status_url = url_for(
"notes.moduleimpl_status",
@ -660,7 +665,6 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
select_all_when_unspecified=True,
etat=None,
)
page_title = (
f'Saisie "{evaluation.description}"'
if evaluation.description
@ -669,12 +673,12 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
# HTML page:
H = [
sco_evaluations.evaluation_describe(
evaluation_id=evaluation_id, link_saisie=False
evaluation_id=evaluation.id, link_saisie=False
),
'<div id="saisie_notes"><span class="eval_title">Saisie des notes</span>',
]
H.append("""<div id="group-tabs"><table><tr><td>""")
H.append(sco_groups_view.form_groups_choice(groups_infos))
H.append(sco_groups_view.form_groups_choice(groups_infos, submit_on_change=True))
H.append('</td><td style="padding-left: 35px;">')
H.append(
htmlutils.make_menu(
@ -755,7 +759,8 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
"sco_page.j2",
content="\n".join(H),
title=page_title,
javascripts=["js/saisie_notes.js"],
javascripts=["js/groups_view.js", "js/saisie_notes.js"],
sco=ScoData(formsemestre=modimpl.formsemestre),
)
@ -839,11 +844,15 @@ def _form_saisie_notes(
"""
formsemestre_id = modimpl.formsemestre_id
formsemestre: FormSemestre = evaluation.moduleimpl.formsemestre
groups = sco_groups.listgroups(groups_infos.group_ids)
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
etudids = [
x[0]
for x in sco_groups.do_evaluation_listeetuds_groups(
evaluation.id, getallstudents=True, include_demdef=True
evaluation.id,
groups=groups,
getallstudents=groups is None,
include_demdef=True,
)
]
if not etudids:
@ -1001,7 +1010,9 @@ def _form_saisie_notes(
H.append(
f"""
<div>
<form id="do_evaluation_set_missing" action="do_evaluation_set_missing" method="POST">
<form id="do_evaluation_set_missing" action="{
url_for("notes.do_evaluation_set_missing", scodoc_dept=g.scodoc_dept)
}" method="POST">
Mettre les notes manquantes à
<input type="text" size="5" name="value"/>
<input type="submit" value="OK"/>

View File

@ -12,7 +12,6 @@ from docx.shared import Mm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_ALIGN_VERTICAL
from app.scodoc import sco_etud
from app.scodoc import sco_photos
import app.scodoc.sco_utils as scu
import sco_version
@ -31,9 +30,9 @@ def trombino_doc(groups_infos):
)
section = document.sections[0]
footer = section.footer
footer.paragraphs[
0
].text = f"Généré par {sco_version.SCONAME} le {scu.timedate_human_repr()}"
footer.paragraphs[0].text = (
f"Généré par {sco_version.SCONAME} le {scu.timedate_human_repr()}"
)
nb_images = len(groups_infos.members)
table = document.add_table(rows=2 * (nb_images // N_PER_ROW + 1), cols=N_PER_ROW)

View File

@ -1543,15 +1543,18 @@ def confirm_dialog(
H = [
f"""<form {action} method="POST">
{message}
<div class="form-group space-before-24">
""",
]
if OK or not cancel_url:
H.append(f'<input type="submit" value="{OK}"/>')
H.append(f'<input class="btn btn-default" type="submit" value="{OK}"/>')
if cancel_url:
H.append(
f"""<input type ="button" value="{cancel_label}"
onClick="document.location='{cancel_url}';"/>"""
f"""<input class="btn btn-default" type="submit" name="cancel" type ="button" value="{cancel_label}"
onClick="event.preventDefault(); document.location='{cancel_url}';"/>"""
)
H.append("</div>")
for param in parameters.keys():
if parameters[param] is None:
parameters[param] = ""

View File

@ -66,6 +66,10 @@ div.sco-app-content {
margin-top: 24px !important;
}
.space-after-24 {
margin-bottom: 24px !important;
}
div.scobox.maxwidth {
max-width: none;
}

View File

@ -441,8 +441,13 @@ textarea {
transition: border-color 0.2s ease-in-out;
}
.form-group input[type="submit"] {
max-width: var(--sco-content-max-width);
/* Media query for desktop devices
évite que les boutons submit ne deviennent trop larges
*/
@media (min-width: 769px) {
.form-group input[type="submit"] {
width: 192px;
}
}
.form-group input:focus,

View File

@ -1836,7 +1836,6 @@ sco_publish(
sco_saisie_excel.feuille_saisie_notes,
Permission.EnsView,
)
sco_publish("/saisie_notes", sco_saisie_notes.saisie_notes, Permission.EnsView)
sco_publish(
"/do_evaluation_set_missing",
sco_saisie_notes.do_evaluation_set_missing,
@ -1851,6 +1850,20 @@ sco_publish(
)
@bp.route("/form_saisie_notes/<int:evaluation_id>")
@scodoc
@permission_required(Permission.EnsView) # + controle contextuel
def form_saisie_notes(evaluation_id: int):
"Formulaire de saisie des notes d'une évaluation"
evaluation = Evaluation.get_evaluation(evaluation_id)
group_ids = request.args.getlist("group_ids")
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
return sco_saisie_notes.saisie_notes(evaluation, group_ids)
@bp.route("/formsemestre_import_notes/<int:formsemestre_id>", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView) # controle contextuel
@ -2117,7 +2130,9 @@ def _formsemestre_bulletins_choice(
explanation=explanation,
choose_mail=choose_mail,
formsemestre=formsemestre,
menu_groups_choice=sco_groups_view.menu_groups_choice(groups_infos),
menu_groups_choice=sco_groups_view.menu_groups_choice(
groups_infos, submit_on_change=True
),
sco=ScoData(formsemestre=formsemestre),
sco_groups_view=sco_groups_view,
title=title,