diff --git a/app/scodoc/gen_tables.py b/app/scodoc/gen_tables.py index e594cdb2d..67a52261c 100644 --- a/app/scodoc/gen_tables.py +++ b/app/scodoc/gen_tables.py @@ -60,7 +60,6 @@ from app.scodoc import sco_pdf from app.scodoc import sco_xml from app.scodoc.sco_pdf import SU from app import log -from app.scodoc.sco_utils import flaskPDFResponse def mark_paras(L, tags): @@ -611,7 +610,6 @@ class GenTable(object): format="html", page_title="", filename=None, - REQUEST=None, javascripts=[], with_html_headers=True, publish=True, @@ -644,40 +642,55 @@ class GenTable(object): H.append(html_sco_header.sco_footer()) return "\n".join(H) elif format == "pdf": - objects = self.pdf() - doc = sco_pdf.pdf_basic_page( - objects, title=title, preferences=self.preferences + pdf_objs = self.pdf() + pdf_doc = sco_pdf.pdf_basic_page( + pdf_objs, title=title, preferences=self.preferences ) if publish: - return scu.PDF_FORMAT.send_file(doc, filename, add_suffix=True, attached=True) - # return scu.flaskPDFResponse(doc, filename + ".pdf") - # return scu.sendPDFFile(REQUEST, doc, filename + ".pdf") + return scu.send_file( + pdf_doc, + filename, + suffix=".pdf", + mime=scu.PDF_MIMETYPE, + attached=True, + ) else: - return doc - elif format == "xls" or format == "xlsx": + return pdf_doc + elif format == "xls" or format == "xlsx": # dans les 2 cas retourne du xlsx xls = self.excel() if publish: - return scu.XLSX_FORMAT.send_file(xls, filename, add_suffix=True, attached=True) - # return sco_excel.send_from_flask(xls, filename + scu.XLSX_SUFFIX) - # return sco_excel.send_excel_file(REQUEST, xls, filename + scu.XLSX_SUFFIX) + return scu.send_file( + xls, + filename, + suffix=scu.XLSX_SUFFIX, + mime=scu.XLSX_MIMETYPE, + attached=True, + ) else: return xls elif format == "text": return self.text() elif format == "csv": - return scu.CSV_FORMAT.send_file(self.text(), filename, add_suffix=True, attached=True) - # return scu.sendCSVFile(REQUEST, self.text(), filename + ".csv") + return scu.send_file( + self.text(), + filename, + suffix=".csv", + mime=scu.CSV_MIMETYPE, + attached=True, + ) elif format == "xml": xml = self.xml() if publish: - # REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE) - # return xml - return scu.XML_FORMAT.send_file(self.xml) + return scu.send_file( + xml, filename, suffix=".xml", mime=scu.XML_MIMETYPE, attached=True + ) + return xml elif format == "json": js = self.json() if publish: - return scu.JSON_FORMAT.send_file(self.xml) - # REQUEST.RESPONSE.setHeader("content-type", scu.JSON_MIMETYPE) + return scu.send_file( + js, filename, suffix=".json", mime=scu.JSON_MIMETYPE, attached=True + ) return js else: log("make_page: format=%s" % format) @@ -738,5 +751,5 @@ if __name__ == "__main__": document.build(objects) data = doc.getvalue() open("/tmp/gen_table.pdf", "wb").write(data) - p = T.make_page(format="pdf", REQUEST=None) + p = T.make_page(format="pdf") open("toto.pdf", "wb").write(p) diff --git a/app/scodoc/sco_evaluations.py b/app/scodoc/sco_evaluations.py index 3697d598a..2397886d0 100644 --- a/app/scodoc/sco_evaluations.py +++ b/app/scodoc/sco_evaluations.py @@ -31,7 +31,7 @@ import datetime import operator import pprint import time -import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error +import urllib import flask from flask import url_for @@ -921,7 +921,7 @@ def formsemestre_evaluations_delai_correction( + "", filename=scu.make_filename("evaluations_delais_" + sem["titreannee"]), ) - return tab.make_page(format=format, REQUEST=REQUEST) + return tab.make_page(format=format) def module_evaluation_insert_before(ModEvals, next_eval): @@ -1046,7 +1046,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True): resp = u["prenomnom"] nomcomplet = u["nomcomplet"] can_edit = sco_permissions_check.can_edit_notes( - , moduleimpl_id, allow_ens=False + current_user, moduleimpl_id, allow_ens=False ) link = ( @@ -1089,7 +1089,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True): % ( scu.ScoURL(), group_id, - six.moves.urllib.parse.quote(E["jour"], safe=""), + urllib.parse.quote(E["jour"], safe=""), ) ) H.append( diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index 96a4d796c..087902a7b 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -49,11 +49,12 @@ import unicodedata import urllib from xml.etree import ElementTree -from flask import g, current_app, make_response +from flask import g, current_app from PIL import Image as PILImage -from flask import g, url_for, request +from flask import g, url_for, request, make_response +from werkzeug.wrappers import response from config import Config from app import log @@ -64,6 +65,7 @@ from app.scodoc import sco_exceptions from app.scodoc import sco_xml import sco_version + # ----- CALCUL ET PRESENTATION DES NOTES NOTES_PRECISION = 1e-4 # evite eventuelles erreurs d'arrondis NOTES_MIN = 0.0 # valeur minimale admise pour une note (sauf malus, dans [-20, 20]) @@ -72,6 +74,7 @@ NOTES_NEUTRALISE = -1000.0 # notes non prises en comptes dans moyennes NOTES_SUPPRESS = -1001.0 # note a supprimer NOTES_ATTENTE = -1002.0 # note "en attente" (se calcule comme une note neutralisee) + # Types de modules MODULE_STANDARD = 0 MODULE_MALUS = 1 @@ -86,7 +89,7 @@ IT_SITUATION_MISSING_STR = ( "____" # shown on ficheEtud (devenir) in place of empty situation ) -RANG_ATTENTE_STR = "(attente)" # rang affiché sur bulletins quand notes en attente +RANG_ATTENTE_STR = "(attente)" # rang affiché sur bulletins quand notes en attente # borne supérieure de chaque mention NOTES_MENTIONS_TH = ( @@ -232,9 +235,11 @@ if not os.path.exists(SCO_TMP_DIR): SCODOC_LOGOS_DIR = os.path.join(SCODOC_CFG_DIR, "logos") LOGOS_IMAGES_ALLOWED_TYPES = ("jpg", "jpeg", "png") # remind that PIL does not read pdf + # ----- Les outils distribués SCO_TOOLS_DIR = os.path.join(Config.SCODOC_DIR, "tools") + # ----- Lecture du fichier de configuration from app.scodoc import sco_config from app.scodoc import sco_config_load @@ -268,6 +273,7 @@ else: SCO_ENCODING = "utf-8" # used by Excel, XML, PDF, ... + SCO_DEFAULT_SQL_USER = "scodoc" # should match Zope process UID SCO_DEFAULT_SQL_PORT = "5432" SCO_DEFAULT_SQL_USERS_CNX = "dbname=SCOUSERS port=%s" % SCO_DEFAULT_SQL_PORT @@ -295,15 +301,33 @@ SCO_DUMP_UP_URL = "https://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/upload- CSV_FIELDSEP = ";" CSV_LINESEP = "\n" CSV_MIMETYPE = "text/comma-separated-values" +CSV_SUFFIX = ".csv" +JSON_MIMETYPE = "application/json" +JSON_SUFFIX = ".json" +PDF_MIMETYPE = "application/pdf" +PDF_SUFFIX = ".pdf" XLS_MIMETYPE = "application/vnd.ms-excel" XLSX_MIMETYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" XLSX_SUFFIX = ".xlsx" -PDF_MIMETYPE = "application/pdf" -PDF_SUFFIX = ".pdf" XML_MIMETYPE = "text/xml" XML_SUFFIX = ".xml" -JSON_MIMETYPE = "application/json" -JSON_SUFFIX = ".json" + + +def get_mime_suffix(format_code: str) -> tuple[str, str]: + """Returns (MIME, SUFFIX) from format_code == "xls", "xml", ... + SUFFIX includes the dot: ".xlsx", ".xml", ... + "xls" and "xlsx" format codes give XLSX + """ + d = { + "csv": (CSV_MIMETYPE, CSV_SUFFIX), + "xls": (XLSX_MIMETYPE, XLSX_SUFFIX), + "xlsx": (XLSX_MIMETYPE, XLSX_SUFFIX), + "pdf": (PDF_MIMETYPE, PDF_SUFFIX), + "xml": (XML_MIMETYPE, XML_SUFFIX), + "json": (JSON_MIMETYPE, JSON_SUFFIX), + } + return d[format_code] + # Admissions des étudiants # Différents types de voies d'admission: @@ -313,31 +337,6 @@ TYPES_ADMISSION = (TYPE_ADMISSION_DEFAULT, "APB", "APB-PC", "CEF", "Direct") BULLETINS_VERSIONS = ("short", "selectedevals", "long") - -# File format management -class FileFormat: - def __init__(self, suffix, mime): - self._mime = mime - self._suffix = suffix - - def send_file(self, data, filename, add_suffix=False, attached=False): - filename = make_filename(filename) - if add_suffix: - filename += self._suffix - response = make_response(data) - response.headers['Content-Type'] = self._mime - if attached: - response.headers['Content-Disposition'] = 'attachment; filename="%s"' % filename - return response - - -CSV_FORMAT = FileFormat(CSV_SUFFIX, CSV_MIMETYPE) -XLSX_FORMAT = FileFormat(XLSX_SUFFIX, XLSX_MIMETYPE) -XLS_FORMAT = FileFormat(XLS_SUFFIX, XLS_MIMETYPE) -PDF_FORMAT = FileFormat(PDF_SUFFIX, PDF_MIMETYPE) -XML_FORMAT = FileFormat(XML_SUFFIX, XML_MIMETYPE) -JSON_FORMAT = FileFormat(JSON_SUFFIX, JSON_MIMETYPE) - # Support for ScoDoc7 compatibility @@ -347,8 +346,8 @@ def ScoURL(): = page accueil département """ return url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)[ - : -len("/index_html") - ] + : -len("/index_html") + ] def NotesURL(): @@ -375,8 +374,8 @@ def EntreprisesURL(): def AbsencesURL(): """URL of Absences""" return url_for("absences.index_html", scodoc_dept=g.scodoc_dept)[ - : -len("/index_html") - ] + : -len("/index_html") + ] def UsersURL(): @@ -462,8 +461,8 @@ def suppress_accents(s): if isinstance(s, str): return ( unicodedata.normalize("NFD", s) - .encode("ascii", "ignore") - .decode(SCO_ENCODING) + .encode("ascii", "ignore") + .decode(SCO_ENCODING) ) return s # may be int @@ -513,7 +512,7 @@ def is_valid_filename(filename): return VALID_EXP.match(filename) -def sendCSVFile(REQUEST, data, filename): +def sendCSVFile(REQUEST, data, filename): # DEPRECATED ne plus utiliser """publication fichier. (on ne doit rien avoir émis avant, car ici sont générés les entetes) """ @@ -527,14 +526,6 @@ def sendCSVFile(REQUEST, data, filename): return data -def flaskPDFResponse(data, filename, mime=PDF_MIMETYPE): - filename = make_filename(filename) - response = make_response(data) - response.headers['Content-Type'] = mime - response.headers['Content-Disposition'] = 'attachment; filename="%s"' % filename - return response - - def sendPDFFile(REQUEST, data, filename): filename = ( unescape_html(suppress_accents(filename)).replace("&", "").replace(" ", "_") @@ -559,19 +550,16 @@ class ScoDocJSONEncoder(json.JSONEncoder): def sendJSON(REQUEST, data): js = json.dumps(data, indent=1, cls=ScoDocJSONEncoder) - if REQUEST: - REQUEST.RESPONSE.setHeader("content-type", JSON_MIMETYPE) - return js + return send_file(js, filename="sco_data.json", mime=JSON_MIMETYPE, attached=False) def sendXML(REQUEST, data, tagname=None, force_outer_xml_tag=True): if type(data) != list: data = [data] # always list-of-dicts if force_outer_xml_tag: - root_tagname = tagname + "_list" - data = [{root_tagname: data}] + data = [{tagname: data}] + tagname += "_list" doc = sco_xml.simple_dictlist2xml(data, tagname=tagname) - if REQUEST: REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE) return doc @@ -590,6 +578,18 @@ def sendResult(REQUEST, data, name=None, format=None, force_outer_xml_tag=True): raise ValueError("invalid format: %s" % format) +def send_file(data, filename, suffix="", mime=None, attached=True): + "Build Flask Response for file download of given type" + if suffix: + filename += suffix + filename = make_filename(filename) + response = make_response(data) + response.headers["Content-Type"] = mime + if attached: + response.headers["Content-Disposition"] = 'attachment; filename="%s"' % filename + return response + + def get_scodoc_version(): "return a string identifying ScoDoc version" return sco_version.SCOVERSION @@ -672,7 +672,7 @@ def sem_decale_str(sem): if sem["semestre_id"] <= 0: return "" if (sem["semestre_id"] % 2 and sem["mois_debut_ord"] <= 6) or ( - not sem["semestre_id"] % 2 and sem["mois_debut_ord"] > 6 + not sem["semestre_id"] % 2 and sem["mois_debut_ord"] > 6 ): return "D" else: @@ -831,15 +831,15 @@ def return_text_if_published(val, REQUEST): def confirm_dialog( - message="
Confirmer ?
", - OK="OK", - Cancel="Annuler", - dest_url="", - cancel_url="", - target_variable="dialog_confirmed", - parameters={}, - add_headers=True, # complete page - helpmsg=None, + message="Confirmer ?
", + OK="OK", + Cancel="Annuler", + dest_url="", + cancel_url="", + target_variable="dialog_confirmed", + parameters={}, + add_headers=True, # complete page + helpmsg=None, ): from app.scodoc import html_sco_header @@ -881,7 +881,7 @@ def confirm_dialog( H.append('' + helpmsg + "
") if add_headers: return ( - html_sco_header.sco_header() + "\n".join(H) + html_sco_header.sco_footer() + html_sco_header.sco_header() + "\n".join(H) + html_sco_header.sco_footer() ) else: return "\n".join(H)