Adaptation fonctions export Apogée pour ScoDoc 9

This commit is contained in:
Emmanuel Viennet 2021-08-31 19:32:12 +02:00
parent 2fbce89edd
commit a52de101b6
3 changed files with 69 additions and 77 deletions

View File

@ -82,14 +82,15 @@ XXX A vérifier:
import collections import collections
import datetime import datetime
from functools import reduce
import io import io
import os import os
import pprint
import re import re
import time import time
from zipfile import ZipFile from zipfile import ZipFile
import pprint
from functools import reduce from flask import send_file
# Pour la détection auto de l'encodage des fichiers Apogée: # Pour la détection auto de l'encodage des fichiers Apogée:
from chardet import detect as chardet_detect from chardet import detect as chardet_detect
@ -500,7 +501,7 @@ class ApoEtud(dict):
# print 'comp_elt_annuel cur_sem=%s autre_sem=%s' % (cur_sem['formsemestre_id'], autre_sem['formsemestre_id']) # print 'comp_elt_annuel cur_sem=%s autre_sem=%s' % (cur_sem['formsemestre_id'], autre_sem['formsemestre_id'])
if not cur_sem: if not cur_sem:
# l'étudiant n'a pas de semestre courant ?! # l'étudiant n'a pas de semestre courant ?!
log("comp_elt_annuel: %s no cur_sem" % etudid) log("comp_elt_annuel: etudid %s has no cur_sem" % etudid)
return VOID_APO_RES return VOID_APO_RES
cur_nt = sco_cache.NotesTableCache.get(cur_sem["formsemestre_id"]) cur_nt = sco_cache.NotesTableCache.get(cur_sem["formsemestre_id"])
cur_decision = cur_nt.get_etud_decision_sem(etudid) cur_decision = cur_nt.get_etud_decision_sem(etudid)
@ -756,22 +757,19 @@ class ApoData(object):
else: else:
self.sems_periode = None self.sems_periode = None
def read_csv(self, data): def read_csv(self, data: str):
if not data: if not data:
raise FormatError("Fichier Apogée vide !") raise FormatError("Fichier Apogée vide !")
data_utf8 = data.decode(APO_INPUT_ENCODING).encode( f = StringIOFileLineWrapper(data) # pour traiter comme un fichier
scu.SCO_ENCODING
) # XXX #sco8
f = StringIOFileLineWrapper(data_utf8) # pour traiter comme un fichier
# check that we are at the begining of Apogee CSV # check that we are at the begining of Apogee CSV
line = f.readline().strip() line = f.readline().strip()
if line != "XX-APO_TITRES-XX": if line != "XX-APO_TITRES-XX":
raise FormatError("format incorrect: pas de XX-APO_TITRES-XX") raise FormatError("format incorrect: pas de XX-APO_TITRES-XX")
# 1-- En-tête: du début jusqu'à la balise XX-APO_VALEURS-XX # 1-- En-tête: du début jusqu'à la balise XX-APO_VALEURS-XX
idx = data_utf8.index("XX-APO_VALEURS-XX") idx = data.index("XX-APO_VALEURS-XX")
self.header = data_utf8[:idx] # en codage ScoDoc (utf8) self.header = data[:idx]
# 2-- Titres: # 2-- Titres:
# on va y chercher apoC_Fichier_Exp qui donnera le nom du fichier # on va y chercher apoC_Fichier_Exp qui donnera le nom du fichier
@ -1236,7 +1234,7 @@ def export_csv_to_apogee(
# Create ZIP # Create ZIP
if not dest_zip: if not dest_zip:
data = io.StringIO() data = io.BytesIO()
dest_zip = ZipFile(data, "w") dest_zip = ZipFile(data, "w")
my_zip = True my_zip = True
else: else:
@ -1293,7 +1291,7 @@ def export_csv_to_apogee(
) )
log(logf.getvalue()) # sortie aussi sur le log ScoDoc log(logf.getvalue()) # sortie aussi sur le log ScoDoc
csv_data = f.getvalue().decode(scu.SCO_ENCODING).encode(APO_OUTPUT_ENCODING) # XXX csv_data = f.getvalue().encode(APO_OUTPUT_ENCODING)
# Write data to ZIP # Write data to ZIP
dest_zip.writestr(csv_filename, csv_data) dest_zip.writestr(csv_filename, csv_data)
@ -1304,13 +1302,12 @@ def export_csv_to_apogee(
if my_zip: if my_zip:
dest_zip.close() dest_zip.close()
size = data.tell() data.seek(0)
content_type = "application/zip" return send_file(
REQUEST.RESPONSE.setHeader( data,
"content-disposition", 'attachement; filename="%s-scodoc.zip"' % basename mimetype="application/zip",
download_name=basename + "-scodoc.zip",
as_attachment=True,
) )
REQUEST.RESPONSE.setHeader("content-type", content_type)
REQUEST.RESPONSE.setHeader("content-length", size)
return data.getvalue()
else: else:
return None # zip modified in place return None # zip modified in place

View File

@ -99,12 +99,14 @@ ApoCSVArchive = ApoCSVArchiver()
# return archive_id # return archive_id
def apo_csv_store(csv_data, annee_scolaire, sem_id): def apo_csv_store(csv_data: str, annee_scolaire, sem_id):
""" """
csv_data: maquette content, as a string, encoding given by APO_INPUT_ENCODING (latin-1, not utf8) csv_data: maquette content (string)
annee_scolaire: int (2016) annee_scolaire: int (2016)
sem_id: 0 (année ?), 1 (premier semestre de l'année) ou 2 (deuxième semestre) sem_id: 0 (année ?), 1 (premier semestre de l'année) ou 2 (deuxième semestre)
:return: etape_apo du fichier CSV stocké :return: etape_apo du fichier CSV stocké
Note: le fichier CSV est stocké encodé en APO_OUTPUT_ENCODING
""" """
# sanity check # sanity check
filesize = len(csv_data) filesize = len(csv_data)
@ -128,7 +130,8 @@ def apo_csv_store(csv_data, annee_scolaire, sem_id):
oid = "%d-%d" % (annee_scolaire, sem_id) oid = "%d-%d" % (annee_scolaire, sem_id)
description = "%s;%s;%s" % (str(apo_data.etape), annee_scolaire, sem_id) description = "%s;%s;%s" % (str(apo_data.etape), annee_scolaire, sem_id)
archive_id = ApoCSVArchive.create_obj_archive(oid, description) archive_id = ApoCSVArchive.create_obj_archive(oid, description)
ApoCSVArchive.store(archive_id, filename, csv_data) csv_data_bytes = csv_data.encode(sco_apogee_csv.APO_OUTPUT_ENCODING)
ApoCSVArchive.store(archive_id, filename, csv_data_bytes)
return apo_data.etape return apo_data.etape
@ -199,7 +202,7 @@ def apo_csv_get_archive(etape_apo, annee_scolaire="", sem_id=""):
return None return None
def apo_csv_get(etape_apo="", annee_scolaire="", sem_id=""): def apo_csv_get(etape_apo="", annee_scolaire="", sem_id="") -> str:
"""Get CSV data for given etape_apo """Get CSV data for given etape_apo
:return: CSV, as a data string :return: CSV, as a data string
""" """
@ -210,7 +213,9 @@ def apo_csv_get(etape_apo="", annee_scolaire="", sem_id=""):
) )
archive_id = info["archive_id"] archive_id = info["archive_id"]
data = ApoCSVArchive.get(archive_id, etape_apo + ".csv") data = ApoCSVArchive.get(archive_id, etape_apo + ".csv")
return data # ce fichier a été archivé donc généré par ScoDoc
# son encodage est donc APO_OUTPUT_ENCODING
return data.decode(sco_apogee_csv.APO_OUTPUT_ENCODING)
# ------------------------------------------------------------------------ # ------------------------------------------------------------------------

View File

@ -28,22 +28,21 @@
"""ScoDoc : formulaires gestion maquettes Apogee / export resultats """ScoDoc : formulaires gestion maquettes Apogee / export resultats
""" """
from io import StringIO import io
from zipfile import ZipFile from zipfile import ZipFile
import flask import flask
from flask import url_for, g from flask import url_for, g, send_file
# from werkzeug.utils import send_file
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app import log from app import log
from app.scodoc import html_sco_header from app.scodoc import html_sco_header
from app.scodoc import notes_table
from app.scodoc import sco_apogee_csv from app.scodoc import sco_apogee_csv
from app.scodoc import sco_archives
from app.scodoc import sco_etape_apogee from app.scodoc import sco_etape_apogee
from app.scodoc import sco_formations from app.scodoc import sco_formations
from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre
from app.scodoc import sco_formsemestre_status
from app.scodoc import sco_portal_apogee from app.scodoc import sco_portal_apogee
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_semset from app.scodoc import sco_semset
@ -180,15 +179,6 @@ def apo_semset_maq_status(
H.append('<div class="apo_csv_problems"><ul>') H.append('<div class="apo_csv_problems"><ul>')
if len(semset.annees_scolaires()) > 1: if len(semset.annees_scolaires()) > 1:
H.append("""<li>Il y a plusieurs années scolaires !</li>""") H.append("""<li>Il y a plusieurs années scolaires !</li>""")
if nips_no_sco: # seulement un warning
url_list = (
"view_apo_etuds?semset_id=%s&title=Etudiants%%20presents%%20dans%%20maquettes%%20Apogee%%20mais%%20pas%%20dans%%20les%%20semestres%%20ScoDoc:&nips=%s"
% (semset_id, "&nips=".join(nips_no_sco))
)
H.append(
'<li class="apo_csv_warning">Attention: il y a <a href="%s">%d étudiant(s)</a> dans les maquettes Apogée chargées non inscrit(s) dans ce semestre ScoDoc;</li>'
% (url_list, len(nips_no_sco))
)
if etapes_missing_csv: if etapes_missing_csv:
H.append( H.append(
@ -200,9 +190,12 @@ def apo_semset_maq_status(
H.append("<li>%d étudiants ScoDoc sans code NIP</li>" % len(etuds_without_nip)) H.append("<li>%d étudiants ScoDoc sans code NIP</li>" % len(etuds_without_nip))
if nips_no_apo: if nips_no_apo:
url_list = ( url_list = url_for(
"view_scodoc_etuds?semset_id=%s&title=Etudiants%%20ScoDoc%%20non%%20listés%%20dans%%20les%%20maquettes%%20Apogée%%20chargées&nips=%s" "notes.view_scodoc_etuds",
% (semset_id, "&nips=".join(nips_no_apo)) scodoc_dept=g.scodoc_dept,
semset_id=semset_id,
title="Etudiants ScoDoc non listés dans les maquettes Apogée chargées",
nip_list=",".join(nips_no_apo),
) )
H.append( H.append(
'<li><a href="%s">%d étudiants</a> dans ce semestre non présents dans les maquettes Apogée chargées</li>' '<li><a href="%s">%d étudiants</a> dans ce semestre non présents dans les maquettes Apogée chargées</li>'
@ -210,9 +203,12 @@ def apo_semset_maq_status(
) )
if nips_no_sco: # seulement un warning if nips_no_sco: # seulement un warning
url_list = ( url_list = url_for(
"view_apo_etuds?semset_id=%s&title=Etudiants%%20presents%%20dans%%20maquettes%%20Apogee%%20mais%%20pas%%20dans%%20les%%20semestres%%20ScoDoc:&nips=%s" "notes.view_apo_etuds",
% (semset_id, "&nips=".join(nips_no_sco)) scodoc_dept=g.scodoc_dept,
semset_id=semset_id,
title="Etudiants présents dans maquettes Apogée mais pas dans les semestres ScoDoc",
nip_list=",".join(nips_no_sco),
) )
H.append( H.append(
'<li class="apo_csv_warning">Attention: il reste <a href="%s">%d étudiants</a> dans les maquettes Apogée chargées mais pas inscrits dans ce semestre ScoDoc</li>' '<li class="apo_csv_warning">Attention: il reste <a href="%s">%d étudiants</a> dans les maquettes Apogée chargées mais pas inscrits dans ce semestre ScoDoc</li>'
@ -220,9 +216,12 @@ def apo_semset_maq_status(
) )
if apo_dups: if apo_dups:
url_list = "view_apo_etuds?semset_id=%s&title=Doublons%%20Apogee&nips=%s" % ( url_list = url_for(
semset_id, "notes.view_apo_etuds",
"&nips=".join(apo_dups), scodoc_dept=g.scodoc_dept,
semset_id=semset_id,
title="Doublons%%20Apogée",
nip_list=",".join(apo_dups),
) )
H.append( H.append(
'<li><a href="%s">%d étudiants</a> présents dans les <em>plusieurs</em> maquettes Apogée chargées</li>' '<li><a href="%s">%d étudiants</a> présents dans les <em>plusieurs</em> maquettes Apogée chargées</li>'
@ -485,16 +484,18 @@ def table_apo_csv_list(semset, REQUEST=None):
return tab return tab
def view_apo_etuds(semset_id, title="", nips=[], format="html", REQUEST=None): def view_apo_etuds(semset_id, title="", nip_list="", format="html", REQUEST=None):
"""Table des étudiants Apogée par nips""" """Table des étudiants Apogée par nips
nip_list est une chaine, codes nip séparés par des ,
"""
if not semset_id: if not semset_id:
raise ValueError("invalid null semset_id") raise ValueError("invalid null semset_id")
semset = sco_semset.SemSet(semset_id=semset_id) semset = sco_semset.SemSet(semset_id=semset_id)
# annee_scolaire = semset["annee_scolaire"] # annee_scolaire = semset["annee_scolaire"]
# sem_id = semset["sem_id"] # sem_id = semset["sem_id"]
if not isinstance(nip_list, str):
if nips and type(nips) != type([]): nip_list = str(nip_list)
nips = [nips] nips = nip_list.split(",")
etuds = sco_etape_apogee.apo_csv_retreive_etuds_by_nip(semset, nips) etuds = sco_etape_apogee.apo_csv_retreive_etuds_by_nip(semset, nips)
# Ils sont parfois dans ScoDoc même si pas dans le semestre: essaie de les retrouver # Ils sont parfois dans ScoDoc même si pas dans le semestre: essaie de les retrouver
for etud in etuds.values(): for etud in etuds.values():
@ -520,22 +521,12 @@ def view_apo_etuds(semset_id, title="", nips=[], format="html", REQUEST=None):
) )
def view_scodoc_etuds( def view_scodoc_etuds(semset_id, title="", nip_list="", format="html", REQUEST=None):
semset_id, title="", etudids=None, nips=None, format="html", REQUEST=None
):
"""Table des étudiants ScoDoc par nips ou etudids""" """Table des étudiants ScoDoc par nips ou etudids"""
if etudids is not None: if not isinstance(nip_list, str):
if type(etudids) != type([]): nip_list = str(nip_list)
etudids = [etudids] nips = nip_list.split(",")
etuds = [
sco_etud.get_etud_info(etudid=etudid, filled=True)[0] for etudid in etudids
]
elif nips is not None:
if type(nips) != type([]):
nips = [nips]
etuds = [sco_etud.get_etud_info(code_nip=nip, filled=True)[0] for nip in nips] etuds = [sco_etud.get_etud_info(code_nip=nip, filled=True)[0] for nip in nips]
else:
raise ValueError("etudid or NIP must be specified")
for e in etuds: for e in etuds:
tgt = url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=e["etudid"]) tgt = url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=e["etudid"])
@ -809,7 +800,7 @@ def apo_csv_export_results(
annee_scolaire = semset["annee_scolaire"] annee_scolaire = semset["annee_scolaire"]
periode = semset["sem_id"] periode = semset["sem_id"]
data = StringIO() data = io.BytesIO()
dest_zip = ZipFile(data, "w") dest_zip = ZipFile(data, "w")
etapes_apo = sco_etape_apogee.apo_csv_list_stored_etapes( etapes_apo = sco_etape_apogee.apo_csv_list_stored_etapes(
@ -830,6 +821,8 @@ def apo_csv_export_results(
REQUEST=REQUEST, REQUEST=REQUEST,
) )
dest_zip.close()
data.seek(0)
basename = ( basename = (
sco_preferences.get_preference("DeptName") sco_preferences.get_preference("DeptName")
+ str(annee_scolaire) + str(annee_scolaire)
@ -838,12 +831,9 @@ def apo_csv_export_results(
) )
basename = scu.sanitize_filename(scu.unescape_html(basename)) basename = scu.sanitize_filename(scu.unescape_html(basename))
dest_zip.close() return send_file(
size = data.tell() data,
content_type = "application/zip" mimetype="application/zip",
REQUEST.RESPONSE.setHeader( download_name=basename + ".zip",
"content-disposition", 'attachement; filename="%s.zip"' % basename as_attachment=True,
) )
REQUEST.RESPONSE.setHeader("content-type", content_type)
REQUEST.RESPONSE.setHeader("content-length", size)
return data.getvalue()