# -*- mode: python -*- # -*- coding: utf-8 -*- ############################################################################## # # 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 aux étudiants Il s'agit de fichiers quelconques, généralement utilisés pour conserver les dossiers d'admission et autres pièces utiles. """ import flask from flask import flash, render_template, url_for from flask import g, request from flask_login import current_user from app.models import Identite import app.scodoc.sco_utils as scu from app.scodoc import sco_import_etuds from app.scodoc import sco_groups from app.scodoc import sco_trombino from app.scodoc import sco_archives from app.scodoc.sco_permissions import Permission from app.scodoc.sco_exceptions import AccessDenied from app.scodoc.TrivialFormulator import TrivialFormulator class EtudsArchiver(sco_archives.BaseArchiver): def __init__(self): sco_archives.BaseArchiver.__init__(self, archive_type="docetuds") # Global au processus, attention ! ETUDS_ARCHIVER = EtudsArchiver() def can_edit_etud_archive(user): """True si l'utilisateur peut modifier les archives etudiantes""" if user.passwd_must_be_changed: return False return user.has_permission(Permission.EtudAddAnnotations) def etud_list_archives_html(etud: Identite): """HTML snippet listing archives.""" can_edit = can_edit_etud_archive(current_user) etud_archive_id = etud.id L = [] for archive_id in ETUDS_ARCHIVER.list_obj_archives( etud_archive_id, dept_id=etud.dept_id ): a = { "archive_id": archive_id, "description": ETUDS_ARCHIVER.get_archive_description( archive_id, dept_id=etud.dept_id ), "date": ETUDS_ARCHIVER.get_archive_date(archive_id), "content": ETUDS_ARCHIVER.list_archive(archive_id, dept_id=etud.dept_id), } L.append(a) delete_icon = scu.icontag( "delete_small_img", title="Supprimer fichier", alt="supprimer" ) delete_disabled_icon = scu.icontag( "delete_small_dis_img", title="Suppression non autorisée" ) H = ['
") return "".join(H) def add_archives_info_to_etud_list(etuds): """Add key 'etudarchive' describing archive of etuds (used to list all archives of a group) """ for etud in etuds: l = [] etud_archive_id = etud["etudid"] # Here, ETUDS_ARCHIVER will use g.dept_id for archive_id in ETUDS_ARCHIVER.list_obj_archives(etud_archive_id): l.append( "%s (%s)" % ( ETUDS_ARCHIVER.get_archive_description(archive_id), ETUDS_ARCHIVER.list_archive(archive_id)[0], ) ) etud["etudarchive"] = ", ".join(l) def etud_upload_file_form(etudid): """Page with a form to choose and upload a file, with a description.""" # check permission if not can_edit_etud_archive(current_user): raise AccessDenied(f"opération non autorisée pour {current_user}") etud = Identite.get_etud(etudid) H = [ f"""

Chargement d'un document associé à {etud.nomprenom}

Le fichier ne doit pas dépasser { scu.CONFIG.ETUD_MAX_FILE_SIZE // (1024 * 1024)}Mo.

""", ] tf = TrivialFormulator( request.base_url, scu.get_request_args(), ( ("etudid", {"default": etudid, "input_type": "hidden"}), ("datafile", {"input_type": "file", "title": "Fichier", "size": 30}), ( "description", { "input_type": "textarea", "rows": 4, "cols": 77, "title": "Description", }, ), ), submitlabel="Valider", cancelbutton="Annuler", ) if tf[0] == 0: return render_template( "sco_page.j2", title=f"Chargement d'un document associé à {etud.nomprenom}", content="\n".join(H) + tf[1], ) if tf[0] == -1: return flask.redirect(etud.url_fiche()) data = tf[2]["datafile"].read() descr = tf[2]["description"] filename = tf[2]["datafile"].filename _store_etud_file_to_new_archive(etudid, data, filename, description=descr) return flask.redirect(etud.url_fiche()) def _store_etud_file_to_new_archive( etud_archive_id, data, filename, description="" ) -> tuple[bool, str]: """Store data to new archive.""" filesize = len(data) if filesize < 10 or filesize > scu.CONFIG.ETUD_MAX_FILE_SIZE: return False, f"Fichier archive '{filename}' de taille invalide ! ({filesize})" archive_id = ETUDS_ARCHIVER.create_obj_archive(etud_archive_id, description) ETUDS_ARCHIVER.store(archive_id, filename, data) return True, "ok" def etud_delete_archive(etudid, archive_name, dialog_confirmed=False): """Delete an archive""" # check permission if not can_edit_etud_archive(current_user): raise AccessDenied(f"opération non autorisée pour {current_user}") etud = Identite.get_etud(etudid) etud_archive_id = etudid archive_id = ETUDS_ARCHIVER.get_id_from_name( etud_archive_id, archive_name, dept_id=etud.dept_id ) if not dialog_confirmed: return scu.confirm_dialog( f"""

Confirmer la suppression des fichiers ?

Fichier associé le { ETUDS_ARCHIVER.get_archive_date(archive_id).strftime("%d/%m/%Y %H:%M") } à l'étudiant {etud.nomprenom}

La suppression sera définitive.

""", dest_url="", cancel_url=url_for( "scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid, ), parameters={"etudid": etudid, "archive_name": archive_name}, ) ETUDS_ARCHIVER.delete_archive(archive_id, dept_id=etud.dept_id) flash("Archive supprimée") return flask.redirect(etud.url_fiche()) def etud_get_archived_file(etudid, archive_name, filename): """Send file to client.""" etud = Identite.get_etud(etudid) etud_archive_id = etud.id return ETUDS_ARCHIVER.get_archived_file( etud_archive_id, archive_name, filename, dept_id=etud.dept_id ) # --- Upload d'un ensemble de fichiers (pour un groupe d'étudiants) def etudarchive_generate_excel_sample(group_id=None): """Feuille excel pour import fichiers etudiants (utilisé pour admissions)""" fmt = sco_import_etuds.sco_import_format() data = sco_import_etuds.sco_import_generate_excel_sample( fmt, group_ids=[group_id], only_tables=["identite"], exclude_cols=[ "date_naissance", "lieu_naissance", "nationalite", "statut", "photo_filename", ], extra_cols=["fichier_a_charger"], ) return scu.send_file( data, "ImportFichiersEtudiants", suffix=scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE, ) def etudarchive_import_files_form(group_id): """Formulaire pour importation fichiers d'un groupe""" H = [ """

Téléchargement de fichier associés aux étudiants

Les fichiers associés (dossiers d'admission, certificats, ...), de types quelconques (pdf, doc, images) sont accessibles aux utilisateurs via la fiche individuelle de l'étudiant.

Ne pas confondre avec les photos des étudiants, qui se chargent via l'onglet "Photos".

Vous pouvez aussi charger à tout moment de nouveaux fichiers, ou en supprimer, via la fiche de chaque étudiant.

Cette page permet de charger en une seule fois les fichiers de plusieurs étudiants.
Il faut d'abord remplir une feuille excel donnant les noms des fichiers (un fichier par étudiant).

Ensuite, réunir vos fichiers dans un fichier zip, puis télécharger simultanément le fichier excel et le fichier zip.

  1. Obtenir la feuille excel à remplir
  2. """ % group_id, ] tf = TrivialFormulator( request.base_url, scu.get_request_args(), ( ("xlsfile", {"title": "Fichier Excel:", "input_type": "file", "size": 40}), ("zipfile", {"title": "Fichier zip:", "input_type": "file", "size": 40}), ( "description", { "input_type": "textarea", "rows": 4, "cols": 77, "title": "Description", }, ), ("group_id", {"input_type": "hidden"}), ), ) if tf[0] == 0: return render_template( "sco_page.j2", title="Import de fichiers associés aux étudiants", content="\n".join(H) + tf[1] + "
", ) # retrouve le semestre à partir du groupe: group = sco_groups.get_group(group_id) if tf[0] == -1: return flask.redirect( url_for( "notes.formsemestre_status", scodoc_dept=g.scodoc_dept, formsemestre_id=group["formsemestre_id"], ) ) else: return etudarchive_import_files( formsemestre_id=group["formsemestre_id"], xlsfile=tf[2]["xlsfile"], zipfile=tf[2]["zipfile"], description=tf[2]["description"], ) def etudarchive_import_files( formsemestre_id=None, xlsfile=None, zipfile=None, description="" ): "Importe des fichiers" def callback(etud: Identite, data, filename): return _store_etud_file_to_new_archive(etud.id, data, filename, description) # Utilise la fontion developpée au depart pour les photos ( ignored_zipfiles, unmatched_files, stored_etud_filename, ) = sco_trombino.zip_excel_import_files( xlsfile=xlsfile, zipfile=zipfile, callback=callback, filename_title="fichier_a_charger", ) return render_template( "scolar/photos_import_files.j2", page_title="Téléchargement de fichiers associés aux étudiants", ignored_zipfiles=ignored_zipfiles, unmatched_files=unmatched_files, stored_etud_filename=stored_etud_filename, next_page=url_for( "scolar.groups_feuilles", scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, ), )