1
0
forked from ScoDoc/ScoDoc

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, submitbuttonattributes=None,
top_buttons=False, # place buttons at top of form top_buttons=False, # place buttons at top of form
bottom_buttons=True, # buttons after form bottom_buttons=True, # buttons after form
html_head_markup="",
html_foot_markup="", html_foot_markup="",
readonly=False, readonly=False,
is_submitted=False, is_submitted=False,
@ -116,6 +117,7 @@ def TrivialFormulator(
submitbuttonattributes=submitbuttonattributes or [], submitbuttonattributes=submitbuttonattributes or [],
top_buttons=top_buttons, top_buttons=top_buttons,
bottom_buttons=bottom_buttons, bottom_buttons=bottom_buttons,
html_head_markup=html_head_markup,
html_foot_markup=html_foot_markup, html_foot_markup=html_foot_markup,
readonly=readonly, readonly=readonly,
is_submitted=is_submitted, is_submitted=is_submitted,
@ -152,6 +154,7 @@ class TF(object):
submitbuttonattributes=None, submitbuttonattributes=None,
top_buttons=False, # place buttons at top of form top_buttons=False, # place buttons at top of form
bottom_buttons=True, # buttons after 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 html_foot_markup="", # html snippet put at the end, just after the table
readonly=False, readonly=False,
is_submitted=False, is_submitted=False,
@ -178,6 +181,7 @@ class TF(object):
self.submitbuttonattributes = submitbuttonattributes or [] self.submitbuttonattributes = submitbuttonattributes or []
self.top_buttons = top_buttons self.top_buttons = top_buttons
self.bottom_buttons = bottom_buttons self.bottom_buttons = bottom_buttons
self.html_head_markup = html_head_markup
self.html_foot_markup = html_foot_markup self.html_foot_markup = html_foot_markup
self.title = title self.title = title
self.after_table = after_table self.after_table = after_table
@ -469,6 +473,7 @@ class TF(object):
if self.top_buttons: if self.top_buttons:
R.append(buttons_markup + "<p></p>") R.append(buttons_markup + "<p></p>")
R.append(self.before_table.format(title=self.title)) R.append(self.before_table.format(title=self.title))
R.append(self.html_head_markup)
R.append('<table class="tf">') R.append('<table class="tf">')
for field, descr in self.formdescription: for field, descr in self.formdescription:
if descr.get("readonly", False): 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 = ( menu_choix_groupe = (
"""<div class="group_ids_sel_menu">Groupes d'étudiants à lister: """ """<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>""" + """(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( H.append(
f""" f"""
<a style="margin-left: 12px;" class="stdlink" href="{url_for( <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> }">saisie des notes</a>
""" """
) )

View File

@ -72,7 +72,7 @@ def moduleimpl_evaluation_menu(evaluation: Evaluation, nbnotes: int = 0) -> str:
menu_eval = [ menu_eval = [
{ {
"title": "Saisir les notes", "title": "Saisir les notes",
"endpoint": "notes.saisie_notes", "endpoint": "notes.form_saisie_notes",
"args": { "args": {
"evaluation_id": evaluation_id, "evaluation_id": evaluation_id,
}, },
@ -745,7 +745,7 @@ def _ligne_evaluation(
) )
if can_edit_notes: if can_edit_notes:
H.append( 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) scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">{scu.icontag("notes_img", alt="saisie notes", title="Saisie des notes")}</a>""" }">{scu.icontag("notes_img", alt="saisie notes", title="Saisie des notes")}</a>"""
) )
@ -824,7 +824,7 @@ def _ligne_evaluation(
) )
else: else:
H.append( 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) scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">saisir notes</a> }">saisir notes</a>
""" """
@ -880,7 +880,7 @@ def _ligne_evaluation(
H.append("""[<font color="red">""") H.append("""[<font color="red">""")
if can_edit_notes: if can_edit_notes:
H.append( 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, scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id,
**{'group_ids:list': gr_moyenne["group_id"]}) **{'group_ids:list': gr_moyenne["group_id"]})
}">incomplet&nbsp;: terminer saisie</a></font>]""" }">incomplet&nbsp;: terminer saisie</a></font>]"""
@ -891,9 +891,9 @@ def _ligne_evaluation(
H.append("""<span class="redboldtext">&nbsp; """) H.append("""<span class="redboldtext">&nbsp; """)
if can_edit_notes: if can_edit_notes:
H.append( 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, 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") 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_preferences
from app.scodoc import sco_pv_pdf from app.scodoc import sco_pv_pdf
from app.scodoc import sco_pv_lettres_inviduelles 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.gen_tables import GenTable
from app.scodoc.codes_cursus import NO_SEMESTRE_ID from app.scodoc.codes_cursus import NO_SEMESTRE_ID
from app.scodoc.sco_pdf import PDFLOCK 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): def formsemestre_pvjury_pdf(formsemestre_id, etudid=None):
"""Generation PV jury en PDF: saisie des paramètres """Génération PV jury en PDF: saisie des paramètres
Si etudid, PV pour un seul etudiant. Sinon, tout les inscrits au groupe indiqué. Si etudid, PV pour un seul etudiant.
Sinon, tout les inscrits au(x) groupe(s) indiqué(s).
""" """
group_ids = group_ids or [] if request.method == "POST":
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) 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: # Mise à jour des groupes d'étapes:
sco_groups.create_etapes_partition(formsemestre_id) sco_groups.create_etapes_partition(formsemestre_id)
groups_infos = None 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] etudids = [m["etudid"] for m in groups_infos.members]
H = [ 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: <span class="fontred">Il est recommandé d'archiver les versions définitives:
<a class="stdlink" href="{url_for( <a class="stdlink" href="{url_for(
'notes.formsemestre_archive', 'notes.formsemestre_archive',
@ -381,7 +391,7 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid
if groups_infos: if groups_infos:
menu_choix_groupe = ( menu_choix_groupe = (
"""<div class="group_ids_sel_menu">Groupes d'étudiants à lister sur le PV: """ """<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>""" + """</div>"""
) )
else: else:
@ -394,24 +404,28 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid
submitlabel="Générer document", submitlabel="Générer document",
name="tf", name="tf",
formid="group_selector", formid="group_selector",
html_foot_markup=menu_choix_groupe, html_head_markup=menu_choix_groupe,
) )
if tf[0] == 0: 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( return render_template(
"sco_page.j2", "sco_page.j2",
title=f"Édition du PV de jury {('de ' + etud.nom_prenom()) if etud else ''}", title=f"Édition du PV de jury {('de ' + etud.nom_prenom()) if etud else ''}",
content=f"""<h2 class="formsemestre">Édition du PV de jury content=f"""<h2 class="formsemestre">Édition du PV de jury
de <a class="discretelink" href="{ {info_etud}</h2>"""
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
}">{etud.nomprenom}</a></h2>"""
+ "\n".join(H) + "\n".join(H)
+ "\n" + "\n"
+ tf[1] + tf[1]
+ "\n".join(F), + "\n".join(F),
javascripts=sco_groups_view.JAVASCRIPTS, javascripts=["js/groups_view.js"],
cssstyles=sco_groups_view.CSSSTYLES,
) )
elif tf[0] == -1: if tf[0] == -1:
return flask.redirect( return flask.redirect(
url_for( url_for(
"notes.formsemestre_pvjury", "notes.formsemestre_pvjury",
@ -419,34 +433,34 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid
formsemestre_id=formsemestre_id, 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: else:
# submit groups_filename = ""
tf[2]["show_title"] = bool(tf[2]["show_title"]) filename = f"""PV-{formsemestre.titre_num()}{groups_filename}-{date_iso}.pdf"""
tf[2]["anonymous"] = bool(tf[2]["anonymous"]) return scu.sendPDFFile(pdfdoc, filename)
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)
def descrform_pvjury(formsemestre: FormSemestre): 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" "Lettres avis jury en PDF"
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) 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: if not group_ids:
# tous les inscrits du semestre # tous les inscrits du semestre
group_ids = [sco_groups.get_default_group(formsemestre_id)] group_ids = [sco_groups.get_default_group(formsemestre_id)]
@ -556,20 +578,22 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=()):
H = [ H = [
f""" f"""
<h2 class="formsemestre">Édition des lettres individuelles</h2> <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 <span class="fontred">Il est recommandé d'archiver les versions définitives: <a
href="{url_for( href="{url_for(
"notes.formsemestre_archive", "notes.formsemestre_archive",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
)}" )}"
>voir cette page</a></span></p> >voir cette page</a></span>
</div>
""", """,
] ]
descr = descrform_lettres_individuelles() descr = descrform_lettres_individuelles()
menu_choix_groupe = ( menu_choix_groupe = (
"""<div class="group_ids_sel_menu">Groupes d'étudiants à lister: """ """<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>""" + """</div>"""
) )
@ -581,15 +605,14 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=()):
submitlabel="Générer document", submitlabel="Générer document",
name="tf", name="tf",
formid="group_selector", formid="group_selector",
html_foot_markup=menu_choix_groupe, html_head_markup=menu_choix_groupe,
) )
if tf[0] == 0: if tf[0] == 0:
return render_template( return render_template(
"sco_page.j2", "sco_page.j2",
title="Édition des lettres individuelles", title="Édition des lettres individuelles",
content="\n".join(H) + "\n" + tf[1], content="\n".join(H) + "\n" + tf[1],
javascripts=sco_groups_view.JAVASCRIPTS, javascripts=["js/groups_view.js"],
cssstyles=sco_groups_view.CSSSTYLES,
) )
elif tf[0] == -1: elif tf[0] == -1:
return flask.redirect( return flask.redirect(

View File

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

View File

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

View File

@ -54,7 +54,6 @@ from app.scodoc.sco_exceptions import (
AccessDenied, AccessDenied,
NoteProcessError, NoteProcessError,
ScoException, ScoException,
ScoInvalidParamError,
ScoValueError, ScoValueError,
) )
from app.scodoc import htmlutils from app.scodoc import htmlutils
@ -71,6 +70,7 @@ from app.scodoc.TrivialFormulator import TF
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import json_error from app.scodoc.sco_utils import json_error
from app.scodoc.sco_utils import ModuleType from app.scodoc.sco_utils import ModuleType
from app.views import ScoData
def convert_note_from_string( def convert_note_from_string(
@ -212,7 +212,7 @@ def do_evaluation_set_missing(
evaluation_id, value, dialog_confirmed=False, group_ids_str: str = "" evaluation_id, value, dialog_confirmed=False, group_ids_str: str = ""
): ):
"""Initialisation des notes manquantes""" """Initialisation des notes manquantes"""
evaluation = Evaluation.query.get_or_404(evaluation_id) evaluation = Evaluation.get_evaluation(evaluation_id)
modimpl = evaluation.moduleimpl modimpl = evaluation.moduleimpl
# Check access # Check access
# (admin, respformation, and responsable_id) # (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) notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
if not group_ids_str: if not group_ids_str:
groups = None groups = None
groups_infos = None
else: else:
group_ids = [int(x) for x in str(group_ids_str).split(",")] 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) groups = sco_groups.listgroups(group_ids)
etudid_etats = sco_groups.do_evaluation_listeetuds_groups( etudid_etats = sco_groups.do_evaluation_listeetuds_groups(
@ -240,7 +244,9 @@ def do_evaluation_set_missing(
# Convert and check values # Convert and check values
valid_notes, invalids, _, _, _ = check_notes(notes, evaluation) valid_notes, invalids, _, _, _ = check_notes(notes, evaluation)
dest_url = url_for( 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 = "" diag = ""
if len(invalids) > 0: if len(invalids) > 0:
@ -260,12 +266,17 @@ def do_evaluation_set_missing(
plural = len(valid_notes) > 1 plural = len(valid_notes) > 1
return scu.confirm_dialog( return scu.confirm_dialog(
f"""<h2>Mettre toutes les notes manquantes de l'évaluation 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) <p>Seuls les étudiants pour lesquels aucune note (ni valeur, ni ABS, ni EXC)
n'a été rentrée seront affectés.</p> 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> par ce changement de note.</b>
</p> </div>
""", """,
dest_url="", dest_url="",
cancel_url=dest_url, cancel_url=dest_url,
@ -624,14 +635,8 @@ def _record_note(
# Nouveau formulaire saisie notes (2016) # 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""" """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 modimpl = evaluation.moduleimpl
moduleimpl_status_url = url_for( moduleimpl_status_url = url_for(
"notes.moduleimpl_status", "notes.moduleimpl_status",
@ -660,7 +665,6 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
select_all_when_unspecified=True, select_all_when_unspecified=True,
etat=None, etat=None,
) )
page_title = ( page_title = (
f'Saisie "{evaluation.description}"' f'Saisie "{evaluation.description}"'
if evaluation.description if evaluation.description
@ -669,12 +673,12 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
# HTML page: # HTML page:
H = [ H = [
sco_evaluations.evaluation_describe( 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>', '<div id="saisie_notes"><span class="eval_title">Saisie des notes</span>',
] ]
H.append("""<div id="group-tabs"><table><tr><td>""") 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('</td><td style="padding-left: 35px;">')
H.append( H.append(
htmlutils.make_menu( htmlutils.make_menu(
@ -755,7 +759,8 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
"sco_page.j2", "sco_page.j2",
content="\n".join(H), content="\n".join(H),
title=page_title, 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_id = modimpl.formsemestre_id
formsemestre: FormSemestre = evaluation.moduleimpl.formsemestre formsemestre: FormSemestre = evaluation.moduleimpl.formsemestre
groups = sco_groups.listgroups(groups_infos.group_ids)
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
etudids = [ etudids = [
x[0] x[0]
for x in sco_groups.do_evaluation_listeetuds_groups( 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: if not etudids:
@ -1001,7 +1010,9 @@ def _form_saisie_notes(
H.append( H.append(
f""" f"""
<div> <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 à Mettre les notes manquantes à
<input type="text" size="5" name="value"/> <input type="text" size="5" name="value"/>
<input type="submit" value="OK"/> <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.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_ALIGN_VERTICAL from docx.enum.table import WD_ALIGN_VERTICAL
from app.scodoc import sco_etud
from app.scodoc import sco_photos from app.scodoc import sco_photos
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import sco_version import sco_version
@ -31,9 +30,9 @@ def trombino_doc(groups_infos):
) )
section = document.sections[0] section = document.sections[0]
footer = section.footer footer = section.footer
footer.paragraphs[ footer.paragraphs[0].text = (
0 f"Généré par {sco_version.SCONAME} le {scu.timedate_human_repr()}"
].text = f"Généré par {sco_version.SCONAME} le {scu.timedate_human_repr()}" )
nb_images = len(groups_infos.members) nb_images = len(groups_infos.members)
table = document.add_table(rows=2 * (nb_images // N_PER_ROW + 1), cols=N_PER_ROW) 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 = [ H = [
f"""<form {action} method="POST"> f"""<form {action} method="POST">
{message} {message}
<div class="form-group space-before-24">
""", """,
] ]
if OK or not cancel_url: 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: if cancel_url:
H.append( H.append(
f"""<input type ="button" value="{cancel_label}" f"""<input class="btn btn-default" type="submit" name="cancel" type ="button" value="{cancel_label}"
onClick="document.location='{cancel_url}';"/>""" onClick="event.preventDefault(); document.location='{cancel_url}';"/>"""
) )
H.append("</div>")
for param in parameters.keys(): for param in parameters.keys():
if parameters[param] is None: if parameters[param] is None:
parameters[param] = "" parameters[param] = ""

View File

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

View File

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

View File

@ -1836,7 +1836,6 @@ sco_publish(
sco_saisie_excel.feuille_saisie_notes, sco_saisie_excel.feuille_saisie_notes,
Permission.EnsView, Permission.EnsView,
) )
sco_publish("/saisie_notes", sco_saisie_notes.saisie_notes, Permission.EnsView)
sco_publish( sco_publish(
"/do_evaluation_set_missing", "/do_evaluation_set_missing",
sco_saisie_notes.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"]) @bp.route("/formsemestre_import_notes/<int:formsemestre_id>", methods=["GET", "POST"])
@scodoc @scodoc
@permission_required(Permission.ScoView) # controle contextuel @permission_required(Permission.ScoView) # controle contextuel
@ -2117,7 +2130,9 @@ def _formsemestre_bulletins_choice(
explanation=explanation, explanation=explanation,
choose_mail=choose_mail, choose_mail=choose_mail,
formsemestre=formsemestre, 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=ScoData(formsemestre=formsemestre),
sco_groups_view=sco_groups_view, sco_groups_view=sco_groups_view,
title=title, title=title,