############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Emmanuel Viennet emmanuel.viennet@viennet.net # ############################################################################## """ScoDoc : gestion des fichiers archivés associés au formsemestre (PV de jury, ...) """ import json import flask from flask import flash, g, request, url_for from app import ScoDocJSONEncoder from app.but import jury_but_pv from app.comp import res_sem from app.comp.res_compat import NotesTableCompat from app.models import FormSemestre from app.scodoc.TrivialFormulator import TrivialFormulator from app.scodoc.sco_exceptions import ScoPermissionDenied from app.scodoc import html_sco_header from app.scodoc import sco_bulletins_pdf from app.scodoc import sco_groups from app.scodoc import sco_groups_view from app.scodoc import sco_pv_forms from app.scodoc import sco_pv_lettres_inviduelles from app.scodoc import sco_pv_pdf from app.scodoc.sco_archives import BaseArchiver from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_recapcomplet import ( gen_formsemestre_recapcomplet_excel, gen_formsemestre_recapcomplet_html_table, gen_formsemestre_recapcomplet_json, ) import app.scodoc.sco_utils as scu class SemsArchiver(BaseArchiver): "Archiveur pour les PV de semestre" def __init__(self): BaseArchiver.__init__(self, archive_type="") PV_ARCHIVER = SemsArchiver() # ---------------------------------------------------------------------------- def do_formsemestre_archive( formsemestre_id, group_ids: list[int] = None, # si indiqué, ne prend que ces groupes description="", date_jury="", signature=None, # pour lettres indiv date_commission=None, numero_arrete=None, code_vdi=None, show_title=False, pv_title=None, pv_title_session=None, with_paragraph_nom=False, anonymous=False, bul_version="long", ): """Make and store new archive for this formsemestre. Store: - tableau recap (xls), pv jury (xls et pdf), bulletins (xml et pdf), lettres individuelles (pdf) """ if bul_version not in scu.BULLETINS_VERSIONS: raise ScoValueError( "do_formsemestre_archive: version de bulletin demandée invalide" ) formsemestre = FormSemestre.get_formsemestre(formsemestre_id) res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) sem_archive_id = formsemestre_id archive_id = PV_ARCHIVER.create_obj_archive( sem_archive_id, description, formsemestre.dept_id ) date = PV_ARCHIVER.get_archive_date(archive_id).strftime("%d/%m/%Y à %H:%M") if not group_ids: # tous les inscrits du semestre group_ids = [sco_groups.get_default_group(formsemestre_id)] groups_infos = sco_groups_view.DisplayedGroupsInfos( group_ids, formsemestre_id=formsemestre_id ) groups_filename = "-" + groups_infos.groups_filename etudids = [m["etudid"] for m in groups_infos.members] # Tableau recap notes en XLS (pour tous les etudiants, n'utilise pas les groupes) data, _ = gen_formsemestre_recapcomplet_excel(res, include_evaluations=True) if data: PV_ARCHIVER.store( archive_id, "Tableau_moyennes" + scu.XLSX_SUFFIX, data, dept_id=formsemestre.dept_id, ) # Tableau recap notes en HTML (pour tous les etudiants, n'utilise pas les groupes) table_html, _, _ = gen_formsemestre_recapcomplet_html_table( formsemestre, res, include_evaluations=True ) if table_html: flash(f"Moyennes archivées le {date}", category="info") data = "\n".join( [ html_sco_header.sco_header( page_title=f"Moyennes archivées le {date}", no_side_bar=True, ), f'

Valeurs archivées le {date}

', """""", table_html, html_sco_header.sco_footer(), ] ) PV_ARCHIVER.store( archive_id, "Tableau_moyennes.html", data, dept_id=formsemestre.dept_id ) # Bulletins en JSON data = gen_formsemestre_recapcomplet_json(formsemestre_id, xml_with_decisions=True) data_js = json.dumps(data, indent=1, cls=ScoDocJSONEncoder) if data: PV_ARCHIVER.store( archive_id, "Bulletins.json", data_js, dept_id=formsemestre.dept_id ) # Décisions de jury, en XLS if formsemestre.formation.is_apc(): response = jury_but_pv.pvjury_page_but(formsemestre_id, fmt="xls") data = response.get_data() else: # formations classiques data = sco_pv_forms.formsemestre_pvjury( formsemestre_id, fmt="xls", publish=False ) if data: PV_ARCHIVER.store( archive_id, "Decisions_Jury" + scu.XLSX_SUFFIX, data, dept_id=formsemestre.dept_id, ) # Classeur bulletins (PDF) data, _ = sco_bulletins_pdf.get_formsemestre_bulletins_pdf( formsemestre_id, version=bul_version ) if data: PV_ARCHIVER.store( archive_id, "Bulletins.pdf", data, dept_id=formsemestre.dept_id, ) # Lettres individuelles (PDF): data = sco_pv_lettres_inviduelles.pdf_lettres_individuelles( formsemestre_id, etudids=etudids, date_jury=date_jury, date_commission=date_commission, signature=signature, ) if data: PV_ARCHIVER.store( archive_id, f"CourriersDecisions{groups_filename}.pdf", data, dept_id=formsemestre.dept_id, ) # PV de jury (PDF): data = sco_pv_pdf.pvjury_pdf( formsemestre, etudids=etudids, date_commission=date_commission, date_jury=date_jury, numero_arrete=numero_arrete, code_vdi=code_vdi, show_title=show_title, pv_title_session=pv_title_session, pv_title=pv_title, with_paragraph_nom=with_paragraph_nom, anonymous=anonymous, ) if data: PV_ARCHIVER.store( archive_id, f"PV_Jury{groups_filename}.pdf", data, dept_id=formsemestre.dept_id, ) def formsemestre_archive(formsemestre_id, group_ids: list[int] = None): """Make and store new archive for this formsemestre. (all students or only selected groups) """ formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) if not formsemestre.can_edit_pv(): raise ScoPermissionDenied( dest_url=url_for( "notes.formsemestre_status", scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, ) ) if not group_ids: # tous les inscrits du semestre group_ids = [sco_groups.get_default_group(formsemestre_id)] groups_infos = sco_groups_view.DisplayedGroupsInfos( group_ids, formsemestre_id=formsemestre_id ) H = [ html_sco_header.html_sem_header( "Archiver les PV et résultats du semestre", javascripts=sco_groups_view.JAVASCRIPTS, cssstyles=sco_groups_view.CSSSTYLES, init_qtip=True, ), """

Cette page permet de générer et d'archiver tous les documents résultant de ce semestre: PV de jury, lettres individuelles, tableaux récapitulatifs.

Les documents archivés sont enregistrés et non modifiables, on peut les retrouver ultérieurement.

On peut archiver plusieurs versions des documents (avant et après le jury par exemple).

""", ] F = [ f"""

Note: les documents sont aussi affectés par les réglages sur la page "Paramétrage" (accessible à l'administrateur du département).

""", html_sco_header.sco_footer(), ] descr = [ ( "description", {"input_type": "textarea", "rows": 4, "cols": 77, "title": "Description"}, ), ("sep", {"input_type": "separator", "title": "Informations sur PV de jury"}), ] descr += sco_pv_forms.descrform_pvjury(formsemestre) descr += [ ( "signature", { "input_type": "file", "size": 30, "explanation": "optionnel: image scannée de la signature pour les lettres individuelles", }, ), ( "bul_version", { "input_type": "menu", "title": "Version des bulletins archivés", "labels": [ "Version courte", "Version intermédiaire", "Version complète", ], "allowed_values": scu.BULLETINS_VERSIONS.keys(), "default": "long", }, ), ] menu_choix_groupe = ( """
Groupes d'étudiants à lister: """ + sco_groups_view.menu_groups_choice(groups_infos) + """(pour les PV et lettres)
""" ) tf = TrivialFormulator( request.base_url, scu.get_request_args(), descr, cancelbutton="Annuler", submitlabel="Générer et archiver les documents", name="tf", formid="group_selector", html_foot_markup=menu_choix_groupe, ) if tf[0] == 0: return "\n".join(H) + "\n" + tf[1] + "\n".join(F) elif tf[0] == -1: msg = "Opération annulée" else: # submit sf = tf[2]["signature"] signature = sf.read() # image of signature if tf[2]["anonymous"]: tf[2]["anonymous"] = True else: tf[2]["anonymous"] = False do_formsemestre_archive( formsemestre_id, group_ids=group_ids, description=tf[2]["description"], date_jury=tf[2]["date_jury"], date_commission=tf[2]["date_commission"], signature=signature, numero_arrete=tf[2]["numero_arrete"], code_vdi=tf[2]["code_vdi"], pv_title_session=tf[2]["pv_title_session"], pv_title=tf[2]["pv_title"], show_title=tf[2]["show_title"], with_paragraph_nom=tf[2]["with_paragraph_nom"], anonymous=tf[2]["anonymous"], bul_version=tf[2]["bul_version"], ) msg = "Nouvelle archive créée" # submitted or cancelled: flash(msg) return flask.redirect( url_for( "notes.formsemestre_list_archives", scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, ) ) def formsemestre_list_archives(formsemestre_id): """Page listing archives""" formsemestre = FormSemestre.get_formsemestre(formsemestre_id) sem_archive_id = formsemestre_id L = [] for archive_id in PV_ARCHIVER.list_obj_archives( sem_archive_id, dept_id=formsemestre.dept_id ): a = { "archive_id": archive_id, "description": PV_ARCHIVER.get_archive_description( archive_id, dept_id=formsemestre.dept_id ), "date": PV_ARCHIVER.get_archive_date(archive_id), "content": PV_ARCHIVER.list_archive( archive_id, dept_id=formsemestre.dept_id ), } L.append(a) H = [html_sco_header.html_sem_header("Archive des PV et résultats ")] if not L: H.append("

aucune archive enregistrée

") else: H.append("") return "\n".join(H) + html_sco_header.sco_footer() def formsemestre_get_archived_file(formsemestre_id, archive_name, filename): """Send file to client.""" formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) sem_archive_id = formsemestre.id return PV_ARCHIVER.get_archived_file( sem_archive_id, archive_name, filename, dept_id=formsemestre.dept_id ) def formsemestre_delete_archive(formsemestre_id, archive_name, dialog_confirmed=False): """Delete an archive""" formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) if not formsemestre.can_edit_pv(): raise ScoPermissionDenied( dest_url=url_for( "notes.formsemestre_status", scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, ) ) sem_archive_id = formsemestre_id archive_id = PV_ARCHIVER.get_id_from_name( sem_archive_id, archive_name, dept_id=formsemestre.dept_id ) dest_url = url_for( "notes.formsemestre_list_archives", scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, ) if not dialog_confirmed: return scu.confirm_dialog( f"""

Confirmer la suppression de l'archive du { PV_ARCHIVER.get_archive_date(archive_id).strftime("%d/%m/%Y %H:%M") } ?

La suppression sera définitive.

""", dest_url="", cancel_url=dest_url, parameters={ "formsemestre_id": formsemestre_id, "archive_name": archive_name, }, ) PV_ARCHIVER.delete_archive(archive_id, dept_id=formsemestre.dept_id) flash("Archive supprimée") return flask.redirect(dest_url)