1
0
forked from ScoDoc/ScoDoc

integration master

This commit is contained in:
Jean-Marie Place 2021-09-17 05:03:34 +02:00
parent 01c264c3c7
commit a23ae38014
3 changed files with 102 additions and 89 deletions

View File

@ -60,7 +60,6 @@ from app.scodoc import sco_pdf
from app.scodoc import sco_xml from app.scodoc import sco_xml
from app.scodoc.sco_pdf import SU from app.scodoc.sco_pdf import SU
from app import log from app import log
from app.scodoc.sco_utils import flaskPDFResponse
def mark_paras(L, tags): def mark_paras(L, tags):
@ -611,7 +610,6 @@ class GenTable(object):
format="html", format="html",
page_title="", page_title="",
filename=None, filename=None,
REQUEST=None,
javascripts=[], javascripts=[],
with_html_headers=True, with_html_headers=True,
publish=True, publish=True,
@ -644,40 +642,55 @@ class GenTable(object):
H.append(html_sco_header.sco_footer()) H.append(html_sco_header.sco_footer())
return "\n".join(H) return "\n".join(H)
elif format == "pdf": elif format == "pdf":
objects = self.pdf() pdf_objs = self.pdf()
doc = sco_pdf.pdf_basic_page( pdf_doc = sco_pdf.pdf_basic_page(
objects, title=title, preferences=self.preferences pdf_objs, title=title, preferences=self.preferences
) )
if publish: if publish:
return scu.PDF_FORMAT.send_file(doc, filename, add_suffix=True, attached=True) return scu.send_file(
# return scu.flaskPDFResponse(doc, filename + ".pdf") pdf_doc,
# return scu.sendPDFFile(REQUEST, doc, filename + ".pdf") filename,
suffix=".pdf",
mime=scu.PDF_MIMETYPE,
attached=True,
)
else: else:
return doc return pdf_doc
elif format == "xls" or format == "xlsx": elif format == "xls" or format == "xlsx": # dans les 2 cas retourne du xlsx
xls = self.excel() xls = self.excel()
if publish: if publish:
return scu.XLSX_FORMAT.send_file(xls, filename, add_suffix=True, attached=True) return scu.send_file(
# return sco_excel.send_from_flask(xls, filename + scu.XLSX_SUFFIX) xls,
# return sco_excel.send_excel_file(REQUEST, xls, filename + scu.XLSX_SUFFIX) filename,
suffix=scu.XLSX_SUFFIX,
mime=scu.XLSX_MIMETYPE,
attached=True,
)
else: else:
return xls return xls
elif format == "text": elif format == "text":
return self.text() return self.text()
elif format == "csv": elif format == "csv":
return scu.CSV_FORMAT.send_file(self.text(), filename, add_suffix=True, attached=True) return scu.send_file(
# return scu.sendCSVFile(REQUEST, self.text(), filename + ".csv") self.text(),
filename,
suffix=".csv",
mime=scu.CSV_MIMETYPE,
attached=True,
)
elif format == "xml": elif format == "xml":
xml = self.xml() xml = self.xml()
if publish: if publish:
# REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE) return scu.send_file(
# return xml xml, filename, suffix=".xml", mime=scu.XML_MIMETYPE, attached=True
return scu.XML_FORMAT.send_file(self.xml) )
return xml
elif format == "json": elif format == "json":
js = self.json() js = self.json()
if publish: if publish:
return scu.JSON_FORMAT.send_file(self.xml) return scu.send_file(
# REQUEST.RESPONSE.setHeader("content-type", scu.JSON_MIMETYPE) js, filename, suffix=".json", mime=scu.JSON_MIMETYPE, attached=True
)
return js return js
else: else:
log("make_page: format=%s" % format) log("make_page: format=%s" % format)
@ -738,5 +751,5 @@ if __name__ == "__main__":
document.build(objects) document.build(objects)
data = doc.getvalue() data = doc.getvalue()
open("/tmp/gen_table.pdf", "wb").write(data) 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) open("toto.pdf", "wb").write(p)

View File

@ -31,7 +31,7 @@ import datetime
import operator import operator
import pprint import pprint
import time import time
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error import urllib
import flask import flask
from flask import url_for from flask import url_for
@ -921,7 +921,7 @@ def formsemestre_evaluations_delai_correction(
+ "", + "",
filename=scu.make_filename("evaluations_delais_" + sem["titreannee"]), 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): def module_evaluation_insert_before(ModEvals, next_eval):
@ -1046,7 +1046,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True):
resp = u["prenomnom"] resp = u["prenomnom"]
nomcomplet = u["nomcomplet"] nomcomplet = u["nomcomplet"]
can_edit = sco_permissions_check.can_edit_notes( can_edit = sco_permissions_check.can_edit_notes(
, moduleimpl_id, allow_ens=False current_user, moduleimpl_id, allow_ens=False
) )
link = ( link = (
@ -1089,7 +1089,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True):
% ( % (
scu.ScoURL(), scu.ScoURL(),
group_id, group_id,
six.moves.urllib.parse.quote(E["jour"], safe=""), urllib.parse.quote(E["jour"], safe=""),
) )
) )
H.append( H.append(

View File

@ -49,11 +49,12 @@ import unicodedata
import urllib import urllib
from xml.etree import ElementTree 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 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 config import Config
from app import log from app import log
@ -64,6 +65,7 @@ from app.scodoc import sco_exceptions
from app.scodoc import sco_xml from app.scodoc import sco_xml
import sco_version import sco_version
# ----- CALCUL ET PRESENTATION DES NOTES # ----- CALCUL ET PRESENTATION DES NOTES
NOTES_PRECISION = 1e-4 # evite eventuelles erreurs d'arrondis NOTES_PRECISION = 1e-4 # evite eventuelles erreurs d'arrondis
NOTES_MIN = 0.0 # valeur minimale admise pour une note (sauf malus, dans [-20, 20]) 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_SUPPRESS = -1001.0 # note a supprimer
NOTES_ATTENTE = -1002.0 # note "en attente" (se calcule comme une note neutralisee) NOTES_ATTENTE = -1002.0 # note "en attente" (se calcule comme une note neutralisee)
# Types de modules # Types de modules
MODULE_STANDARD = 0 MODULE_STANDARD = 0
MODULE_MALUS = 1 MODULE_MALUS = 1
@ -86,7 +89,7 @@ IT_SITUATION_MISSING_STR = (
"____" # shown on ficheEtud (devenir) in place of empty situation "____" # 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 # borne supérieure de chaque mention
NOTES_MENTIONS_TH = ( 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") SCODOC_LOGOS_DIR = os.path.join(SCODOC_CFG_DIR, "logos")
LOGOS_IMAGES_ALLOWED_TYPES = ("jpg", "jpeg", "png") # remind that PIL does not read pdf LOGOS_IMAGES_ALLOWED_TYPES = ("jpg", "jpeg", "png") # remind that PIL does not read pdf
# ----- Les outils distribués # ----- Les outils distribués
SCO_TOOLS_DIR = os.path.join(Config.SCODOC_DIR, "tools") SCO_TOOLS_DIR = os.path.join(Config.SCODOC_DIR, "tools")
# ----- Lecture du fichier de configuration # ----- Lecture du fichier de configuration
from app.scodoc import sco_config from app.scodoc import sco_config
from app.scodoc import sco_config_load from app.scodoc import sco_config_load
@ -268,6 +273,7 @@ else:
SCO_ENCODING = "utf-8" # used by Excel, XML, PDF, ... SCO_ENCODING = "utf-8" # used by Excel, XML, PDF, ...
SCO_DEFAULT_SQL_USER = "scodoc" # should match Zope process UID SCO_DEFAULT_SQL_USER = "scodoc" # should match Zope process UID
SCO_DEFAULT_SQL_PORT = "5432" SCO_DEFAULT_SQL_PORT = "5432"
SCO_DEFAULT_SQL_USERS_CNX = "dbname=SCOUSERS port=%s" % SCO_DEFAULT_SQL_PORT 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_FIELDSEP = ";"
CSV_LINESEP = "\n" CSV_LINESEP = "\n"
CSV_MIMETYPE = "text/comma-separated-values" 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" XLS_MIMETYPE = "application/vnd.ms-excel"
XLSX_MIMETYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" XLSX_MIMETYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
XLSX_SUFFIX = ".xlsx" XLSX_SUFFIX = ".xlsx"
PDF_MIMETYPE = "application/pdf"
PDF_SUFFIX = ".pdf"
XML_MIMETYPE = "text/xml" XML_MIMETYPE = "text/xml"
XML_SUFFIX = ".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 # Admissions des étudiants
# Différents types de voies d'admission: # 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") 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 # Support for ScoDoc7 compatibility
@ -347,8 +346,8 @@ def ScoURL():
= page accueil département = page accueil département
""" """
return url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)[ return url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)[
: -len("/index_html") : -len("/index_html")
] ]
def NotesURL(): def NotesURL():
@ -375,8 +374,8 @@ def EntreprisesURL():
def AbsencesURL(): def AbsencesURL():
"""URL of Absences""" """URL of Absences"""
return url_for("absences.index_html", scodoc_dept=g.scodoc_dept)[ return url_for("absences.index_html", scodoc_dept=g.scodoc_dept)[
: -len("/index_html") : -len("/index_html")
] ]
def UsersURL(): def UsersURL():
@ -462,8 +461,8 @@ def suppress_accents(s):
if isinstance(s, str): if isinstance(s, str):
return ( return (
unicodedata.normalize("NFD", s) unicodedata.normalize("NFD", s)
.encode("ascii", "ignore") .encode("ascii", "ignore")
.decode(SCO_ENCODING) .decode(SCO_ENCODING)
) )
return s # may be int return s # may be int
@ -513,7 +512,7 @@ def is_valid_filename(filename):
return VALID_EXP.match(filename) return VALID_EXP.match(filename)
def sendCSVFile(REQUEST, data, filename): def sendCSVFile(REQUEST, data, filename): # DEPRECATED ne plus utiliser
"""publication fichier. """publication fichier.
(on ne doit rien avoir émis avant, car ici sont générés les entetes) (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 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): def sendPDFFile(REQUEST, data, filename):
filename = ( filename = (
unescape_html(suppress_accents(filename)).replace("&", "").replace(" ", "_") unescape_html(suppress_accents(filename)).replace("&", "").replace(" ", "_")
@ -559,19 +550,16 @@ class ScoDocJSONEncoder(json.JSONEncoder):
def sendJSON(REQUEST, data): def sendJSON(REQUEST, data):
js = json.dumps(data, indent=1, cls=ScoDocJSONEncoder) js = json.dumps(data, indent=1, cls=ScoDocJSONEncoder)
if REQUEST: return send_file(js, filename="sco_data.json", mime=JSON_MIMETYPE, attached=False)
REQUEST.RESPONSE.setHeader("content-type", JSON_MIMETYPE)
return js
def sendXML(REQUEST, data, tagname=None, force_outer_xml_tag=True): def sendXML(REQUEST, data, tagname=None, force_outer_xml_tag=True):
if type(data) != list: if type(data) != list:
data = [data] # always list-of-dicts data = [data] # always list-of-dicts
if force_outer_xml_tag: if force_outer_xml_tag:
root_tagname = tagname + "_list" data = [{tagname: data}]
data = [{root_tagname: data}] tagname += "_list"
doc = sco_xml.simple_dictlist2xml(data, tagname=tagname) doc = sco_xml.simple_dictlist2xml(data, tagname=tagname)
if REQUEST: if REQUEST:
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE) REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE)
return doc 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) 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(): def get_scodoc_version():
"return a string identifying ScoDoc version" "return a string identifying ScoDoc version"
return sco_version.SCOVERSION return sco_version.SCOVERSION
@ -672,7 +672,7 @@ def sem_decale_str(sem):
if sem["semestre_id"] <= 0: if sem["semestre_id"] <= 0:
return "" return ""
if (sem["semestre_id"] % 2 and sem["mois_debut_ord"] <= 6) or ( 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" return "D"
else: else:
@ -831,15 +831,15 @@ def return_text_if_published(val, REQUEST):
def confirm_dialog( def confirm_dialog(
message="<p>Confirmer ?</p>", message="<p>Confirmer ?</p>",
OK="OK", OK="OK",
Cancel="Annuler", Cancel="Annuler",
dest_url="", dest_url="",
cancel_url="", cancel_url="",
target_variable="dialog_confirmed", target_variable="dialog_confirmed",
parameters={}, parameters={},
add_headers=True, # complete page add_headers=True, # complete page
helpmsg=None, helpmsg=None,
): ):
from app.scodoc import html_sco_header from app.scodoc import html_sco_header
@ -881,7 +881,7 @@ def confirm_dialog(
H.append('<p class="help">' + helpmsg + "</p>") H.append('<p class="help">' + helpmsg + "</p>")
if add_headers: if add_headers:
return ( 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: else:
return "\n".join(H) return "\n".join(H)