From 6c044dd4dd313e1b1b8bc9c347192c4be8ab968e Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sun, 25 Aug 2024 07:23:36 +0200 Subject: [PATCH] Adaptation multi-select groupes --- app/scodoc/TrivialFormulator.py | 5 + app/scodoc/sco_archives_formsemestre.py | 2 +- app/scodoc/sco_evaluations.py | 4 +- app/scodoc/sco_moduleimpl_status.py | 12 +-- app/scodoc/sco_pv_forms.py | 119 ++++++++++++++---------- app/scodoc/sco_report.py | 36 +++++-- app/scodoc/sco_saisie_excel.py | 54 +++++++---- app/scodoc/sco_saisie_notes.py | 49 ++++++---- app/scodoc/sco_trombino_doc.py | 7 +- app/scodoc/sco_utils.py | 9 +- app/static/css/scodoc.css | 4 + app/static/css/scodoc97.css | 9 +- app/views/notes.py | 19 +++- 13 files changed, 216 insertions(+), 113 deletions(-) diff --git a/app/scodoc/TrivialFormulator.py b/app/scodoc/TrivialFormulator.py index d9bd37d2..b1525510 100644 --- a/app/scodoc/TrivialFormulator.py +++ b/app/scodoc/TrivialFormulator.py @@ -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 + "

") R.append(self.before_table.format(title=self.title)) + R.append(self.html_head_markup) R.append('') for field, descr in self.formdescription: if descr.get("readonly", False): diff --git a/app/scodoc/sco_archives_formsemestre.py b/app/scodoc/sco_archives_formsemestre.py index 561d0b6c..31548bcd 100644 --- a/app/scodoc/sco_archives_formsemestre.py +++ b/app/scodoc/sco_archives_formsemestre.py @@ -289,7 +289,7 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement. ] menu_choix_groupe = ( """
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)
""" ) diff --git a/app/scodoc/sco_evaluations.py b/app/scodoc/sco_evaluations.py index 40072970..9c5fad6d 100644 --- a/app/scodoc/sco_evaluations.py +++ b/app/scodoc/sco_evaluations.py @@ -732,7 +732,9 @@ def evaluation_describe(evaluation_id="", edit_in_place=True, link_saisie=True) H.append( f""" saisie des notes """ ) diff --git a/app/scodoc/sco_moduleimpl_status.py b/app/scodoc/sco_moduleimpl_status.py index 139081f1..ed1e256d 100644 --- a/app/scodoc/sco_moduleimpl_status.py +++ b/app/scodoc/sco_moduleimpl_status.py @@ -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"""{scu.icontag("notes_img", alt="saisie notes", title="Saisie des notes")}""" ) @@ -824,7 +824,7 @@ def _ligne_evaluation( ) else: H.append( - f"""saisir notes """ @@ -880,7 +880,7 @@ def _ligne_evaluation( H.append("""[""") if can_edit_notes: H.append( - f"""incomplet : terminer saisie]""" @@ -891,9 +891,9 @@ def _ligne_evaluation( H.append("""  """) if can_edit_notes: H.append( - f"""""" ) H.append("pas de notes") diff --git a/app/scodoc/sco_pv_forms.py b/app/scodoc/sco_pv_forms.py index 02521cac..ce8e97f9 100644 --- a/app/scodoc/sco_pv_forms.py +++ b/app/scodoc/sco_pv_forms.py @@ -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"""
Utiliser cette page pour éditer des versions provisoires des PV. + f"""""" ) 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 {etud.nomprenom}""" + 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"""

Édition du PV de jury - de {etud.nomprenom}

""" + {info_etud}""" + "\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"""

Édition des lettres individuelles

-

Utiliser cette page pour éditer des versions provisoires des PV. +

+ Utiliser cette page pour éditer des versions provisoires des PV. Il est recommandé d'archiver les versions définitives: voir cette page

+ >voir cette page +
""", ] descr = descrform_lettres_individuelles() menu_choix_groupe = ( """
Groupes d'étudiants à lister: """ - + sco_groups_view.menu_groups_choice(groups_infos) + + sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True) + """
""" ) @@ -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( diff --git a/app/scodoc/sco_report.py b/app/scodoc/sco_report.py index 0984c8c3..f234786f 100644 --- a/app/scodoc/sco_report.py +++ b/app/scodoc/sco_report.py @@ -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), diff --git a/app/scodoc/sco_saisie_excel.py b/app/scodoc/sco_saisie_excel.py index 6aa6a86f..f46d4a6d 100644 --- a/app/scodoc/sco_saisie_excel.py +++ b/app/scodoc/sco_saisie_excel.py @@ -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("""
""") - 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("
") H.append( f"""
Étape 1 : @@ -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     - Formulaire de saisie des notes
""" @@ -1113,7 +1128,9 @@ def saisie_notes_tableur(evaluation_id: int, group_ids=()):