Merge branch 'master' into dyn_fields

# Conflicts:
#	app/scodoc/sco_placement.py
#	app/static/css/scodoc.css
#	app/templates/scodoc/forms/placement.html
#	tests/unit/test_abs_demijournee.py
#	tests/unit/test_formations.py
This commit is contained in:
Jean-Marie Place 2021-09-24 10:29:24 +02:00
commit 1af6f79da9
86 changed files with 3738 additions and 3588 deletions

View File

@ -25,7 +25,7 @@ from flask_moment import Moment
from flask_caching import Cache
import sqlalchemy
from app.scodoc.sco_exceptions import ScoValueError, APIInvalidParams
from app.scodoc.sco_exceptions import ScoGenError, ScoValueError, APIInvalidParams
from config import DevConfig
import sco_version
@ -163,6 +163,7 @@ def create_app(config_class=DevConfig):
cache.init_app(app)
sco_cache.CACHE = cache
app.register_error_handler(ScoGenError, handle_sco_value_error)
app.register_error_handler(ScoValueError, handle_sco_value_error)
app.register_error_handler(500, internal_server_error)
app.register_error_handler(503, postgresql_server_error)

View File

@ -46,17 +46,17 @@ class ZRequest(object):
# if current_app.config["DEBUG"]:
# le ReverseProxied se charge maintenant de mettre le bon protocole http ou https
self.URL = request.base_url
self.BASE0 = request.url_root
# self.URL = request.base_url
# self.BASE0 = request.url_root
# else:
# self.URL = request.base_url.replace("http://", "https://")
# self.BASE0 = request.url_root.replace("http://", "https://")
self.URL0 = self.URL
# self.URL0 = self.URL
# query_string is bytes:
self.QUERY_STRING = request.query_string.decode("utf-8")
self.REQUEST_METHOD = request.method
self.AUTHENTICATED_USER = current_user
self.REMOTE_ADDR = request.remote_addr
# self.QUERY_STRING = request.query_string.decode("utf-8")
# self.REQUEST_METHOD = request.method
# self.AUTHENTICATED_USER = current_user
# self.REMOTE_ADDR = request.remote_addr
if request.method == "POST":
# request.form is a werkzeug.datastructures.ImmutableMultiDict
# must copy to get a mutable version (needed by TrivialFormulator)
@ -74,16 +74,13 @@ class ZRequest(object):
if k.endswith(":list"):
self.form[k[:-5]] = request.args.getlist(k)
else:
self.form[k] = request.args[k]
values = request.args.getlist(k)
self.form[k] = values[0] if len(values) == 1 else values
# current_app.logger.info("ZRequest.form=%s" % str(self.form))
self.RESPONSE = ZResponse()
def __str__(self):
return """REQUEST
URL={r.URL}
QUERY_STRING={r.QUERY_STRING}
REQUEST_METHOD={r.REQUEST_METHOD}
AUTHENTICATED_USER={r.AUTHENTICATED_USER}
return """ZREQUEST
form={r.form}
""".format(
r=self
@ -233,6 +230,7 @@ def scodoc7func(func):
if arg_name == "REQUEST": # special case
pos_arg_values.append(REQUEST)
else:
# peut produire une KeyError s'il manque un argument attendu:
v = req_args[arg_name]
# try to convert all arguments to INTEGERS
# necessary for db ids and boolean values

View File

@ -11,7 +11,7 @@ class NotesFormation(db.Model):
"""Programme pédagogique d'une formation"""
__tablename__ = "notes_formations"
__table_args__ = (db.UniqueConstraint("acronyme", "titre", "version"),)
__table_args__ = (db.UniqueConstraint("dept_id", "acronyme", "titre", "version"),)
id = db.Column(db.Integer, primary_key=True)
formation_id = db.synonym("id")

View File

@ -386,8 +386,8 @@ def bonus_demo(notes_sport, coefs, infos=None):
qui est ECRASE à chaque appel.
*** Ne pas utiliser en production !!! ***
"""
f = open("/tmp/scodoc_bonus.log", "w") # mettre 'a' pour ajouter en fin
f.write("\n---------------\n" + pprint.pformat(infos) + "\n")
with open("/tmp/scodoc_bonus.log", "w") as f: # mettre 'a' pour ajouter en fin
f.write("\n---------------\n" + pprint.pformat(infos) + "\n")
# Statut de chaque UE
# for ue_id in infos['moy_ues']:
# ue_status = infos['moy_ues'][ue_id]

View File

@ -573,7 +573,7 @@ class GenTable(object):
"""
doc = ElementTree.Element(
self.xml_outer_tag,
id=self.table_id,
id=str(self.table_id),
origin=self.origin or "",
caption=self.caption or "",
)
@ -587,7 +587,7 @@ class GenTable(object):
v = row.get(cid, "")
if v is None:
v = ""
x_cell = ElementTree.Element(cid, value=str(v))
x_cell = ElementTree.Element(str(cid), value=str(v))
x_row.append(x_cell)
return sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(scu.SCO_ENCODING)
@ -652,7 +652,6 @@ class GenTable(object):
filename,
suffix=".pdf",
mime=scu.PDF_MIMETYPE,
attached=True,
)
else:
return pdf_doc
@ -664,7 +663,6 @@ class GenTable(object):
filename,
suffix=scu.XLSX_SUFFIX,
mime=scu.XLSX_MIMETYPE,
attached=True,
)
else:
return xls
@ -682,14 +680,14 @@ class GenTable(object):
xml = self.xml()
if publish:
return scu.send_file(
xml, filename, suffix=".xml", mime=scu.XML_MIMETYPE, attached=True
xml, filename, suffix=".xml", mime=scu.XML_MIMETYPE
)
return xml
elif format == "json":
js = self.json()
if publish:
return scu.send_file(
js, filename, suffix=".json", mime=scu.JSON_MIMETYPE, attached=True
js, filename, suffix=".json", mime=scu.JSON_MIMETYPE
)
return js
else:

View File

@ -30,7 +30,7 @@
"""
import datetime
from flask import url_for, g
from flask import url_for, g, request
import app.scodoc.sco_utils as scu
from app.scodoc import notesdb as ndb
@ -814,8 +814,8 @@ def ListeAbsEtud(
etudid, datedebut, with_evals=with_evals, format=format
)
if REQUEST:
base_url_nj = "%s?etudid=%s&absjust_only=0" % (REQUEST.URL0, etudid)
base_url_j = "%s?etudid=%s&absjust_only=1" % (REQUEST.URL0, etudid)
base_url_nj = "%s?etudid=%s&absjust_only=0" % (request.base_url, etudid)
base_url_j = "%s?etudid=%s&absjust_only=1" % (request.base_url, etudid)
else:
base_url_nj = base_url_j = ""
tab_absnonjust = GenTable(
@ -928,11 +928,11 @@ def _TablesAbsEtud(
cursor.execute(
"""SELECT mi.moduleimpl_id
FROM absences abs, notes_moduleimpl_inscription mi, notes_moduleimpl m
WHERE abs.matin = %(matin)s
and abs.jour = %(jour)s
and abs.etudid = %(etudid)s
and abs.moduleimpl_id = mi.moduleimpl_id
and mi.moduleimpl_id = m.id
WHERE abs.matin = %(matin)s
and abs.jour = %(jour)s
and abs.etudid = %(etudid)s
and abs.moduleimpl_id = mi.moduleimpl_id
and mi.moduleimpl_id = m.id
and mi.etudid = %(etudid)s
""",
{
@ -959,8 +959,9 @@ def _TablesAbsEtud(
)[0]
if format == "html":
ex.append(
'<a href="Notes/moduleimpl_status?moduleimpl_id=%s">%s</a>'
% (mod["moduleimpl_id"], mod["module"]["code"])
f"""<a href="{url_for('notes.moduleimpl_status',
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])}
">{mod["module"]["code"]}</a>"""
)
else:
ex.append(mod["module"]["code"])
@ -976,8 +977,9 @@ def _TablesAbsEtud(
)[0]
if format == "html":
ex.append(
'<a href="Notes/moduleimpl_status?moduleimpl_id=%s">%s</a>'
% (mod["moduleimpl_id"], mod["module"]["code"])
f"""<a href="{url_for('notes.moduleimpl_status',
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])}
">{mod["module"]["code"]}</a>"""
)
else:
ex.append(mod["module"]["code"])

View File

@ -29,19 +29,19 @@
Archives are plain files, stored in
<SCODOC_VAR_DIR>/archives/<deptid>
(where <SCODOC_VAR_DIR> is usually /opt/scodoc-data, and <deptid> a departement id)
<SCODOC_VAR_DIR>/archives/<dept_id>
(where <SCODOC_VAR_DIR> is usually /opt/scodoc-data, and <dept_id> a departement id (int))
Les PV de jurys et documents associés sont stockées dans un sous-repertoire de la forme
<archivedir>/<dept>/<formsemestre_id>/<YYYY-MM-DD-HH-MM-SS>
(formsemestre_id est ici FormSemestre.id)
Les documents liés à l'étudiant sont dans
<archivedir>/docetuds/<dept>/<etudid>/<YYYY-MM-DD-HH-MM-SS>
<archivedir>/docetuds/<dept_id>/<etudid>/<YYYY-MM-DD-HH-MM-SS>
(etudid est ici Identite.id)
Les maquettes Apogée pour l'export des notes sont dans
<archivedir>/apo_csv/<dept>/<annee_scolaire>-<sem_id>/<YYYY-MM-DD-HH-MM-SS>/<code_etape>.csv
<archivedir>/apo_csv/<dept_id>/<annee_scolaire>-<sem_id>/<YYYY-MM-DD-HH-MM-SS>/<code_etape>.csv
Un répertoire d'archive contient des fichiers quelconques, et un fichier texte nommé _description.txt
qui est une description (humaine, format libre) de l'archive.
@ -56,11 +56,13 @@ import shutil
import time
import flask
from flask import g
from flask import g, request
from flask_login import current_user
import app.scodoc.sco_utils as scu
from config import Config
from app import log
from app.models import Departement
from app.scodoc.TrivialFormulator import TrivialFormulator
from app.scodoc.sco_exceptions import (
AccessDenied,
@ -109,7 +111,8 @@ class BaseArchiver(object):
If directory does not yet exist, create it.
"""
self.initialize()
dept_dir = os.path.join(self.root, g.scodoc_dept)
dept = Departement.query.filter_by(acronym=g.scodoc_dept).first()
dept_dir = os.path.join(self.root, str(dept.id))
try:
scu.GSL.acquire()
if not os.path.isdir(dept_dir):
@ -128,7 +131,8 @@ class BaseArchiver(object):
:return: list of archive oids
"""
self.initialize()
base = os.path.join(self.root, g.scodoc_dept) + os.path.sep
dept = Departement.query.filter_by(acronym=g.scodoc_dept).first()
base = os.path.join(self.root, str(dept.id)) + os.path.sep
dirs = glob.glob(base + "*")
return [os.path.split(x)[1] for x in dirs]
@ -379,9 +383,7 @@ def formsemestre_archive(REQUEST, formsemestre_id, group_ids=[]):
(all students or only selected groups)
"""
if not sco_permissions_check.can_edit_pv(formsemestre_id):
raise AccessDenied(
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
)
raise AccessDenied("opération non autorisée pour %s" % str(current_user))
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
if not group_ids:
@ -454,7 +456,7 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement.
)
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
descr,
cancelbutton="Annuler",
@ -556,9 +558,7 @@ def formsemestre_delete_archive(
):
"""Delete an archive"""
if not sco_permissions_check.can_edit_pv(formsemestre_id):
raise AccessDenied(
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
)
raise AccessDenied("opération non autorisée pour %s" % str(current_user))
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
sem_archive_id = formsemestre_id
archive_id = PVArchive.get_id_from_name(sem_archive_id, archive_name)

View File

@ -30,7 +30,8 @@
les dossiers d'admission et autres pièces utiles.
"""
import flask
from flask import url_for, g
from flask import url_for, g, request
from flask_login import current_user
import app.scodoc.sco_utils as scu
from app.scodoc import sco_import_etuds
@ -60,7 +61,7 @@ def can_edit_etud_archive(authuser):
def etud_list_archives_html(REQUEST, etudid):
"""HTML snippet listing archives"""
can_edit = can_edit_etud_archive(REQUEST.AUTHENTICATED_USER)
can_edit = can_edit_etud_archive(current_user)
etuds = sco_etud.get_etud_info(etudid=etudid)
if not etuds:
raise ScoValueError("étudiant inexistant")
@ -133,10 +134,8 @@ def add_archives_info_to_etud_list(etuds):
def etud_upload_file_form(REQUEST, etudid):
"""Page with a form to choose and upload a file, with a description."""
# check permission
if not can_edit_etud_archive(REQUEST.AUTHENTICATED_USER):
raise AccessDenied(
"opération non autorisée pour %s" % REQUEST.AUTHENTICATED_USER
)
if not can_edit_etud_archive(current_user):
raise AccessDenied("opération non autorisée pour %s" % current_user)
etuds = sco_etud.get_etud_info(filled=True)
if not etuds:
raise ScoValueError("étudiant inexistant")
@ -153,7 +152,7 @@ def etud_upload_file_form(REQUEST, etudid):
% (scu.CONFIG.ETUD_MAX_FILE_SIZE // (1024 * 1024)),
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("etudid", {"default": etudid, "input_type": "hidden"}),
@ -202,10 +201,8 @@ def _store_etud_file_to_new_archive(etud_archive_id, data, filename, description
def etud_delete_archive(REQUEST, etudid, archive_name, dialog_confirmed=False):
"""Delete an archive"""
# check permission
if not can_edit_etud_archive(REQUEST.AUTHENTICATED_USER):
raise AccessDenied(
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
)
if not can_edit_etud_archive(current_user):
raise AccessDenied("opération non autorisée pour %s" % str(current_user))
etuds = sco_etud.get_etud_info(filled=True)
if not etuds:
raise ScoValueError("étudiant inexistant")
@ -269,8 +266,12 @@ def etudarchive_generate_excel_sample(group_id=None, REQUEST=None):
],
extra_cols=["fichier_a_charger"],
)
breakpoint()
return scu.send_file(data, "ImportFichiersEtudiants", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True)
return scu.send_file(
data,
"ImportFichiersEtudiants",
suffix=scu.XLSX_SUFFIX,
mime=scu.XLSX_MIMETYPE,
)
# return sco_excel.send_excel_file(REQUEST, data, "ImportFichiersEtudiants" + scu.XLSX_SUFFIX)
@ -308,7 +309,7 @@ def etudarchive_import_files_form(group_id, REQUEST=None):
]
F = html_sco_header.sco_footer()
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("xlsfile", {"title": "Fichier Excel:", "input_type": "file", "size": 40}),

View File

@ -28,6 +28,7 @@
"""Génération des bulletins de notes
"""
from app.models import formsemestre
import time
import pprint
import email
@ -38,7 +39,7 @@ from email.header import Header
from reportlab.lib.colors import Color
import urllib
from flask import g
from flask import g, request
from flask import url_for
from flask_login import current_user
from flask_mail import Message
@ -47,7 +48,7 @@ import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app import log
from app.scodoc.sco_permissions import Permission
from app.scodoc.sco_exceptions import AccessDenied
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
from app.scodoc import html_sco_header
from app.scodoc import htmlutils
from app.scodoc import sco_abs
@ -120,9 +121,7 @@ def make_context_dict(sem, etud):
return C
def formsemestre_bulletinetud_dict(
formsemestre_id, etudid, version="long", REQUEST=None
):
def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
"""Collecte informations pour bulletin de notes
Retourne un dictionnaire (avec valeur par défaut chaine vide).
Le contenu du dictionnaire dépend des options (rangs, ...)
@ -142,10 +141,7 @@ def formsemestre_bulletinetud_dict(
I["etudid"] = etudid
I["formsemestre_id"] = formsemestre_id
I["sem"] = nt.sem
if REQUEST:
I["server_name"] = REQUEST.BASE0
else:
I["server_name"] = ""
I["server_name"] = request.url_root
# Formation et parcours
I["formation"] = sco_formations.formation_list(
@ -777,7 +773,10 @@ def formsemestre_bulletinetud(
etud = sco_etud.get_etud_info(filled=True)[0]
etudid = etud["etudid"]
except:
return scu.log_unknown_etud(REQUEST, format=format)
sco_etud.log_unknown_etud()
raise ScoValueError("étudiant inconnu")
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
bulletin = do_formsemestre_bulletinetud(
formsemestre_id,
@ -790,7 +789,8 @@ def formsemestre_bulletinetud(
REQUEST=REQUEST,
)[0]
if format not in {"html", "pdfmail"}:
return bulletin
filename = scu.bul_filename(sem, etud, format)
return scu.send_file(bulletin, filename, mime=scu.get_mime_suffix(format)[0])
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
H = [
@ -861,14 +861,13 @@ def do_formsemestre_bulletinetud(
):
"""Génère le bulletin au format demandé.
Retourne: (bul, filigranne)
bul est au format demandé (html, pdf, pdfmail, pdfpart, xml)
bul est str ou bytes au format demandé (html, pdf, pdfmail, pdfpart, xml, json)
et filigranne est un message à placer en "filigranne" (eg "Provisoire").
"""
if format == "xml":
bul = sco_bulletins_xml.make_xml_formsemestre_bulletinetud(
formsemestre_id,
etudid,
REQUEST=REQUEST,
xml_with_decisions=xml_with_decisions,
force_publishing=force_publishing,
version=version,
@ -880,19 +879,18 @@ def do_formsemestre_bulletinetud(
bul = sco_bulletins_json.make_json_formsemestre_bulletinetud(
formsemestre_id,
etudid,
REQUEST=REQUEST,
xml_with_decisions=xml_with_decisions,
force_publishing=force_publishing,
version=version,
)
return bul, ""
I = formsemestre_bulletinetud_dict(formsemestre_id, etudid, REQUEST=REQUEST)
I = formsemestre_bulletinetud_dict(formsemestre_id, etudid)
etud = I["etud"]
if format == "html":
htm, _ = sco_bulletins_generator.make_formsemestre_bulletinetud(
I, version=version, format="html", REQUEST=REQUEST
I, version=version, format="html"
)
return htm, I["filigranne"]
@ -902,11 +900,10 @@ def do_formsemestre_bulletinetud(
version=version,
format="pdf",
stand_alone=(format != "pdfpart"),
REQUEST=REQUEST,
)
if format == "pdf":
return (
scu.sendPDFFile(REQUEST, bul, filename),
scu.sendPDFFile(bul, filename),
I["filigranne"],
) # unused ret. value
else:
@ -922,11 +919,11 @@ def do_formsemestre_bulletinetud(
htm = "" # speed up if html version not needed
else:
htm, _ = sco_bulletins_generator.make_formsemestre_bulletinetud(
I, version=version, format="html", REQUEST=REQUEST
I, version=version, format="html"
)
pdfdata, filename = sco_bulletins_generator.make_formsemestre_bulletinetud(
I, version=version, format="pdf", REQUEST=REQUEST
I, version=version, format="pdf"
)
if prefer_mail_perso:
@ -997,7 +994,6 @@ def mail_bulletin(formsemestre_id, I, pdfdata, filename, recipient_addr):
# Attach pdf
msg.attach(filename, scu.PDF_MIMETYPE, pdfdata)
log("mail bulletin a %s" % recipient_addr)
email.send_message(msg)
@ -1032,7 +1028,7 @@ def _formsemestre_bulletinetud_header_html(
),
"""
<form name="f" method="GET" action="%s">"""
% REQUEST.URL0,
% request.base_url,
f"""Bulletin <span class="bull_liensemestre"><a href="{
url_for("notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
@ -1062,14 +1058,20 @@ def _formsemestre_bulletinetud_header_html(
H.append("""</select></td>""")
# Menu
endpoint = "notes.formsemestre_bulletinetud"
url = REQUEST.URL0
qurl = urllib.parse.quote_plus(url + "?" + REQUEST.QUERY_STRING)
menuBul = [
{
"title": "Réglages bulletins",
"endpoint": "notes.formsemestre_edit_options",
"args": {"formsemestre_id": formsemestre_id, "target_url": qurl},
"args": {
"formsemestre_id": formsemestre_id,
# "target_url": url_for(
# "notes.formsemestre_bulletinetud",
# scodoc_dept=g.scodoc_dept,
# formsemestre_id=formsemestre_id,
# etudid=etudid,
# ),
},
"enabled": (current_user.id in sem["responsables"])
or current_user.has_permission(Permission.ScoImplement),
},
@ -1112,6 +1114,16 @@ def _formsemestre_bulletinetud_header_html(
"enabled": etud["emailperso"]
and can_send_bulletin_by_mail(formsemestre_id),
},
{
"title": "Version json",
"endpoint": endpoint,
"args": {
"formsemestre_id": formsemestre_id,
"etudid": etudid,
"version": version,
"format": "json",
},
},
{
"title": "Version XML",
"endpoint": endpoint,
@ -1187,9 +1199,14 @@ def _formsemestre_bulletinetud_header_html(
H.append(
'<td> <a href="%s">%s</a></td>'
% (
url
+ "?formsemestre_id=%s&etudid=%s&format=pdf&version=%s"
% (formsemestre_id, etudid, version),
url_for(
"notes.formsemestre_bulletinetud",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
etudid=etudid,
format="pdf",
version=version,
),
scu.ICON_PDF,
)
)

View File

@ -52,6 +52,9 @@ import reportlab
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Frame, PageBreak
from reportlab.platypus import Table, TableStyle, Image, KeepInFrame
from flask import request
from flask_login import current_user
from app.scodoc import sco_utils as scu
from app.scodoc.sco_exceptions import NoteProcessError
from app import log
@ -148,14 +151,7 @@ class BulletinGenerator(object):
def get_filename(self):
"""Build a filename to be proposed to the web client"""
sem = sco_formsemestre.get_formsemestre(self.infos["formsemestre_id"])
dt = time.strftime("%Y-%m-%d")
filename = "bul-%s-%s-%s.pdf" % (
sem["titre_num"],
dt,
self.infos["etud"]["nom"],
)
filename = scu.unescape_html(filename).replace(" ", "_").replace("&", "")
return filename
return scu.bul_filename(sem, self.infos["etud"], "pdf")
def generate(self, format="", stand_alone=True):
"""Return bulletin in specified format"""
@ -260,7 +256,6 @@ def make_formsemestre_bulletinetud(
version="long", # short, long, selectedevals
format="pdf", # html, pdf
stand_alone=True,
REQUEST=None,
):
"""Bulletin de notes
@ -286,10 +281,10 @@ def make_formsemestre_bulletinetud(
PDFLOCK.acquire()
bul_generator = gen_class(
infos,
authuser=REQUEST.AUTHENTICATED_USER,
authuser=current_user,
version=version,
filigranne=infos["filigranne"],
server_name=REQUEST.BASE0,
server_name=request.url_root,
)
if format not in bul_generator.supported_formats:
# use standard generator
@ -301,10 +296,10 @@ def make_formsemestre_bulletinetud(
gen_class = bulletin_get_class(bul_class_name)
bul_generator = gen_class(
infos,
authuser=REQUEST.AUTHENTICATED_USER,
authuser=current_user,
version=version,
filigranne=infos["filigranne"],
server_name=REQUEST.BASE0,
server_name=request.url_root,
)
data = bul_generator.generate(format=format, stand_alone=stand_alone)

View File

@ -47,27 +47,22 @@ from app.scodoc import sco_etud
def make_json_formsemestre_bulletinetud(
formsemestre_id,
etudid,
REQUEST=None,
formsemestre_id: int,
etudid: int,
xml_with_decisions=False,
version="long",
force_publishing=False, # force publication meme si semestre non publie sur "portail"
):
) -> str:
"""Renvoie bulletin en chaine JSON"""
d = formsemestre_bulletinetud_published_dict(
formsemestre_id,
etudid,
force_publishing=force_publishing,
REQUEST=REQUEST,
xml_with_decisions=xml_with_decisions,
version=version,
)
if REQUEST:
REQUEST.RESPONSE.setHeader("content-type", scu.JSON_MIMETYPE)
return json.dumps(d, cls=scu.ScoDocJSONEncoder)
@ -79,7 +74,6 @@ def formsemestre_bulletinetud_published_dict(
etudid,
force_publishing=False,
xml_nodate=False,
REQUEST=None,
xml_with_decisions=False, # inclue les decisions même si non publiées
version="long",
):

View File

@ -58,7 +58,7 @@ import traceback
from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate
from flask import g, url_for
from flask import g, url_for, request
import app.scodoc.sco_utils as scu
from app import log
@ -193,7 +193,7 @@ def get_formsemestre_bulletins_pdf(formsemestre_id, REQUEST, version="selectedev
#
infos = {"DeptName": sco_preferences.get_preference("DeptName", formsemestre_id)}
if REQUEST:
server_name = REQUEST.BASE0
server_name = request.url_root
else:
server_name = ""
try:
@ -243,7 +243,7 @@ def get_etud_bulletins_pdf(etudid, REQUEST, version="selectedevals"):
i = i + 1
infos = {"DeptName": sco_preferences.get_preference("DeptName")}
if REQUEST:
server_name = REQUEST.BASE0
server_name = request.url_root
else:
server_name = ""
try:

View File

@ -69,16 +69,13 @@ def make_xml_formsemestre_bulletinetud(
doc=None, # XML document
force_publishing=False,
xml_nodate=False,
REQUEST=None,
xml_with_decisions=False, # inclue les decisions même si non publiées
version="long",
):
) -> str:
"bulletin au format XML"
from app.scodoc import sco_bulletins
log("xml_bulletin( formsemestre_id=%s, etudid=%s )" % (formsemestre_id, etudid))
if REQUEST:
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
if (not sem["bul_hide_xml"]) or force_publishing:

View File

@ -30,6 +30,8 @@
(coût théorique en heures équivalent TD)
"""
from flask import request
import app.scodoc.sco_utils as scu
from app.scodoc.gen_tables import GenTable
from app.scodoc import sco_formsemestre
@ -182,7 +184,7 @@ def formsemestre_estim_cost(
<br/>
</form>
""" % (
REQUEST.URL0,
request.base_url,
formsemestre_id,
n_group_td,
n_group_tp,
@ -190,7 +192,7 @@ def formsemestre_estim_cost(
)
tab.html_before_table = h
tab.base_url = "%s?formsemestre_id=%s&n_group_td=%s&n_group_tp=%s&coef_tp=%s" % (
REQUEST.URL0,
request.base_url,
formsemestre_id,
n_group_td,
n_group_tp,

View File

@ -29,7 +29,7 @@
Rapport (table) avec dernier semestre fréquenté et débouché de chaque étudiant
"""
import http
from flask import url_for, g
from flask import url_for, g, request
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
@ -47,10 +47,10 @@ from app.scodoc import sco_etud
import sco_version
def report_debouche_date(start_year=None, format="html", REQUEST=None):
def report_debouche_date(start_year=None, format="html"):
"""Rapport (table) pour les débouchés des étudiants sortis à partir de l'année indiquée."""
if not start_year:
return report_debouche_ask_date(REQUEST=REQUEST)
return report_debouche_ask_date()
if format == "xls":
keep_numeric = True # pas de conversion des notes en strings
else:
@ -64,7 +64,7 @@ def report_debouche_date(start_year=None, format="html", REQUEST=None):
"Généré par %s le " % sco_version.SCONAME + scu.timedate_human_repr() + ""
)
tab.caption = "Récapitulatif débouchés à partir du 1/1/%s." % start_year
tab.base_url = "%s?start_year=%s" % (REQUEST.URL0, start_year)
tab.base_url = "%s?start_year=%s" % (request.base_url, start_year)
return tab.make_page(
title="""<h2 class="formsemestre">Débouchés étudiants </h2>""",
init_qtip=True,
@ -193,7 +193,7 @@ def table_debouche_etudids(etudids, keep_numeric=True):
return tab
def report_debouche_ask_date(REQUEST=None):
def report_debouche_ask_date():
"""Formulaire demande date départ"""
return (
html_sco_header.sco_header()
@ -248,7 +248,7 @@ def itemsuivi_get(cnx, itemsuivi_id, ignore_errors=False):
return None
def itemsuivi_suppress(itemsuivi_id, REQUEST=None):
def itemsuivi_suppress(itemsuivi_id):
"""Suppression d'un item"""
if not sco_permissions_check.can_edit_suivi():
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
@ -258,9 +258,10 @@ def itemsuivi_suppress(itemsuivi_id, REQUEST=None):
_itemsuivi_delete(cnx, itemsuivi_id)
logdb(cnx, method="itemsuivi_suppress", etudid=item["etudid"])
log("suppressed itemsuivi %s" % (itemsuivi_id,))
return ("", 204)
def itemsuivi_create(etudid, item_date=None, situation="", REQUEST=None, format=None):
def itemsuivi_create(etudid, item_date=None, situation="", format=None):
"""Creation d'un item"""
if not sco_permissions_check.can_edit_suivi():
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
@ -272,11 +273,11 @@ def itemsuivi_create(etudid, item_date=None, situation="", REQUEST=None, format=
log("created itemsuivi %s for %s" % (itemsuivi_id, etudid))
item = itemsuivi_get(cnx, itemsuivi_id)
if format == "json":
return scu.sendJSON(REQUEST, item)
return scu.sendJSON(item)
return item
def itemsuivi_set_date(itemsuivi_id, item_date, REQUEST=None):
def itemsuivi_set_date(itemsuivi_id, item_date):
"""set item date
item_date is a string dd/mm/yyyy
"""
@ -287,9 +288,10 @@ def itemsuivi_set_date(itemsuivi_id, item_date, REQUEST=None):
item = itemsuivi_get(cnx, itemsuivi_id)
item["item_date"] = item_date
_itemsuivi_edit(cnx, item)
return ("", 204)
def itemsuivi_set_situation(object, value, REQUEST=None):
def itemsuivi_set_situation(object, value):
"""set situation"""
if not sco_permissions_check.can_edit_suivi():
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
@ -303,14 +305,14 @@ def itemsuivi_set_situation(object, value, REQUEST=None):
return situation or scu.IT_SITUATION_MISSING_STR
def itemsuivi_list_etud(etudid, format=None, REQUEST=None):
def itemsuivi_list_etud(etudid, format=None):
"""Liste des items pour cet étudiant, avec tags"""
cnx = ndb.GetDBConnexion()
items = _itemsuivi_list(cnx, {"etudid": etudid})
for it in items:
it["tags"] = ", ".join(itemsuivi_tag_list(it["itemsuivi_id"]))
if format == "json":
return scu.sendJSON(REQUEST, items)
return scu.sendJSON(items)
return items
@ -327,7 +329,7 @@ def itemsuivi_tag_list(itemsuivi_id):
return [x["title"] for x in r]
def itemsuivi_tag_search(term, REQUEST=None):
def itemsuivi_tag_search(term):
"""List all used tag names (for auto-completion)"""
# restrict charset to avoid injections
if not scu.ALPHANUM_EXP.match(term):
@ -342,10 +344,10 @@ def itemsuivi_tag_search(term, REQUEST=None):
)
data = [x["title"] for x in r]
return scu.sendJSON(REQUEST, data)
return scu.sendJSON(data)
def itemsuivi_tag_set(itemsuivi_id="", taglist=[], REQUEST=None):
def itemsuivi_tag_set(itemsuivi_id="", taglist=None):
"""taglist may either be:
a string with tag names separated by commas ("un;deux")
or a list of strings (["un", "deux"])

View File

@ -28,7 +28,7 @@
"""Page accueil département (liste des semestres, etc)
"""
from flask import g
from flask import g, request
from flask_login import current_user
import app
@ -131,7 +131,7 @@ def index_html(REQUEST=None, showcodes=0, showsemtable=0):
if not showsemtable:
H.append(
'<hr/><p><a href="%s?showsemtable=1">Voir tous les semestres</a></p>'
% REQUEST.URL0
% request.base_url
)
H.append(
@ -242,7 +242,7 @@ def _sem_table_gt(sems, showcodes=False):
rows=sems,
html_class="table_leftalign semlist",
html_sortable=True,
# base_url = '%s?formsemestre_id=%s' % (REQUEST.URL0, formsemestre_id),
# base_url = '%s?formsemestre_id=%s' % (request.base_url, formsemestre_id),
# caption='Maquettes enregistrées',
preferences=sco_preferences.SemPreferences(),
)

View File

@ -51,6 +51,7 @@ import fcntl
import subprocess
import requests
from flask_login import current_user
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
@ -64,7 +65,7 @@ from app.scodoc.sco_exceptions import ScoValueError
SCO_DUMP_LOCK = "/tmp/scodump.lock"
def sco_dump_and_send_db(REQUEST=None):
def sco_dump_and_send_db():
"""Dump base de données et l'envoie anonymisée pour debug"""
H = [html_sco_header.sco_header(page_title="Assistance technique")]
# get currect (dept) DB name:
@ -93,7 +94,7 @@ def sco_dump_and_send_db(REQUEST=None):
_anonymize_db(ano_db_name)
# Send
r = _send_db(REQUEST, ano_db_name)
r = _send_db(ano_db_name)
if (
r.status_code
== requests.codes.INSUFFICIENT_STORAGE # pylint: disable=no-member
@ -171,29 +172,27 @@ def _get_scodoc_serial():
return 0
def _send_db(REQUEST, ano_db_name):
def _send_db(ano_db_name):
"""Dump this (anonymized) database and send it to tech support"""
log("dumping anonymized database {}".format(ano_db_name))
log(f"dumping anonymized database {ano_db_name}")
try:
data = subprocess.check_output("pg_dump {} | gzip".format(ano_db_name), shell=1)
except subprocess.CalledProcessError as e:
log("sco_dump_and_send_db: exception in anonymisation: {}".format(e))
raise ScoValueError(
"erreur lors de l'anonymisation de la base {}".format(ano_db_name)
dump = subprocess.check_output(
f"pg_dump --format=custom {ano_db_name}", shell=1
)
except subprocess.CalledProcessError as e:
log(f"sco_dump_and_send_db: exception in anonymisation: {e}")
raise ScoValueError(f"erreur lors de l'anonymisation de la base {ano_db_name}")
log("uploading anonymized dump...")
files = {"file": (ano_db_name + ".gz", data)}
files = {"file": (ano_db_name + ".dump", dump)}
r = requests.post(
scu.SCO_DUMP_UP_URL,
files=files,
data={
"dept_name": sco_preferences.get_preference("DeptName"),
"serial": _get_scodoc_serial(),
"sco_user": str(REQUEST.AUTHENTICATED_USER),
"sent_by": sco_users.user_info(str(REQUEST.AUTHENTICATED_USER))[
"nomcomplet"
],
"sco_user": str(current_user),
"sent_by": sco_users.user_info(str(current_user))["nomcomplet"],
"sco_version": sco_version.SCOVERSION,
"sco_fullversion": scu.get_scodoc_version(),
},

View File

@ -29,7 +29,7 @@
(portage from DTML)
"""
import flask
from flask import g, url_for
from flask import g, url_for, request
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
@ -159,7 +159,7 @@ def formation_edit(formation_id=None, create=False, REQUEST=None):
)
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("formation_id", {"default": formation_id, "input_type": "hidden"}),

View File

@ -29,7 +29,7 @@
(portage from DTML)
"""
import flask
from flask import g, url_for
from flask import g, url_for, request
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
@ -116,7 +116,7 @@ associé.
</p>""",
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("ue_id", {"input_type": "hidden", "default": ue_id}),
@ -202,7 +202,7 @@ def matiere_delete(matiere_id=None, REQUEST=None):
]
dest_url = scu.NotesURL() + "/ue_list?formation_id=" + str(UE["formation_id"])
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(("matiere_id", {"input_type": "hidden"}),),
initvalues=M,
@ -256,7 +256,7 @@ des notes.</em>
associé.
</p>"""
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("matiere_id", {"input_type": "hidden"}),
@ -323,4 +323,4 @@ def matiere_is_locked(matiere_id):
""",
{"matiere_id": matiere_id},
)
return len(r) > 0
return len(r) > 0

View File

@ -29,7 +29,8 @@
(portage from DTML)
"""
import flask
from flask import url_for, g
from flask import url_for, g, request
from flask_login import current_user
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
@ -143,7 +144,7 @@ def module_create(matiere_id=None, REQUEST=None):
else:
default_num = 10
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
(
@ -258,12 +259,13 @@ def do_module_delete(oid):
# S'il y a des moduleimpls, on ne peut pas detruire le module !
mods = sco_moduleimpl.do_moduleimpl_list(module_id=oid)
if mods:
err_page = scu.confirm_dialog(
message="""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>""",
helpmsg="""Il faut d'abord supprimer le semestre. Mais il est peut être préférable de laisser ce programme intact et d'en créer une nouvelle version pour la modifier.""",
dest_url="ue_list",
parameters={"formation_id": mod["formation_id"]},
)
err_page = f"""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>
<p class="help">Il faut d'abord supprimer le semestre. Mais il est peut être préférable de
laisser ce programme intact et d'en créer une nouvelle version pour la modifier.
</p>
<a href="{url_for('notes.ue_list', scodoc_dept=g.scodoc_dept,
formation_id=mod["formation_id"])}">reprendre</a>
"""
raise ScoGenError(err_page)
# delete
cnx = ndb.GetDBConnexion()
@ -294,7 +296,7 @@ def module_delete(module_id=None, REQUEST=None):
dest_url = scu.NotesURL() + "/ue_list?formation_id=" + str(Mod["formation_id"])
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(("module_id", {"input_type": "hidden"}),),
initvalues=Mod,
@ -388,7 +390,7 @@ def module_edit(module_id=None, REQUEST=None):
)
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
(
@ -544,7 +546,7 @@ def module_list(formation_id, REQUEST=None):
% F,
'<ul class="notes_module_list">',
]
editable = REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoChangeFormation)
editable = current_user.has_permission(Permission.ScoChangeFormation)
for Mod in do_module_list(args={"formation_id": formation_id}):
H.append('<li class="notes_module_list">%s' % Mod)

View File

@ -29,7 +29,7 @@
"""
import flask
from flask import g, url_for
from flask import g, url_for, request
from flask_login import current_user
import app.scodoc.notesdb as ndb
@ -326,7 +326,11 @@ def ue_edit(ue_id=None, create=False, formation_id=None, REQUEST=None):
)
)
tf = TrivialFormulator(
REQUEST.URL0, REQUEST.form, fw, initvalues=initvalues, submitlabel=submitlabel
request.base_url,
REQUEST.form,
fw,
initvalues=initvalues,
submitlabel=submitlabel,
)
if tf[0] == 0:
X = """<div id="ue_list_code"></div>
@ -1033,7 +1037,7 @@ def formation_table_recap(formation_id, format="html", REQUEST=None):
caption=title,
html_caption=title,
html_class="table_leftalign",
base_url="%s?formation_id=%s" % (REQUEST.URL0, formation_id),
base_url="%s?formation_id=%s" % (request.base_url, formation_id),
page_title=title,
html_title="<h2>" + title + "</h2>",
pdf_title=title,

View File

@ -149,7 +149,7 @@ def group_edt_json(group_id, start="", end="", REQUEST=None): # actuellement in
}
J.append(d)
return scu.sendJSON(REQUEST, J)
return scu.sendJSON(J)
"""XXX

View File

@ -32,11 +32,11 @@
Voir sco_apogee_csv.py pour la structure du fichier Apogée.
Stockage: utilise sco_archive.py
=> /opt/scodoc/var/scodoc/archives/apo_csv/RT/2016-1/2016-07-03-16-12-19/V3ASR.csv
=> /opt/scodoc/var/scodoc/archives/apo_csv/<dept_id>/2016-1/2016-07-03-16-12-19/V3ASR.csv
pour une maquette de l'année scolaire 2016, semestre 1, etape V3ASR
ou bien (à partir de ScoDoc 1678) :
/opt/scodoc/var/scodoc/archives/apo_csv/RT/2016-1/2016-07-03-16-12-19/V3ASR!111.csv
/opt/scodoc/var/scodoc/archives/apo_csv/<dept_id>/2016-1/2016-07-03-16-12-19/V3ASR!111.csv
pour une maquette de l'étape V3ASR version VDI 111.
La version VDI sera ignorée sauf si elle est indiquée dans l'étape du semestre.

View File

@ -32,7 +32,7 @@ import io
from zipfile import ZipFile
import flask
from flask import url_for, g, send_file
from flask import url_for, g, send_file, request
# from werkzeug.utils import send_file
@ -250,7 +250,7 @@ def apo_semset_maq_status(
"""<form name="f" method="get" action="%s">
<input type="hidden" name="semset_id" value="%s"></input>
<div><input type="checkbox" name="allow_missing_apo" value="1" onchange="document.f.submit()" """
% (REQUEST.URL0, semset_id)
% (request.base_url, semset_id)
)
if allow_missing_apo:
H.append("checked")
@ -476,7 +476,7 @@ def table_apo_csv_list(semset, REQUEST=None):
rows=T,
html_class="table_leftalign apo_maq_list",
html_sortable=True,
# base_url = '%s?formsemestre_id=%s' % (REQUEST.URL0, formsemestre_id),
# base_url = '%s?formsemestre_id=%s' % (request.base_url, formsemestre_id),
# caption='Maquettes enregistrées',
preferences=sco_preferences.SemPreferences(),
)
@ -747,7 +747,8 @@ def view_apo_csv(etape_apo="", semset_id="", format="html", REQUEST=None):
rows=etuds,
html_sortable=True,
html_class="table_leftalign apo_maq_table",
base_url="%s?etape_apo=%s&semset_id=%s" % (REQUEST.URL0, etape_apo, semset_id),
base_url="%s?etape_apo=%s&semset_id=%s"
% (request.base_url, etape_apo, semset_id),
filename="students_" + etape_apo,
caption="Etudiants Apogée en " + etape_apo,
preferences=sco_preferences.SemPreferences(),
@ -769,7 +770,7 @@ def view_apo_csv(etape_apo="", semset_id="", format="html", REQUEST=None):
return "\n".join(H)
# called from Web
# called from Web (GET)
def apo_csv_export_results(
semset_id,
block_export_res_etape=False,

View File

@ -338,31 +338,33 @@ def _check_duplicate_code(
)
if etudid:
OK = "retour à la fiche étudiant"
dest_url = "ficheEtud"
dest_endpoint = "scolar.ficheEtud"
parameters = {"etudid": etudid}
else:
if "tf_submitted" in args:
del args["tf_submitted"]
OK = "Continuer"
dest_url = "etudident_create_form"
dest_endpoint = "scolar.etudident_create_form"
parameters = args
else:
OK = "Annuler"
dest_url = ""
dest_endpoint = "notes.index_html"
parameters = {}
if not disable_notify:
err_page = scu.confirm_dialog(
message="""<h3>Code étudiant (%s) dupliqué !</h3>""" % code_name,
helpmsg="""Le %s %s est déjà utilisé: un seul étudiant peut avoir ce code. Vérifier votre valeur ou supprimer l'autre étudiant avec cette valeur.<p><ul><li>"""
% (code_name, args[code_name])
+ "</li><li>".join(listh)
+ "</li></ul><p>",
OK=OK,
dest_url=dest_url,
parameters=parameters,
)
err_page = f"""<h3><h3>Code étudiant ({code_name}) dupliqué !</h3>
<p class="help">Le {code_name} {args[code_name]} est déjà utilisé: un seul étudiant peut avoir
ce code. Vérifier votre valeur ou supprimer l'autre étudiant avec cette valeur.
</p>
<ul><li>
{ '</li><li>'.join(listh) }
</li></ul>
<p>
<a href="{ url_for(dest_endpoint, scodoc_dept=g.scodoc_dept, **parameters) }
">{OK}</a>
</p>
"""
else:
err_page = """<h3>Code étudiant (%s) dupliqué !</h3>""" % code_name
err_page = f"""<h3>Code étudiant ({code_name}) dupliqué !</h3>"""
log("*** error: code %s duplique: %s" % (code_name, args[code_name]))
raise ScoGenError(err_page)
@ -552,7 +554,6 @@ _admissionEditor = ndb.EditableTable(
"villelycee",
"codepostallycee",
"codelycee",
"debouche",
"type_admission",
"boursier_prec",
),
@ -649,6 +650,12 @@ def make_etud_args(etudid=None, code_nip=None, use_request=True, raise_exc=True)
return args
def log_unknown_etud():
"""Log request: cas ou getEtudInfo n'a pas ramene de resultat"""
etud_args = make_etud_args(raise_exc=False)
log(f"unknown student: args={etud_args}")
def get_etud_info(etudid=False, code_nip=False, filled=False) -> list:
"""infos sur un etudiant (API). If not foud, returns empty list.
On peut specifier etudid ou code_nip

View File

@ -274,13 +274,13 @@ def do_evaluation_create(
if args["jour"]:
next_eval = None
t = (
ndb.DateDMYtoISO(args["jour"]),
ndb.TimetoISO8601(args["heure_debut"]),
ndb.DateDMYtoISO(args["jour"], null_is_empty=True),
ndb.TimetoISO8601(args["heure_debut"], null_is_empty=True),
)
for e in ModEvals:
if (
ndb.DateDMYtoISO(e["jour"]),
ndb.TimetoISO8601(e["heure_debut"]),
ndb.DateDMYtoISO(e["jour"], null_is_empty=True),
ndb.TimetoISO8601(e["heure_debut"], null_is_empty=True),
) > t:
next_eval = e
break
@ -915,7 +915,7 @@ def formsemestre_evaluations_delai_correction(
html_title="<h2>Correction des évaluations du semestre</h2>",
caption="Correction des évaluations du semestre",
preferences=sco_preferences.SemPreferences(formsemestre_id),
base_url="%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id),
base_url="%s?formsemestre_id=%s" % (request.base_url, formsemestre_id),
origin="Généré par %s le " % sco_version.SCONAME
+ scu.timedate_human_repr()
+ "",
@ -1346,7 +1346,7 @@ def evaluation_create_form(
),
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
form,
cancelbutton="Annuler",

View File

@ -59,22 +59,6 @@ class COLORS(Enum):
LIGHT_YELLOW = "FFFFFF99"
# def send_excel_file(request, data, filename, mime=scu.XLSX_MIMETYPE):
# """publication fichier.
# (on ne doit rien avoir émis avant, car ici sont générés les entetes)
# """
# filename = (
# scu.unescape_html(scu.suppress_accents(filename))
# .replace("&", "")
# .replace(" ", "_")
# )
# request.RESPONSE.setHeader("content-type", mime)
# request.RESPONSE.setHeader(
# "content-disposition", 'attachment; filename="%s"' % filename
# )
# return data
# Un style est enregistré comme un dictionnaire qui précise la valeur d'un attributdans la liste suivante:
# font, border, number_format, fill,...
# (cf https://openpyxl.readthedocs.io/en/stable/styles.html#working-with-styles)
@ -88,6 +72,11 @@ def xldate_as_datetime(xldate, datemode=0):
def adjust_sheetname(sheet_name):
"""Renvoie un nom convenable pour une feuille excel: < 31 cars, sans caractères spéciaux
Le / n'est pas autorisé par exemple.
Voir https://xlsxwriter.readthedocs.io/workbook.html#add_worksheet
"""
sheet_name = scu.make_filename(sheet_name)
# Le nom de la feuille ne peut faire plus de 31 caractères.
# si la taille du nom de feuille est > 31 on tronque (on pourrait remplacer par 'feuille' ?)
return sheet_name[:31]
@ -351,10 +340,10 @@ class ScoExcelSheet:
Ce flux pourra ensuite être repris dans send_excel_file (classeur mono feille)
ou pour la génération d'un classeur multi-feuilles
"""
for col in self.column_dimensions.keys():
self.ws.column_dimensions[col] = self.column_dimensions[col]
for col in self.row_dimensions.keys():
self.ws.row_dimensions[col] = self.row_dimensions[col]
for row in self.column_dimensions.keys():
self.ws.column_dimensions[row] = self.column_dimensions[row]
for row in self.row_dimensions.keys():
self.ws.row_dimensions[row] = self.row_dimensions[row]
for row in self.rows:
self.ws.append(row)
@ -574,12 +563,24 @@ def excel_feuille_saisie(e, titreannee, description, lines):
def excel_bytes_to_list(bytes_content):
filelike = io.BytesIO(bytes_content)
return _excel_to_list(filelike)
try:
filelike = io.BytesIO(bytes_content)
return _excel_to_list(filelike)
except:
raise ScoValueError("""
scolars_import_excel_file: un contenu xlsx semble corrompu!
peut-être avez vous fourni un fichier au mauvais format (txt, xls, ..)
""")
def excel_file_to_list(filename):
return _excel_to_list(filename)
try:
return _excel_to_list(filename)
except:
raise ScoValueError("""
scolars_import_excel_file: un contenu xlsx semble corrompu!
peut-être avez vous fourni un fichier au mauvais format (txt, xls, ..)
""")
def _excel_to_list(filelike): # we may need 'encoding' argument ?

View File

@ -55,11 +55,9 @@ class InvalidNoteValue(ScoException):
# Exception qui stoque dest_url, utilisee dans Zope standard_error_message
class ScoValueError(ScoException):
def __init__(self, msg, dest_url=None, REQUEST=None):
def __init__(self, msg, dest_url=None):
ScoException.__init__(self, msg)
self.dest_url = dest_url
if REQUEST and dest_url:
REQUEST.set("dest_url", dest_url)
class FormatError(ScoValueError):
@ -79,7 +77,7 @@ class ScoConfigurationError(ScoValueError):
class ScoLockedFormError(ScoException):
def __init__(self, msg="", REQUEST=None):
def __init__(self, msg=""):
msg = (
"Cette formation est verrouillée (car il y a un semestre verrouillé qui s'y réfère). "
+ str(msg)
@ -90,7 +88,7 @@ class ScoLockedFormError(ScoException):
class ScoGenError(ScoException):
"exception avec affichage d'une page explicative ad-hoc"
def __init__(self, msg="", REQUEST=None):
def __init__(self, msg=""):
ScoException.__init__(self, msg)

View File

@ -27,7 +27,7 @@
"""Export d'une table avec les résultats de tous les étudiants
"""
from flask import url_for, g
from flask import url_for, g, request
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
@ -240,7 +240,7 @@ def scodoc_table_results(
start_date_iso, end_date_iso, types_parcours
)
tab.base_url = "%s?start_date=%s&end_date=%s&types_parcours=%s" % (
REQUEST.URL0,
request.base_url,
start_date,
end_date,
"&types_parcours=".join([str(x) for x in types_parcours]),

View File

@ -31,7 +31,8 @@ from operator import itemgetter
import xml.dom.minidom
import flask
from flask import g, url_for
from flask import g, url_for, request
from flask_login import current_user
import app.scodoc.sco_utils as scu
@ -131,9 +132,7 @@ def formation_export(
if mod["ects"] is None:
del mod["ects"]
return scu.sendResult(
REQUEST, F, name="formation", format=format, force_outer_xml_tag=False
)
return scu.sendResult(F, name="formation", format=format, force_outer_xml_tag=False)
def formation_import_xml(doc: str, import_tags=True):
@ -247,7 +246,7 @@ def formation_list_table(formation_id=None, args={}, REQUEST=None):
"edit_img", border="0", alt="modifier", title="Modifier titres et code"
)
editable = REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoChangeFormation)
editable = current_user.has_permission(Permission.ScoChangeFormation)
# Traduit/ajoute des champs à afficher:
for f in formations:
@ -347,7 +346,7 @@ def formation_list_table(formation_id=None, args={}, REQUEST=None):
html_class="formation_list_table table_leftalign",
html_with_td_classes=True,
html_sortable=True,
base_url="%s?formation_id=%s" % (REQUEST.URL0, formation_id),
base_url="%s?formation_id=%s" % (request.base_url, formation_id),
page_title=title,
pdf_title=title,
preferences=sco_preferences.SemPreferences(),

View File

@ -31,7 +31,7 @@ from app.scodoc.sco_exceptions import ScoValueError
import time
from operator import itemgetter
from flask import g
from flask import g, request
import app
from app.models import Departement
@ -583,7 +583,7 @@ def view_formsemestre_by_etape(etape_apo=None, format="html", REQUEST=None):
Etape: <input name="etape_apo" type="text" size="8"></input>
</form>""",
)
tab.base_url = "%s?etape_apo=%s" % (REQUEST.URL0, etape_apo or "")
tab.base_url = "%s?etape_apo=%s" % (request.base_url, etape_apo or "")
return tab.make_page(format=format)

View File

@ -28,7 +28,7 @@
"""Menu "custom" (défini par l'utilisateur) dans les semestres
"""
import flask
from flask import g, url_for
from flask import g, url_for, request
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
@ -119,7 +119,7 @@ def formsemestre_custommenu_edit(formsemestre_id, REQUEST=None):
initvalues["title_" + str(item["custommenu_id"])] = item["title"]
initvalues["url_" + str(item["custommenu_id"])] = item["url"]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
descr,
initvalues=initvalues,

View File

@ -28,7 +28,7 @@
"""Form choix modules / responsables et creation formsemestre
"""
import flask
from flask import url_for, g
from flask import url_for, g, request
from flask_login import current_user
from app.auth.models import User
@ -661,7 +661,7 @@ def do_formsemestre_createwithmodules(REQUEST=None, edit=False):
#
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
modform,
submitlabel=submitlabel,
@ -966,7 +966,7 @@ def formsemestre_clone(formsemestre_id, REQUEST=None):
),
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
descr,
submitlabel="Dupliquer ce semestre",
@ -1268,7 +1268,7 @@ def formsemestre_delete(formsemestre_id, REQUEST=None):
else:
submit_label = "Confirmer la suppression du semestre"
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(("formsemestre_id", {"input_type": "hidden"}),),
initvalues=F,
@ -1428,7 +1428,7 @@ def do_formsemestre_delete(formsemestre_id):
# ---------------------------------------------------------------------------------------
def formsemestre_edit_options(formsemestre_id, target_url=None, REQUEST=None):
def formsemestre_edit_options(formsemestre_id, REQUEST=None):
"""dialog to change formsemestre options
(accessible par ScoImplement ou dir. etudes)
"""
@ -1583,7 +1583,7 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None, REQUEST=None):
form.append(("ue_" + str(ue["ue_id"]), descr))
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
form,
submitlabel="Changer les coefficients",

View File

@ -34,7 +34,7 @@ Ces semestres n'auront qu'un seul inscrit !
import time
import flask
from flask import url_for, g
from flask import url_for, g, request
from flask_login import current_user
import app.scodoc.sco_utils as scu
@ -181,7 +181,7 @@ def formsemestre_ext_create_form(etudid, formsemestre_id, REQUEST=None):
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
descr,
cancelbutton="Annuler",
@ -231,7 +231,7 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid, REQUEST=None):
else:
initvalues = {}
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
descr,
cssclass="tf_ext_edit_ue_validations",

View File

@ -30,7 +30,7 @@
import time
import flask
from flask import url_for, g
from flask import url_for, g, request
import app.scodoc.sco_utils as scu
from app import log
@ -415,7 +415,7 @@ def formsemestre_inscription_with_modules(
<input type="hidden" name="etudid" value="%s">
<input type="hidden" name="formsemestre_id" value="%s">
"""
% (REQUEST.URL0, etudid, formsemestre_id)
% (request.base_url, etudid, formsemestre_id)
)
H.append(sco_groups.form_group_choice(formsemestre_id, allow_none=True))
@ -533,7 +533,7 @@ function chkbx_select(field_id, state) {
"""
)
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
descr,
initvalues,

View File

@ -436,7 +436,7 @@ def formsemestre_status_menubar(sem):
return "\n".join(H)
def retreive_formsemestre_from_request():
def retreive_formsemestre_from_request() -> int:
"""Cherche si on a de quoi déduire le semestre affiché à partir des
arguments de la requête:
formsemestre_id ou moduleimpl ou evaluation ou group_id ou partition_id
@ -482,7 +482,7 @@ def retreive_formsemestre_from_request():
else:
return None # no current formsemestre
return formsemestre_id
return int(formsemestre_id)
# Element HTML decrivant un semestre (barre de menu et infos)
@ -698,7 +698,7 @@ def formsemestre_description_table(formsemestre_id, REQUEST=None, with_evals=Fal
html_caption=title,
html_class="table_leftalign formsemestre_description",
base_url="%s?formsemestre_id=%s&with_evals=%s"
% (REQUEST.URL0, formsemestre_id, with_evals),
% (request.base_url, formsemestre_id, with_evals),
page_title=title,
html_title=html_sco_header.html_sem_header(
REQUEST, "Description du semestre", sem, with_page_header=False
@ -721,7 +721,7 @@ def formsemestre_description(
tab.html_before_table = """<form name="f" method="get" action="%s">
<input type="hidden" name="formsemestre_id" value="%s"></input>
<input type="checkbox" name="with_evals" value="1" onchange="document.f.submit()" """ % (
REQUEST.URL0,
request.base_url,
formsemestre_id,
)
if with_evals:

View File

@ -30,7 +30,7 @@
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error, time, datetime
import flask
from flask import url_for, g
from flask import url_for, g, request
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
@ -356,7 +356,7 @@ def formsemestre_validation_etud(
#
Se.valide_decision(selected_choice, REQUEST) # enregistre
return _redirect_valid_choice(
formsemestre_id, etudid, Se, selected_choice, desturl, sortcol, REQUEST
formsemestre_id, etudid, Se, selected_choice, desturl, sortcol
)
@ -402,19 +402,17 @@ def formsemestre_validation_etud_manu(
Se.valide_decision(choice, REQUEST) # enregistre
if redirect:
return _redirect_valid_choice(
formsemestre_id, etudid, Se, choice, desturl, sortcol, REQUEST
formsemestre_id, etudid, Se, choice, desturl, sortcol
)
def _redirect_valid_choice(
formsemestre_id, etudid, Se, choice, desturl, sortcol, REQUEST
):
def _redirect_valid_choice(formsemestre_id, etudid, Se, choice, desturl, sortcol):
adr = "formsemestre_validation_etud_form?formsemestre_id=%s&etudid=%s&check=1" % (
formsemestre_id,
etudid,
)
if sortcol:
adr += "&sortcol=" + sortcol
adr += "&sortcol=" + str(sortcol)
# if desturl:
# desturl += "&desturl=" + desturl
return flask.redirect(adr)
@ -1017,7 +1015,7 @@ def formsemestre_validate_previous_ue(formsemestre_id, etudid, REQUEST=None):
ue_names = ["Choisir..."] + ["%(acronyme)s %(titre)s" % ue for ue in ues]
ue_ids = [""] + [ue["ue_id"] for ue in ues]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("etudid", {"input_type": "hidden"}),

View File

@ -42,7 +42,7 @@ from xml.etree import ElementTree
from xml.etree.ElementTree import Element
import flask
from flask import g
from flask import g, request
from flask import url_for
import app.scodoc.sco_utils as scu
@ -476,7 +476,7 @@ def formsemestre_partition_list(formsemestre_id, format="xml", REQUEST=None):
# Ajoute les groupes
for p in partitions:
p["group"] = get_partition_groups(p)
return scu.sendResult(REQUEST, partitions, name="partition", format=format)
return scu.sendResult(partitions, name="partition", format=format)
# Encore utilisé par groupmgr.js
@ -1079,7 +1079,7 @@ def partition_rename(partition_id, REQUEST=None):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
H = ["<h2>Renommer une partition</h2>"]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("partition_id", {"default": partition_id, "input_type": "hidden"}),
@ -1188,7 +1188,7 @@ def group_rename(group_id, REQUEST=None):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
H = ["<h2>Renommer un groupe de %s</h2>" % group["partition_name"]]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("group_id", {"default": group_id, "input_type": "hidden"}),
@ -1268,7 +1268,7 @@ def groups_auto_repartition(partition_id=None, REQUEST=None):
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
descr,
{},

View File

@ -40,6 +40,7 @@ import time
from flask import url_for, g, request
from flask_login import current_user
import app.scodoc.sco_utils as scu
from app.scodoc import html_sco_header
@ -304,8 +305,8 @@ class DisplayedGroupsInfos(object):
if isinstance(group_ids, int):
if group_ids:
group_ids = [group_ids] # cas ou un seul parametre, pas de liste
else:
group_ids = []
else:
group_ids = [int(g) for g in group_ids]
if not formsemestre_id and moduleimpl_id:
mods = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)
if len(mods) != 1:
@ -719,12 +720,10 @@ def groups_table(
partitions=groups_infos.partitions,
with_codes=with_codes,
with_paiement=with_paiement,
server_name=REQUEST.BASE0,
server_name=request.url_root,
)
filename = "liste_%s" % groups_infos.groups_filename + ".xlsx"
breakpoint()
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True)
# return sco_excel.send_excel_file(REQUEST, xls, filename)
filename = "liste_%s" % groups_infos.groups_filename
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE)
elif format == "allxls":
# feuille Excel avec toutes les infos etudiants
if not groups_infos.members:
@ -793,16 +792,14 @@ def groups_table(
title = "etudiants_%s" % groups_infos.groups_filename
xls = sco_excel.excel_simple_table(titles=titles, lines=L, sheet_name=title)
filename = title + scu.XLSX_SUFFIX
breakpoint()
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True)
# return sco_excel.send_excel_file(REQUEST, xls, filename)
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE)
else:
raise ValueError("unsupported format")
def tab_absences_html(groups_infos, etat=None, REQUEST=None):
"""contenu du tab "absences et feuilles diverses" """
authuser = REQUEST.AUTHENTICATED_USER
authuser = current_user
H = ['<div class="tab-content">']
if not groups_infos.members:
return "".join(H) + "<h3>Aucun étudiant !</h3></div>"
@ -871,7 +868,7 @@ def tab_photos_html(groups_infos, etat=None, REQUEST=None):
def form_choix_jour_saisie_hebdo(groups_infos, moduleimpl_id=None, REQUEST=None):
"""Formulaire choix jour semaine pour saisie."""
authuser = REQUEST.AUTHENTICATED_USER
authuser = current_user
if not authuser.has_permission(Permission.ScoAbsChange):
return ""
sem = groups_infos.formsemestre
@ -912,17 +909,17 @@ def form_choix_jour_saisie_hebdo(groups_infos, moduleimpl_id=None, REQUEST=None)
# Ajout Le Havre
# Formulaire saisie absences semaine
def form_choix_saisie_semaine(groups_infos, REQUEST=None):
authuser = REQUEST.AUTHENTICATED_USER
authuser = current_user
if not authuser.has_permission(Permission.ScoAbsChange):
return ""
# construit l'URL "destination"
# (a laquelle on revient apres saisie absences)
query_args = parse_qs(REQUEST.QUERY_STRING)
query_args = parse_qs(request.query_string)
moduleimpl_id = query_args.get("moduleimpl_id", [""])[0]
if "head_message" in query_args:
del query_args["head_message"]
destination = "%s?%s" % (
REQUEST.URL,
request.base_url,
urllib.parse.urlencode(query_args, True),
)
destination = destination.replace(

View File

@ -31,7 +31,7 @@
import datetime
from operator import itemgetter
from flask import url_for, g
from flask import url_for, g, request
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
@ -414,7 +414,7 @@ def build_page(
html_sco_header.html_sem_header(
REQUEST, "Passages dans le semestre", sem, with_page_header=False
),
"""<form method="post" action="%s">""" % REQUEST.URL0,
"""<form method="post" action="%s">""" % request.base_url,
"""<input type="hidden" name="formsemestre_id" value="%(formsemestre_id)s"/>
<input type="submit" name="submitted" value="Appliquer les modifications"/>
&nbsp;<a href="#help">aide</a>

View File

@ -31,7 +31,7 @@ from operator import itemgetter
import urllib
import flask
from flask import url_for, g
from flask import url_for, g, request
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
@ -177,7 +177,7 @@ def do_evaluation_listenotes(REQUEST):
),
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
descr,
cancelbutton=None,

View File

@ -31,7 +31,7 @@
"""
from operator import itemgetter
from flask import url_for, g
from flask import url_for, g, request
import app
import app.scodoc.sco_utils as scu
@ -84,7 +84,7 @@ def scodoc_table_etuds_lycees(format="html", REQUEST=None):
sco_preferences.SemPreferences(),
no_links=True,
)
tab.base_url = REQUEST.URL0
tab.base_url = request.base_url
t = tab.make_page(format=format, with_html_headers=False)
if format != "html":
return t
@ -187,7 +187,7 @@ def formsemestre_etuds_lycees(
tab, etuds_by_lycee = formsemestre_table_etuds_lycees(
formsemestre_id, only_primo=only_primo, group_lycees=not no_grouping
)
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
if only_primo:
tab.base_url += "&only_primo=1"
if no_grouping:

View File

@ -30,7 +30,8 @@
from operator import itemgetter
import flask
from flask import url_for, g
from flask import url_for, g, request
from flask_login import current_user
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
@ -137,7 +138,7 @@ def moduleimpl_inscriptions_edit(
</script>"""
)
H.append("""<form method="post" id="mi_form" action="%s">""" % REQUEST.URL0)
H.append("""<form method="post" id="mi_form" action="%s">""" % request.base_url)
H.append(
"""
<input type="hidden" name="moduleimpl_id" value="%(moduleimpl_id)s"/>
@ -250,7 +251,7 @@ def moduleimpl_inscriptions_stats(formsemestre_id, REQUEST=None):
tous sauf <liste d'au plus 7 noms>
"""
authuser = REQUEST.AUTHENTICATED_USER
authuser = current_user
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
inscrits = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(

View File

@ -64,7 +64,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
if (
sco_permissions_check.can_edit_notes(
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
current_user, E["moduleimpl_id"], allow_ens=False
)
and nbnotes != 0
):
@ -80,7 +80,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
"evaluation_id": evaluation_id,
},
"enabled": sco_permissions_check.can_edit_notes(
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"]
current_user, E["moduleimpl_id"]
),
},
{
@ -90,7 +90,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
"evaluation_id": evaluation_id,
},
"enabled": sco_permissions_check.can_edit_notes(
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
current_user, E["moduleimpl_id"], allow_ens=False
),
},
{
@ -101,7 +101,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
},
"enabled": nbnotes == 0
and sco_permissions_check.can_edit_notes(
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
current_user, E["moduleimpl_id"], allow_ens=False
),
},
{
@ -111,7 +111,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
"evaluation_id": evaluation_id,
},
"enabled": sco_permissions_check.can_edit_notes(
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
current_user, E["moduleimpl_id"], allow_ens=False
),
},
{
@ -128,9 +128,8 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
"args": {
"evaluation_id": evaluation_id,
},
"enabled": nbnotes == 0
and sco_permissions_check.can_edit_notes(
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"]
"enabled": sco_permissions_check.can_edit_notes(
current_user, E["moduleimpl_id"]
),
},
{

View File

@ -31,6 +31,7 @@
"""
from flask import url_for, g
from flask_login import current_user
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
@ -144,7 +145,7 @@ def _menuScolarite(authuser, sem, etudid):
def ficheEtud(etudid=None, REQUEST=None):
"fiche d'informations sur un etudiant"
authuser = REQUEST.AUTHENTICATED_USER
authuser = current_user
cnx = ndb.GetDBConnexion()
if etudid and REQUEST:
# la sidebar est differente s'il y a ou pas un etudid
@ -491,7 +492,7 @@ def menus_etud(REQUEST=None):
"""Menu etudiant (operations sur l'etudiant)"""
if "etudid" not in REQUEST.form:
return ""
authuser = REQUEST.AUTHENTICATED_USER
authuser = current_user
etud = sco_etud.get_etud_info(filled=True)[0]

View File

@ -588,7 +588,6 @@ class SituationEtudParcoursGeneric(object):
self.etudid,
decision.code_etat,
decision.assiduite,
REQUEST=REQUEST,
)
# -- modification du code du semestre precedent
if self.prev and decision.new_code_prev:
@ -619,7 +618,6 @@ class SituationEtudParcoursGeneric(object):
self.etudid,
decision.new_code_prev,
decision.assiduite, # attention: en toute rigueur il faudrait utiliser une indication de l'assiduite au sem. precedent, que nous n'avons pas...
REQUEST=REQUEST,
)
sco_cache.invalidate_formsemestre(
@ -897,9 +895,7 @@ def formsemestre_update_validation_sem(
return to_invalidate
def formsemestre_validate_ues(
formsemestre_id, etudid, code_etat_sem, assiduite, REQUEST=None
):
def formsemestre_validate_ues(formsemestre_id, etudid, code_etat_sem, assiduite):
"""Enregistre codes UE, selon état semestre.
Les codes UE sont toujours calculés ici, et non passés en paramètres
car ils ne dépendent que de la note d'UE et de la validation ou non du semestre.
@ -933,14 +929,13 @@ def formsemestre_validate_ues(
cnx, nt, formsemestre_id, etudid, ue_id, code_ue
)
if REQUEST:
logdb(
cnx,
method="validate_ue",
etudid=etudid,
msg="ue_id=%s code=%s" % (ue_id, code_ue),
commit=False,
)
logdb(
cnx,
method="validate_ue",
etudid=etudid,
msg="ue_id=%s code=%s" % (ue_id, code_ue),
commit=False,
)
cnx.commit()

View File

@ -43,6 +43,7 @@ Les images sont servies par ScoDoc, via la méthode getphotofile?etudid=xxx
"""
from app.scodoc.sco_exceptions import ScoGenError
import datetime
import glob
import io
@ -52,6 +53,7 @@ import requests
import time
import traceback
import PIL
from PIL import Image as PILImage
from flask import request, g
@ -246,7 +248,10 @@ def store_photo(etud, data):
filesize = len(data)
if filesize < 10 or filesize > MAX_FILE_SIZE:
return 0, "Fichier image de taille invalide ! (%d)" % filesize
filename = save_image(etud["etudid"], data)
try:
filename = save_image(etud["etudid"], data)
except PIL.UnidentifiedImageError:
raise ScoGenError(msg="Fichier d'image invalide ou non format non supporté")
# update database:
etud["photo_filename"] = filename
etud["foto"] = None
@ -298,6 +303,7 @@ def save_image(etudid, data):
filename = get_new_filename(etudid)
path = os.path.join(PHOTO_DIR, filename)
log("saving %dx%d jpeg to %s" % (img.size[0], img.size[1], path))
img = img.convert("RGB")
img.save(path + IMAGE_EXT, format="JPEG", quality=92)
# resize:
img = scale_height(img)
@ -341,7 +347,7 @@ def find_new_dir():
def copy_portal_photo_to_fs(etud):
"""Copy the photo from portal (distant website) to local fs.
Returns rel. path or None if copy failed, with a diagnotic message
Returns rel. path or None if copy failed, with a diagnostic message
"""
sco_etud.format_etud_ident(etud)
url = photo_portal_url(etud)
@ -353,11 +359,12 @@ def copy_portal_photo_to_fs(etud):
log("copy_portal_photo_to_fs: getting %s" % url)
r = requests.get(url, timeout=portal_timeout)
except:
log("download failed: exception:\n%s" % traceback.format_exc())
log("called from:\n" + "".join(traceback.format_stack()))
# log("download failed: exception:\n%s" % traceback.format_exc())
# log("called from:\n" + "".join(traceback.format_stack()))
log("copy_portal_photo_to_fs: error.")
return None, "%s: erreur chargement de %s" % (etud["nomprenom"], url)
if r.status_code != 200:
log("download failed")
log(f"copy_portal_photo_to_fs: download failed {r.status_code }")
return None, "%s: erreur chargement de %s" % (etud["nomprenom"], url)
data = r.content # image bytes
try:

View File

@ -27,7 +27,8 @@
"""ScoDoc: génération feuille émargement et placement
Contribution M. Salomon, UFC / IUT DE BELFORT-MONTBÉLIARD, 2016
Contribution J.-M. Place 2021
basée sur une idée de M. Salomon, UFC / IUT DE BELFORT-MONTBÉLIARD, 2016
"""
import random
@ -47,10 +48,7 @@ from wtforms import (
HiddenField,
SelectMultipleField,
BooleanField,
FormField,
)
from wtforms.form import BaseForm
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app import ScoValueError
@ -68,8 +66,8 @@ from app.scodoc.gen_tables import GenTable
from app.scodoc import sco_etud
import sco_version
# _ = lambda x: x # sans babel
# _l = _
_ = lambda x: x # sans babel
_l = _
COORD = "Coordonnées"
SEQ = "Continue"
@ -112,7 +110,10 @@ class PlacementForm(FlaskForm):
batiment = StringField("Batiment")
salle = StringField("Salle")
nb_rangs = SelectField(
"nb_rangs", coerce=int, choices=[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
"nb de places en largeur",
coerce=int,
choices=[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
description="largeur de la salle, en nombre de places",
)
etiquetage = RadioField(
"Numérotation",
@ -121,6 +122,7 @@ class PlacementForm(FlaskForm):
wtforms.validators.DataRequired("indiquez le style de numérotation"),
],
)
# groups = SelectMultipleField(
# "Groupe(s)",
# validators=[
@ -258,7 +260,12 @@ class PlacementRunner:
self.salle = form["salle"].data
self.nb_rangs = form["nb_rangs"].data
self.file_format = form["file_format"].data
self.groups_ids = form["groups"].data
if len(form["groups"].data) == 0:
self.groups_ids = [form.tous_id]
else: # On remplace le mot-clé TOUS le l'identiant de ce groupe
self.groups_ids = [
gid if gid != TOUS else form.tous_id for gid in form["groups"].data
]
self.eval_data = sco_evaluations.do_evaluation_list(
{"evaluation_id": self.evaluation_id}
)[0]
@ -361,7 +368,7 @@ class PlacementRunner:
def _production_xls(self):
filename = "placement_%s_%s" % (self.evalname, self.gr_title_filename)
xls = self._excel_feuille_placement()
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE)
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE)
def _production_pdf(self):
pdf_title = "<br/>".join(self.desceval)
@ -538,6 +545,7 @@ class PlacementRunner:
cells_b = [ws0.make_cell("", self.styles["2b"])]
cells_c = [ws0.make_cell("", self.styles["2b"])]
row = 13 # première ligne de signature
rang += 1
for linetud in self.plan:
cells_a.append(ws0.make_cell(linetud[0][0], self.styles["1t"])) # nom
cells_b.append(ws0.make_cell(linetud[0][1], self.styles["1m"])) # prenom

View File

@ -31,7 +31,7 @@ Recapitule tous les semestres validés dans une feuille excel.
"""
import collections
from flask import url_for, g
from flask import url_for, g, request
import app.scodoc.sco_utils as scu
from app.scodoc import sco_abs
@ -211,7 +211,7 @@ def formsemestre_poursuite_report(formsemestre_id, format="html", REQUEST=None):
)
tab.caption = "Récapitulatif %s." % sem["titreannee"]
tab.html_caption = "Récapitulatif %s." % sem["titreannee"]
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
return tab.make_page(
title="""<h2 class="formsemestre">Poursuite d'études</h2>""",
init_qtip=True,

View File

@ -111,7 +111,7 @@ get_base_preferences(formsemestre_id)
"""
import flask
from flask import g, url_for
from flask import g, url_for, request
from flask_login import current_user
from app.models import Departement
@ -2032,7 +2032,7 @@ class BasePreferences(object):
]
form = self.build_tf_form()
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
form,
initvalues=self.prefs[None],
@ -2197,7 +2197,7 @@ function set_global_pref(el, pref_name) {
form.append(("destination", {"input_type": "hidden"}))
form.append(("formsemestre_id", {"input_type": "hidden"}))
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
form,
initvalues=self,
@ -2248,7 +2248,7 @@ function set_global_pref(el, pref_name) {
return flask.redirect(dest_url + "&head_message=Préférences modifiées")
elif destination == "again":
return flask.redirect(
REQUEST.URL0 + "?formsemestre_id=" + str(self.formsemestre_id)
request.base_url + "?formsemestre_id=" + str(self.formsemestre_id)
)
elif destination == "global":
return flask.redirect(scu.ScoURL() + "/edit_preferences")

View File

@ -31,6 +31,9 @@ import time
from openpyxl.styles.numbers import FORMAT_NUMBER_00
from flask import request
from flask_login import current_user
import app.scodoc.sco_utils as scu
from app.scodoc import sco_abs
from app.scodoc import sco_groups
@ -318,11 +321,14 @@ def feuille_preparation_jury(formsemestre_id, REQUEST):
% (
sco_version.SCONAME,
time.strftime("%d/%m/%Y"),
REQUEST.BASE0,
REQUEST.AUTHENTICATED_USER,
request.url_root,
current_user,
)
)
xls = ws.generate()
breakpoint()
return scu.send_file(xls, f"PrepaJury{sn}{scu.XLSX_SUFFIX}", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True)
# return sco_excel.send_excel_file(REQUEST, xls, f"PrepaJury{sn}{scu.XLSX_SUFFIX}")
return scu.send_file(
xls,
f"PrepaJury{sn}{scu.XLSX_SUFFIX}",
scu.XLSX_SUFFIX,
mime=scu.XLSX_MIMETYPE,
)

View File

@ -52,7 +52,7 @@ from reportlab.platypus import Paragraph
from reportlab.lib import styles
import flask
from flask import url_for, g
from flask import url_for, g, request
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
@ -537,7 +537,7 @@ def formsemestre_pvjury(formsemestre_id, format="html", publish=True, REQUEST=No
with_html_headers=False,
publish=publish,
)
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
H = [
html_sco_header.html_sem_header(
REQUEST,
@ -657,7 +657,7 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None, REQUEST=
else:
menu_choix_groupe = "" # un seul etudiant à editer
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
descr,
cancelbutton="Annuler",
@ -706,7 +706,7 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None, REQUEST=
else:
groups_filename = ""
filename = "PV-%s%s-%s.pdf" % (sem["titre_num"], groups_filename, dt)
return scu.sendPDFFile(REQUEST, pdfdoc, filename)
return scu.sendPDFFile(pdfdoc, filename)
def descrform_pvjury(sem):
@ -806,7 +806,7 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=[], REQUEST=No
H = [
html_sco_header.html_sem_header(
REQUEST,
"Edition des lettres individuelles",
"Édition des lettres individuelles",
sem=sem,
javascripts=sco_groups_view.JAVASCRIPTS,
cssstyles=sco_groups_view.CSSSTYLES,
@ -826,7 +826,7 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=[], REQUEST=No
)
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
descr,
cancelbutton="Annuler",
@ -867,7 +867,7 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=[], REQUEST=No
dt = time.strftime("%Y-%m-%d")
groups_filename = "-" + groups_infos.groups_filename
filename = "lettres-%s%s-%s.pdf" % (sem["titre_num"], groups_filename, dt)
return scu.sendPDFFile(REQUEST, pdfdoc, filename)
return scu.sendPDFFile(pdfdoc, filename)
def descrform_lettres_individuelles():

View File

@ -32,6 +32,8 @@ import json
import time
from xml.etree import ElementTree
from flask import request
import app.scodoc.sco_utils as scu
from app import log
from app.scodoc import html_sco_header
@ -101,7 +103,7 @@ def formsemestre_recapcomplet(
sco_formsemestre_status.formsemestre_status_head(
formsemestre_id=formsemestre_id, REQUEST=REQUEST
),
'<form name="f" method="get" action="%s">' % REQUEST.URL0,
'<form name="f" method="get" action="%s">' % request.base_url,
'<input type="hidden" name="formsemestre_id" value="%s"></input>'
% formsemestre_id,
'<input type="hidden" name="pref_override" value="0"></input>',
@ -971,4 +973,4 @@ def formsemestres_bulletins(annee_scolaire, REQUEST=None):
)
jslist.append(J)
return scu.sendJSON(REQUEST, jslist)
return scu.sendJSON(jslist)

View File

@ -37,7 +37,7 @@ import time
import datetime
from operator import itemgetter
from flask import url_for, g
from flask import url_for, g, request
import pydot
import app.scodoc.sco_utils as scu
@ -247,7 +247,7 @@ def formsemestre_report(
sem["titreannee"],
)
tab.html_caption = "Répartition des résultats par %s." % category_name
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
if only_primo:
tab.base_url += "&only_primo=on"
return tab
@ -322,7 +322,7 @@ def formsemestre_report_counts(
F = [
"""<form name="f" method="get" action="%s"><p>
Colonnes: <select name="result" onchange="document.f.submit()">"""
% REQUEST.URL0
% request.base_url
]
for k in keys:
if k == result:
@ -717,7 +717,7 @@ def formsemestre_suivi_cohorte(
)
tab.base_url = (
"%s?formsemestre_id=%s&percent=%s&bac=%s&bacspecialite=%s&civilite=%s"
% (REQUEST.URL0, formsemestre_id, percent, bac, bacspecialite, civilite)
% (request.base_url, formsemestre_id, percent, bac, bacspecialite, civilite)
)
if only_primo:
tab.base_url += "&only_primo=on"
@ -725,7 +725,7 @@ def formsemestre_suivi_cohorte(
if format != "html":
return t
base_url = REQUEST.URL0
base_url = request.base_url
burl = "%s?formsemestre_id=%s&bac=%s&bacspecialite=%s&civilite=%s&statut=%s" % (
base_url,
formsemestre_id,
@ -815,7 +815,7 @@ def _gen_form_selectetuds(
<p>Bac: <select name="bac" onchange="javascript: submit(this);">
<option value="" %s>tous</option>
"""
% (REQUEST.URL0, selected)
% (request.base_url, selected)
]
for b in bacs:
if bac == b:
@ -1166,7 +1166,7 @@ def table_suivi_parcours(formsemestre_id, only_primo=False, grouped_parcours=Tru
def tsp_form_primo_group(REQUEST, only_primo, no_grouping, formsemestre_id, format):
"""Element de formulaire pour choisir si restriction aux primos entrants et groupement par lycees"""
F = ["""<form name="f" method="get" action="%s">""" % REQUEST.URL0]
F = ["""<form name="f" method="get" action="%s">""" % request.base_url]
if only_primo:
checked = 'checked="1"'
else:
@ -1204,7 +1204,7 @@ def formsemestre_suivi_parcours(
only_primo=only_primo,
grouped_parcours=not no_grouping,
)
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
if only_primo:
tab.base_url += "&only_primo=1"
if no_grouping:
@ -1491,7 +1491,7 @@ def formsemestre_graph_parcours(
statut=statut,
)
filename = scu.make_filename("flux " + sem["titreannee"])
return scu.sendPDFFile(REQUEST, doc, filename + ".pdf")
return scu.sendPDFFile(doc, filename + ".pdf")
elif format == "png":
#
(

View File

@ -35,7 +35,7 @@ import datetime
import psycopg2
import flask
from flask import g, url_for
from flask import g, url_for, request
from flask_login import current_user
import app.scodoc.sco_utils as scu
@ -168,7 +168,7 @@ def do_evaluation_upload_xls(REQUEST):
"""
Soumission d'un fichier XLS (evaluation_id, notefile)
"""
authuser = REQUEST.AUTHENTICATED_USER
authuser = current_user
evaluation_id = int(REQUEST.form["evaluation_id"])
comment = REQUEST.form["comment"]
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
@ -657,7 +657,7 @@ def saisie_notes_tableur(evaluation_id, group_ids=[], REQUEST=None):
)
nf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("evaluation_id", {"default": evaluation_id, "input_type": "hidden"}),
@ -711,7 +711,7 @@ def saisie_notes_tableur(evaluation_id, group_ids=[], REQUEST=None):
#
H.append("""</div><h3>Autres opérations</h3><ul>""")
if sco_permissions_check.can_edit_notes(
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
current_user, E["moduleimpl_id"], allow_ens=False
):
H.append(
"""
@ -829,8 +829,7 @@ def feuille_saisie_notes(evaluation_id, group_ids=[], REQUEST=None):
filename = "notes_%s_%s.xlsx" % (evalname, gr_title_filename)
xls = sco_excel.excel_feuille_saisie(E, sem["titreannee"], description, lines=L)
breakpoint()
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True)
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE)
# return sco_excel.send_excel_file(REQUEST, xls, filename)
@ -1223,7 +1222,7 @@ def _form_saisie_notes(E, M, group_ids, destination="", REQUEST=None):
def save_note(etudid=None, evaluation_id=None, value=None, comment="", REQUEST=None):
"""Enregistre une note (ajax)"""
authuser = REQUEST.AUTHENTICATED_USER
authuser = current_user
log(
"save_note: evaluation_id=%s etudid=%s uid=%s value=%s"
% (evaluation_id, etudid, authuser, value)
@ -1260,7 +1259,7 @@ def save_note(etudid=None, evaluation_id=None, value=None, comment="", REQUEST=N
else:
result["history_menu"] = "" # no update needed
result["status"] = "ok"
return scu.sendJSON(REQUEST, result)
return scu.sendJSON(result)
def get_note_history_menu(evaluation_id, etudid):

View File

@ -171,7 +171,7 @@ class SemSet(dict):
def remove(self, formsemestre_id):
ndb.SimpleQuery(
"""DELETE FROM notes_semset_formsemestre
WHERE id=%(semset_id)s
WHERE semset_id=%(semset_id)s
AND formsemestre_id=%(formsemestre_id)s
""",
{"formsemestre_id": formsemestre_id, "semset_id": self.semset_id},

View File

@ -214,7 +214,7 @@ def module_tag_search(term, REQUEST=None):
)
data = [x["title"] for x in r]
return scu.sendJSON(REQUEST, data)
return scu.sendJSON(data)
def module_tag_list(module_id=""):

View File

@ -44,7 +44,7 @@ from reportlab.lib import colors
from PIL import Image as PILImage
import flask
from flask import url_for, g, send_file
from flask import url_for, g, send_file, request
from app import log
import app.scodoc.sco_utils as scu
@ -466,7 +466,7 @@ def _listeappel_photos_pdf(groups_infos, REQUEST):
document.build(objects)
data = report.getvalue()
return scu.sendPDFFile(REQUEST, data, filename)
return scu.sendPDFFile(data, filename)
# --------------------- Upload des photos de tout un groupe
@ -486,8 +486,9 @@ def photos_generate_excel_sample(group_ids=[], REQUEST=None):
],
extra_cols=["fichier_photo"],
)
breakpoint()
return scu.send_file(data, "ImportPhotos", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True)
return scu.send_file(
data, "ImportPhotos", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True
)
# return sco_excel.send_excel_file(REQUEST, data, "ImportPhotos" + scu.XLSX_SUFFIX)
@ -518,7 +519,7 @@ def photos_import_files_form(group_ids=[], REQUEST=None):
F = html_sco_header.sco_footer()
REQUEST.form["group_ids"] = groups_infos.group_ids
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("xlsfile", {"title": "Fichier Excel:", "input_type": "file", "size": 40}),

View File

@ -272,7 +272,7 @@ def pdf_trombino_tours(
document.build(objects)
data = report.getvalue()
return scu.sendPDFFile(REQUEST, data, filename)
return scu.sendPDFFile(data, filename)
# Feuille d'absences en pdf avec photos:
@ -466,4 +466,4 @@ def pdf_feuille_releve_absences(
document.build(objects)
data = report.getvalue()
return scu.sendPDFFile(REQUEST, data, filename)
return scu.sendPDFFile(data, filename)

View File

@ -54,6 +54,7 @@ Solution proposée (nov 2014):
"""
import flask
from flask import request
from flask_login import current_user
import app.scodoc.notesdb as ndb
@ -168,7 +169,7 @@ def external_ue_inscrit_et_note(
)
# Saisie des notes
_, _, _ = sco_saisie_notes._notes_add(
REQUEST.AUTHENTICATED_USER,
current_user,
evaluation_id,
list(notes_etuds.items()),
do_it=True,
@ -248,7 +249,7 @@ def external_ue_create_form(formsemestre_id, etudid, REQUEST=None):
default_label = "Aucune UE externe existante"
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("formsemestre_id", {"input_type": "hidden"}),

View File

@ -46,6 +46,8 @@ Opérations:
"""
import datetime
from flask import request
from app.scodoc.intervals import intervalmap
import app.scodoc.sco_utils as scu
@ -217,7 +219,7 @@ def formsemestre_list_saisies_notes(formsemestre_id, format="html", REQUEST=None
html_sortable=True,
caption="Saisies de notes dans %s" % sem["titreannee"],
preferences=sco_preferences.SemPreferences(formsemestre_id),
base_url="%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id),
base_url="%s?formsemestre_id=%s" % (request.base_url, formsemestre_id),
origin="Généré par %s le " % sco_version.SCONAME
+ scu.timedate_human_repr()
+ "",
@ -261,7 +263,7 @@ def get_note_history(evaluation_id, etudid, REQUEST=None, fmt=""):
x["user_name"] = sco_users.user_info(x["uid"])["nomcomplet"]
if fmt == "json":
return scu.sendJSON(REQUEST, history)
return scu.sendJSON(history)
else:
return history

View File

@ -30,7 +30,7 @@
# Anciennement ZScoUsers.py, fonctions de gestion des données réécrite avec flask/SQLAlchemy
from flask import url_for, g
from flask import url_for, g, request
from flask_login import current_user
import cracklib # pylint: disable=import-error
@ -51,11 +51,7 @@ import app.scodoc.sco_utils as scu
from app.scodoc.sco_exceptions import (
AccessDenied,
ScoException,
ScoValueError,
ScoInvalidDateError,
ScoLockedFormError,
ScoGenError,
)
@ -117,7 +113,7 @@ def index_html(REQUEST, all_depts=False, with_inactives=False, format="html"):
<input type="checkbox" name="all_depts" value="1" onchange="document.f.submit();" %s>Tous les départements</input>
<input type="checkbox" name="with_inactives" value="1" onchange="document.f.submit();" %s>Avec anciens utilisateurs</input>
</form></p>"""
% (REQUEST.URL0, checked, olds_checked)
% (request.base_url, checked, olds_checked)
)
L = list_users(
@ -212,7 +208,7 @@ def list_users(
html_class="table_leftalign list_users",
html_with_td_classes=True,
html_sortable=True,
base_url="%s?all=%s" % (REQUEST.URL0, all),
base_url="%s?all=%s" % (request.base_url, all),
pdf_link=False, # table is too wide to fit in a paper page => disable pdf
preferences=sco_preferences.SemPreferences(),
)

View File

@ -297,6 +297,7 @@ SCO_DEV_MAIL = "emmanuel.viennet@gmail.com" # SVP ne pas changer
# Adresse pour l'envoi des dumps (pour assistance technnique):
# ne pas changer (ou vous perdez le support)
SCO_DUMP_UP_URL = "https://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/upload-dump"
# SCO_DUMP_UP_URL = "http://192.168.56.1:5000/upload_dump"
CSV_FIELDSEP = ";"
CSV_LINESEP = "\n"
@ -479,14 +480,17 @@ def sanitize_string(s):
return suppress_accents(s.translate(trans)).replace(" ", "_").replace("\t", "_")
_BAD_FILENAME_CHARS = str.maketrans("", "", ":/\\")
_BAD_FILENAME_CHARS = str.maketrans("", "", ":/\\&[]*?'")
def make_filename(name):
"""Try to convert name to a reasonable filename
without spaces, (back)slashes, : and without accents
"""
return suppress_accents(name.translate(_BAD_FILENAME_CHARS)).replace(" ", "_")
return (
suppress_accents(name.translate(_BAD_FILENAME_CHARS)).replace(" ", "_")
or "scodoc"
)
VALID_CARS = (
@ -512,6 +516,14 @@ def is_valid_filename(filename):
return VALID_EXP.match(filename)
def bul_filename(sem, etud, format):
"""Build a filename for this bulletin"""
dt = time.strftime("%Y-%m-%d")
filename = f"bul-{sem['titre_num']}-{dt}-{etud['nom']}.{format}"
filename = make_filename(filename)
return 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)
@ -526,16 +538,9 @@ def sendCSVFile(REQUEST, data, filename): # DEPRECATED ne plus utiliser
return data
def sendPDFFile(REQUEST, data, filename):
filename = (
unescape_html(suppress_accents(filename)).replace("&", "").replace(" ", "_")
)
if REQUEST:
REQUEST.RESPONSE.setHeader("content-type", PDF_MIMETYPE)
REQUEST.RESPONSE.setHeader(
"content-disposition", 'attachment; filename="%s"' % filename
)
return data
def sendPDFFile(data, filename):
filename = make_filename(filename)
return send_file(data, filename=filename, mime=PDF_MIMETYPE, attached=True)
class ScoDocJSONEncoder(json.JSONEncoder):
@ -548,38 +553,41 @@ class ScoDocJSONEncoder(json.JSONEncoder):
return json.JSONEncoder.default(self, o)
def sendJSON(REQUEST, data):
def sendJSON(data):
js = json.dumps(data, indent=1, cls=ScoDocJSONEncoder)
return send_file(js, filename="sco_data.json", mime=JSON_MIMETYPE, attached=False)
def sendXML(REQUEST, data, tagname=None, force_outer_xml_tag=True):
def sendXML(data, tagname=None, force_outer_xml_tag=True):
if type(data) != list:
data = [data] # always list-of-dicts
if force_outer_xml_tag:
data = [{tagname: data}]
tagname += "_list"
doc = sco_xml.simple_dictlist2xml(data, tagname=tagname)
if REQUEST:
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE)
return doc
return send_file(doc, filename="sco_data.xml", mime=XML_MIMETYPE, attached=False)
def sendResult(REQUEST, data, name=None, format=None, force_outer_xml_tag=True):
def sendResult(data, name=None, format=None, force_outer_xml_tag=True):
if (format is None) or (format == "html"):
return data
elif format == "xml": # name is outer tagname
return sendXML(
REQUEST, data, tagname=name, force_outer_xml_tag=force_outer_xml_tag
)
return sendXML(data, tagname=name, force_outer_xml_tag=force_outer_xml_tag)
elif format == "json":
return sendJSON(REQUEST, data)
return sendJSON(data)
else:
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"
def send_file(data, filename, suffix="", mime=None, attached=None):
"""Build Flask Response for file download of given type
By default (attached is None), json and xml are inlined and otrher types are atteched.
"""
if attached is None:
if mime == XML_MIMETYPE or mime == JSON_MIMETYPE:
attached = True
else:
attached = False
if suffix:
filename += suffix
filename = make_filename(filename)
@ -791,42 +799,13 @@ def AnneeScolaire(sco_year=None):
return year
def log_unknown_etud(REQUEST=None, format="html"):
"""Log request: cas ou getEtudInfo n'a pas ramene de resultat"""
etudid = REQUEST.form.get("etudid", "?")
code_nip = REQUEST.form.get("code_nip", "?")
code_ine = REQUEST.form.get("code_ine", "?")
log(
"unknown student: etudid=%s code_nip=%s code_ine=%s"
% (etudid, code_nip, code_ine)
)
return _sco_error_response("unknown student", format=format, REQUEST=REQUEST)
# XXX #sco8 à tester ou ré-écrire
def _sco_error_response(msg, format="html", REQUEST=None):
"""Send an error message to the client, in html or xml format."""
REQUEST.RESPONSE.setStatus(404, reason=msg)
if format == "html" or format == "pdf":
raise sco_exceptions.ScoValueError(msg)
elif format == "xml":
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE)
doc = ElementTree.Element("error", msg=msg)
return sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(SCO_ENCODING)
elif format == "json":
REQUEST.RESPONSE.setHeader("content-type", JSON_MIMETYPE)
return "undefined" # XXX voir quoi faire en cas d'erreur json
else:
raise ValueError("ScoErrorResponse: invalid format")
def return_text_if_published(val, REQUEST):
"""Pour les méthodes publiées qui ramènent soit du texte (HTML) soit du JSON
sauf quand elles sont appellées depuis python.
La présence de l'argument REQUEST indique la publication.
"""
if REQUEST and not isinstance(val, str):
return sendJSON(REQUEST, val)
return sendJSON(val)
return val
@ -855,9 +834,10 @@ def confirm_dialog(
action = f'action="{dest_url}"'
H = [
f"""<form {action} method="post">""",
message,
"""<input type="submit" value="%s"/>""" % OK,
f"""<form {action} method="POST">
{message}
<input type="submit" value="{OK}"/>
""",
]
if cancel_url:
H.append(

File diff suppressed because it is too large Load Diff

View File

@ -5,13 +5,14 @@
<h2>Erreur !</h2>
<p>{{ exc }}</p>
{{ exc | safe }}
<p>
<p class="footer">
{% if g.scodoc_dept %}
<a href="{{ exc.dest_url or url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}">continuer</a>
<a href="{{ exc.dest_url or url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}">retour page d'accueil
departement {{ g.scodoc_dept }}</a>
{% else %}
<a href="{{ exc.dest_url or url_for('scodoc.index') }}">continuer</a>
<a href="{{ exc.dest_url or url_for('scodoc.index') }}">retour page d'accueil</a>
{% endif %}
</p>

View File

@ -1,21 +1,57 @@
{% import 'bootstrap/wtf.html' as wtf %}
{% macro render_field(field) %}
<tr>
<th class="wtf-field">{{ field.label }}</th>
<td class="wtf-field">{{ field()|safe }}
<tr>
<td class="wtf-field">{{ field.label }}</td>
<td class="wtf-field">{{ field()|safe }}
{% if field.errors %}
<ul class=errors>
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
<li>{{ error }}</li>
{% endfor %}
</ul>
</ul>
{% endif %}
</td>
</tr>
</td>
</tr>
{% endmacro %}
<div class="saisienote_etape1 form_placement">
<form method=post>
{{ form.evaluation_id }}
{{ form.csrf_token }}
<table class="tf">
<tbody>
{{ render_field(form.surveillants) }}
{{ render_field(form.batiment) }}
{{ render_field(form.salle) }}
{{ render_field(form.nb_rangs) }}
{{ render_field(form.etiquetage) }}
{% if form.has_groups %}
{{ render_field(form.groups) }}
<!-- Tentative de recréer le choix des groupes sous forme de cases à cocher // demande à créer des champs wtf dynamiquement
{% for partition in form.groups_tree %}
<tr>
{% if partition == 'Tous' %}
<td rowspan="{{ form.nb_groups }}">Groupes</td>
{% endif %}
<td>{{ partition }}</td>
<td>
{% for groupe in form.groups_tree[partition] %}
{{ groupe }}{{ form[form.groups_tree[partition][groupe]] }}
{% endfor %}
</td>
</tr>
{% endfor %}
-->
{% endif %}
{{ render_field(form.file_format) }}
</tbody>
</table>
<p>
<input id="gr_submit" type=submit value="Ok">
<input id="gr_cancel" type=submit value="Annuler">
</script>
</form>
<form method=post>
{{ form.evaluation_id }}
{{ form.csrf_token }}
@ -57,25 +93,28 @@
</script>
</form>
<h3>Explications</h3>
<ul>
<li>préciser les surveillants et la localisation (bâtiment et salle) et indiquer le nombre de colonnes;</li>
<li>deux types de placements sont possibles :
<ul>
<li>continue suppose que les tables ont toutes un numéro unique;</li>
<li>coordonnées localise chaque table via un numéro de colonne et un numéro de ligne (ou rangée).</li>
</ul></li>
<li>il est possible de choisir un ou plusieurs groupes (shift/ctrl click) ou de choisir 'tous'.</li>
<li>Choisir le format du fichier résultat :
<ul>
<li>le format pdf consiste en un tableau précisant pour chaque étudiant la localisation de sa table;</li>
<li>le format xls produit un classeur avec deux onglets:
<ul>
<li>le premier onglet donne une vue de la salle avec la localisation des étudiants et
peut servir de feuille d'émargement;</li>
<li>le second onglet est un tableau similaire à celui du fichier pdf;</li>
</ul></li>
</ul> </li>
</ul>
</div>
<ul>
<li>préciser les surveillants et la localisation (bâtiment et salle) et indiquer la largeur de la salle (nombre
de colonnes);</li>
<li>deux types de placements sont possibles :
<ul>
<li>continue suppose que les tables ont toutes un numéro unique;</li>
<li>coordonnées localise chaque table via un numéro de colonne et un numéro de ligne (ou rangée).</li>
</ul>
</li>
<li>Il est possible de choisir un ou plusieurs groupes (shift/ctrl click) ou de choisir 'tous'.</li>
<li>Choisir le format du fichier résultat :
<ul>
<li>le format pdf consiste en un tableau précisant pour chaque étudiant la localisation de sa table;
</li>
<li>le format xls produit un classeur avec deux onglets:
<ul>
<li>le premier onglet donne une vue de la salle avec la localisation des étudiants et
peut servir de feuille d'émargement;</li>
<li>le second onglet est un tableau similaire à celui du fichier pdf;</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>

View File

@ -56,8 +56,9 @@ import urllib
from xml.etree import ElementTree
import flask
from flask import g
from flask import g, request
from flask import url_for
from flask_login import current_user
from app.decorators import (
scodoc,
@ -124,7 +125,7 @@ def index_html(REQUEST=None):
"""Gestionnaire absences, page principale"""
# crude portage from 1999 DTML
sems = sco_formsemestre.do_formsemestre_list()
authuser = REQUEST.AUTHENTICATED_USER
authuser = current_user
H = [
html_sco_header.sco_header(
@ -161,7 +162,7 @@ def index_html(REQUEST=None):
Saisie par semaine </span> - Choix du groupe:
<input name="datelundi" type="hidden" value="x"/>
"""
% REQUEST.URL0,
% request.base_url,
sco_abs_views.formChoixSemestreGroupe(),
"</p>",
cal_select_week(),
@ -961,7 +962,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
<input type="submit" name="" value="visualiser les absences">
</form></div>
"""
% (REQUEST.URL0, formsemestre_id, groups_infos.get_form_elem()),
% (request.base_url, formsemestre_id, groups_infos.get_form_elem()),
)
return tab.make_page(format=format)
@ -1069,7 +1070,9 @@ def AddBilletAbsence(
# check etudid
etuds = sco_etud.get_etud_info(etudid=etudid, code_nip=code_nip, filled=True)
if not etuds:
return scu.log_unknown_etud(REQUEST=REQUEST)
sco_etud.log_unknown_etud()
raise ScoValueError("étudiant inconnu")
etud = etuds[0]
# check dates
begin_date = dateutil.parser.isoparse(begin) # may raises ValueError
@ -1119,7 +1122,7 @@ def AddBilletAbsenceForm(etudid, REQUEST=None):
)
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("etudid", {"input_type": "hidden"}),
@ -1220,11 +1223,12 @@ def _tableBillets(billets, etud=None, title=""):
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func
def listeBilletsEtud(etudid=False, REQUEST=None, format="html"):
def listeBilletsEtud(etudid=False, format="html"):
"""Liste billets pour un etudiant"""
etuds = sco_etud.get_etud_info(filled=True, etudid=etudid)
if not etuds:
return scu.log_unknown_etud(format=format, REQUEST=REQUEST)
sco_etud.log_unknown_etud()
raise ScoValueError("étudiant inconnu")
etud = etuds[0]
cnx = ndb.GetDBConnexion()
@ -1239,12 +1243,12 @@ def listeBilletsEtud(etudid=False, REQUEST=None, format="html"):
@scodoc
@permission_required_compat_scodoc7(Permission.ScoView)
@scodoc7func
def XMLgetBilletsEtud(etudid=False, REQUEST=None):
def XMLgetBilletsEtud(etudid=False):
"""Liste billets pour un etudiant"""
if not sco_preferences.get_preference("handle_billets_abs"):
return ""
t0 = time.time()
r = listeBilletsEtud(etudid, REQUEST=REQUEST, format="xml")
r = listeBilletsEtud(etudid, format="xml")
log("XMLgetBilletsEtud (%gs)" % (time.time() - t0))
return r
@ -1256,7 +1260,7 @@ def XMLgetBilletsEtud(etudid=False, REQUEST=None):
def listeBillets(REQUEST=None):
"""Page liste des billets non traités et formulaire recherche d'un billet"""
cnx = ndb.GetDBConnexion()
billets = sco_abs.billet_absence_list(cnx, {"etat": 0})
billets = sco_abs.billet_absence_list(cnx, {"etat": False})
tab = _tableBillets(billets)
T = tab.html()
H = [
@ -1265,7 +1269,7 @@ def listeBillets(REQUEST=None):
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(("billet_id", {"input_type": "text", "title": "Numéro du billet"}),),
submitbutton=False,
@ -1398,7 +1402,7 @@ def ProcessBilletAbsenceForm(billet_id, REQUEST=None):
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("billet_id", {"input_type": "hidden"}),

View File

@ -38,6 +38,9 @@ import re
import time
import calendar
from flask import request
from flask_login import current_user
# MIGRATION EN COURS => MODULE DESACTIVE !
# A REVOIR
@ -79,7 +82,7 @@ def sidebar(REQUEST):
<ul class="insidebar">"""
% params,
]
if REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoEntrepriseChange):
if current_user.has_permission(Permission.ScoEntrepriseChange):
H.append(
"""<li class="insidebar"><a href="%(ScoURL)s/Entreprises/entreprise_create" class="sidebar">Nouvelle entreprise</a> </li>"""
% params
@ -104,9 +107,7 @@ def sidebar(REQUEST):
<li class="insidebar"><a href="%(ScoURL)s/Entreprises/entreprise_correspondant_list?entreprise_id=%(entreprise_id)s" class="sidebar">Corresp.</a></li>"""
% params
) # """
if REQUEST.AUTHENTICATED_USER.has_permission(
Permission.ScoEntrepriseChange
):
if current_user.has_permission(Permission.ScoEntrepriseChange):
H.append(
"""<li class="insidebar"><a href="%(ScoURL)s/Entreprises/entreprise_correspondant_create?entreprise_id=%(entreprise_id)s" class="sidebar">Nouveau Corresp.</a></li>"""
% params
@ -115,9 +116,7 @@ def sidebar(REQUEST):
"""<li class="insidebar"><a href="%(ScoURL)s/Entreprises/entreprise_contact_list?entreprise_id=%(entreprise_id)s" class="sidebar">Contacts</a></li>"""
% params
)
if REQUEST.AUTHENTICATED_USER.has_permission(
Permission.ScoEntrepriseChange
):
if current_user.has_permission(Permission.ScoEntrepriseChange):
H.append(
"""<li class="insidebar"><a href="%(ScoURL)s/Entreprises/entreprise_contact_create?entreprise_id=%(entreprise_id)s" class="sidebar">Nouveau "contact"</a></li>"""
% params
@ -126,7 +125,7 @@ def sidebar(REQUEST):
#
H.append("""<br/><br/>%s""" % scu.icontag("entreprise_side_img"))
if not REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoEntrepriseChange):
if not current_user.has_permission(Permission.ScoEntrepriseChange):
H.append("""<br/><em>(Lecture seule)</em>""")
H.append("""</div> </div> <!-- end of sidebar -->""")
return "".join(H)
@ -166,14 +165,14 @@ def index_html(REQUEST=None, etud_nom=None, limit=50, offset="", format="html"):
if offset:
webparams["offset"] = max((offset or 0) - limit, 0)
prev_lnk = '<a class="stdlink" href="%s">précédentes</a>' % (
REQUEST.URL0 + "?" + six.moves.urllib.parse.urlencode(webparams)
request.base_url + "?" + six.moves.urllib.parse.urlencode(webparams)
)
else:
prev_lnk = ""
if len(entreprises) >= limit:
webparams["offset"] = (offset or 0) + limit
next_lnk = '<a class="stdlink" href="%s">suivantes</a>' % (
REQUEST.URL0 + "?" + six.moves.urllib.parse.urlencode(webparams)
request.base_url + "?" + six.moves.urllib.parse.urlencode(webparams)
)
else:
next_lnk = ""
@ -220,7 +219,7 @@ def index_html(REQUEST=None, etud_nom=None, limit=50, offset="", format="html"):
html_class="entreprise_list table_leftalign",
html_with_td_classes=True,
html_next_section=table_navigation,
base_url=REQUEST.URL0 + "?",
base_url=request.base_url + "?",
preferences=context.get_preferences(),
)
if format != "html":
@ -293,7 +292,7 @@ def entreprise_contact_list(entreprise_id=None, format="html", REQUEST=None):
html_sortable=True,
html_class="contact_list table_leftalign",
html_with_td_classes=True,
base_url=REQUEST.URL0 + "?",
base_url=request.base_url + "?",
preferences=context.get_preferences(),
)
if format != "html":
@ -301,7 +300,7 @@ def entreprise_contact_list(entreprise_id=None, format="html", REQUEST=None):
H.append(tab.html())
if REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoEntrepriseChange):
if current_user.has_permission(Permission.ScoEntrepriseChange):
if entreprise_id:
H.append(
"""<p class="entreprise_create"><a class="entreprise_create" href="entreprise_contact_create?entreprise_id=%(entreprise_id)s">nouveau "contact"</a></p>
@ -399,7 +398,7 @@ def entreprise_correspondant_list(
html_sortable=True,
html_class="contact_list table_leftalign",
html_with_td_classes=True,
base_url=REQUEST.URL0 + "?",
base_url=request.base_url + "?",
preferences=context.get_preferences(),
)
if format != "html":
@ -407,7 +406,7 @@ def entreprise_correspondant_list(
H.append(tab.html())
if REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoEntrepriseChange):
if current_user.has_permission(Permission.ScoEntrepriseChange):
H.append(
"""<p class="entreprise_create"><a class="entreprise_create" href="entreprise_correspondant_create?entreprise_id=%(entreprise_id)s">Ajouter un correspondant dans l'entreprise %(nom)s</a></p>
"""
@ -444,7 +443,7 @@ def entreprise_contact_edit(entreprise_contact_id, REQUEST=None):
% E,
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
(
@ -515,14 +514,12 @@ def entreprise_contact_edit(entreprise_contact_id, REQUEST=None):
cancelbutton="Annuler",
initvalues=c,
submitlabel="Modifier les valeurs",
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
Permission.ScoEntrepriseChange
),
readonly=not current_user.has_permission(Permission.ScoEntrepriseChange),
)
if tf[0] == 0:
H.append(tf[1])
if REQUEST.AUTHENTICATED_USER.has_permission(
if current_user.has_permission(
Permission.ScoEntrepriseChange,
):
H.append(
@ -560,7 +557,7 @@ def entreprise_correspondant_edit(entreprise_corresp_id, REQUEST=None):
"""<h2 class="entreprise_correspondant">Édition contact entreprise</h2>""",
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
(
@ -641,9 +638,7 @@ def entreprise_correspondant_edit(entreprise_corresp_id, REQUEST=None):
cancelbutton="Annuler",
initvalues=c,
submitlabel="Modifier les valeurs",
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
Permission.ScoEntrepriseChange
),
readonly=not current_user.has_permission(Permission.ScoEntrepriseChange),
)
if tf[0] == 0:
H.append(tf[1])
@ -684,7 +679,7 @@ def entreprise_contact_create(entreprise_id, REQUEST=None):
% E,
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("entreprise_id", {"input_type": "hidden", "default": entreprise_id}),
@ -750,9 +745,7 @@ def entreprise_contact_create(entreprise_id, REQUEST=None):
),
cancelbutton="Annuler",
submitlabel="Ajouter ce contact",
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
Permission.ScoEntrepriseChange
),
readonly=not current_user.has_permission(Permission.ScoEntrepriseChange),
)
if tf[0] == 0:
H.append(tf[1])
@ -783,13 +776,13 @@ def entreprise_contact_delete(entreprise_contact_id, REQUEST=None):
"""<h2>Suppression du contact</h2>""",
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(("entreprise_contact_id", {"input_type": "hidden"}),),
initvalues=c,
submitlabel="Confirmer la suppression",
cancelbutton="Annuler",
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(ScoEntrepriseChange),
readonly=not current_user.has_permission(ScoEntrepriseChange),
)
if tf[0] == 0:
H.append(tf[1])
@ -814,7 +807,7 @@ def entreprise_correspondant_create(entreprise_id, REQUEST=None):
% E,
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("entreprise_id", {"input_type": "hidden", "default": entreprise_id}),
@ -892,9 +885,7 @@ def entreprise_correspondant_create(entreprise_id, REQUEST=None):
),
cancelbutton="Annuler",
submitlabel="Ajouter ce correspondant",
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
Permission.ScoEntrepriseChange
),
readonly=not current_user.has_permission(Permission.ScoEntrepriseChange),
)
if tf[0] == 0:
H.append(tf[1])
@ -920,15 +911,13 @@ def entreprise_correspondant_delete(entreprise_corresp_id, REQUEST=None):
"""<h2>Suppression du correspondant %(nom)s %(prenom)s</h2>""" % c,
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(("entreprise_corresp_id", {"input_type": "hidden"}),),
initvalues=c,
submitlabel="Confirmer la suppression",
cancelbutton="Annuler",
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
Permission.ScoEntrepriseChange
),
readonly=not current_user.has_permission(Permission.ScoEntrepriseChange),
)
if tf[0] == 0:
H.append(tf[1])
@ -976,15 +965,13 @@ def entreprise_delete(entreprise_id, REQUEST=None):
H.append("""<li>%(date)s %(description)s</li>""" % c)
H.append("""</ul>""")
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(("entreprise_id", {"input_type": "hidden"}),),
initvalues=E,
submitlabel="Confirmer la suppression",
cancelbutton="Annuler",
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
Permission.ScoEntrepriseChange
),
readonly=not current_user.has_permission(Permission.ScoEntrepriseChange),
)
if tf[0] == 0:
H.append(tf[1])
@ -1008,7 +995,7 @@ def entreprise_create(REQUEST=None):
"""<h2 class="entreprise_new">Création d'une entreprise</h2>""",
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("nom", {"size": 25, "title": "Nom de l'entreprise"}),
@ -1079,9 +1066,7 @@ def entreprise_create(REQUEST=None):
),
cancelbutton="Annuler",
submitlabel="Ajouter cette entreprise",
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
Permission.ScoEntrepriseChange
),
readonly=not current_user.has_permission(Permission.ScoEntrepriseChange),
)
if tf[0] == 0:
return "\n".join(H) + tf[1] + entreprise_footer(REQUEST)
@ -1097,7 +1082,7 @@ security.declareProtected(ScoEntrepriseView, "entreprise_edit")
def entreprise_edit(entreprise_id, REQUEST=None, start=1):
"""Form. edit entreprise"""
authuser = REQUEST.AUTHENTICATED_USER
authuser = current_user
readonly = not authuser.has_permission(Permission.ScoEntrepriseChange)
F = sco_entreprises.do_entreprise_list(args={"entreprise_id": entreprise_id})[0]
H = [
@ -1105,7 +1090,7 @@ def entreprise_edit(entreprise_id, REQUEST=None, start=1):
"""<h2 class="entreprise">%(nom)s</h2>""" % F,
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("entreprise_id", {"default": entreprise_id, "input_type": "hidden"}),

View File

@ -38,8 +38,8 @@ from operator import itemgetter
from xml.etree import ElementTree
import flask
from flask import url_for, g
from flask import current_app
from flask import url_for
from flask import current_app, g, request
from flask_login import current_user
from config import Config
@ -146,23 +146,31 @@ def sco_publish(route, function, permission, methods=["GET"]):
# --------------------- Quelques essais élémentaires:
@bp.route("/essai")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func
def essai(REQUEST=None):
return essai_(REQUEST)
# @bp.route("/essai")
# @scodoc
# @permission_required(Permission.ScoView)
# @scodoc7func
# def essai(REQUEST=None):
# return essai_(REQUEST)
def essai_(REQUEST):
return "<html><body><h2>essai !</h2><p>%s</p></body></html>" % (REQUEST,)
# def essai_(REQUEST):
# return "<html><body><h2>essai !</h2><p>%s</p></body></html>" % (REQUEST,)
def essai2():
return essai_("sans request")
# def essai2():
# err_page = f"""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>
# <p class="help">Il faut d'abord supprimer le semestre. Mais il est peut être préférable de
# laisser ce programme intact et d'en créer une nouvelle version pour la modifier.
# </p>
# <a href="url_for('notes.ue_list', scodoc-dept=g.scodoc_dept, formation_id='XXX')">reprendre</a>
# """
# raise ScoGenError(err_page)
# # raise ScoGenError("une erreur banale")
# return essai_("sans request")
sco_publish("/essai2", essai2, Permission.ScoImplement)
# sco_publish("/essai2", essai2, Permission.ScoImplement)
# --------------------------------------------------------------------
@ -406,7 +414,7 @@ sco_publish(
def index_html(REQUEST=None):
"Page accueil formations"
editable = REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoChangeFormation)
editable = current_user.has_permission(Permission.ScoChangeFormation)
H = [
html_sco_header.sco_header(page_title="Programmes formations"),
@ -461,7 +469,7 @@ def formation_list(format=None, REQUEST=None, formation_id=None, args={}):
(when args is given, formation_id is ignored).
"""
r = sco_formations.formation_list(formation_id=formation_id, args=args)
return scu.sendResult(REQUEST, r, name="formation", format=format)
return scu.sendResult(r, name="formation", format=format)
@bp.route("/formation_export")
@ -501,7 +509,7 @@ def formation_import_xml_form(REQUEST):
]
footer = html_sco_header.sco_footer()
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(("xmlfile", {"input_type": "file", "title": "Fichier XML", "size": 30}),),
submitlabel="Importer",
@ -606,8 +614,7 @@ sco_publish("/ue_move", sco_edit_formation.ue_move, Permission.ScoChangeFormatio
@permission_required_compat_scodoc7(Permission.ScoView)
@scodoc7func
def formsemestre_list(
format=None,
REQUEST=None,
format="json",
formsemestre_id=None,
formation_id=None,
etape_apo=None,
@ -624,7 +631,7 @@ def formsemestre_list(
args[argname] = L[argname]
sems = sco_formsemestre.do_formsemestre_list(args=args)
# log('formsemestre_list: format="%s", %s semestres found' % (format,len(sems)))
return scu.sendResult(REQUEST, sems, name="formsemestre", format=format)
return scu.sendResult(sems, name="formsemestre", format=format)
@bp.route(
@ -637,7 +644,7 @@ def XMLgetFormsemestres(etape_apo=None, formsemestre_id=None, REQUEST=None):
"""List all formsemestres matching etape, XML format
DEPRECATED: use formsemestre_list()
"""
log("Warning: calling deprecated XMLgetFormsemestres")
current_app.logger.debug("Warning: calling deprecated XMLgetFormsemestres")
args = {}
if etape_apo:
args["etape_apo"] = etape_apo
@ -778,7 +785,7 @@ def edit_enseignants_form(REQUEST, moduleimpl_id):
),
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
modform,
submitlabel="Ajouter enseignant",
@ -875,7 +882,7 @@ def edit_moduleimpl_resp(REQUEST, moduleimpl_id):
),
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
form,
submitlabel="Changer responsable",
@ -979,7 +986,7 @@ def edit_moduleimpl_expr(REQUEST, moduleimpl_id):
),
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
form,
submitlabel="Modifier formule de calcul",
@ -1083,7 +1090,7 @@ def view_module_abs(REQUEST, moduleimpl_id, format="html"):
columns_ids=("nomprenom", "just", "nojust", "total"),
rows=T,
html_class="table_leftalign",
base_url="%s?moduleimpl_id=%s" % (REQUEST.URL0, moduleimpl_id),
base_url="%s?moduleimpl_id=%s" % (request.base_url, moduleimpl_id),
filename="absmodule_" + scu.make_filename(M["module"]["titre"]),
caption="Absences dans le module %s" % M["module"]["titre"],
preferences=sco_preferences.SemPreferences(),
@ -1139,7 +1146,7 @@ def edit_ue_expr(REQUEST, formsemestre_id, ue_id):
),
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
form,
submitlabel="Modifier formule de calcul",
@ -1253,7 +1260,7 @@ def formsemestre_enseignants_list(REQUEST, formsemestre_id, format="html"):
html_title=html_sco_header.html_sem_header(
REQUEST, "Enseignants du semestre", sem, with_page_header=False
),
base_url="%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id),
base_url="%s?formsemestre_id=%s" % (request.base_url, formsemestre_id),
caption="Tous les enseignants (responsables ou associés aux modules de ce semestre) apparaissent. Le nombre de saisies d'absences est le nombre d'opérations d'ajout effectuées sur ce semestre, sans tenir compte des annulations ou double saisies.",
preferences=sco_preferences.SemPreferences(formsemestre_id),
)
@ -1320,7 +1327,7 @@ def do_formsemestre_inscription_listinscrits(
r = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits(
formsemestre_id
)
return scu.sendResult(REQUEST, r, format=format, name="inscrits")
return scu.sendResult(r, format=format, name="inscrits")
@bp.route("/formsemestre_desinscription", methods=["GET", "POST"])
@ -1538,7 +1545,7 @@ def evaluation_delete(REQUEST, evaluation_id):
H.append("""</div>""")
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(("evaluation_id", {"input_type": "hidden"}),),
initvalues=E,
@ -1691,7 +1698,7 @@ def formsemestre_bulletins_pdf(formsemestre_id, REQUEST, version="selectedevals"
pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf(
formsemestre_id, REQUEST, version=version
)
return scu.sendPDFFile(REQUEST, pdfdoc, filename)
return scu.sendPDFFile(pdfdoc, filename)
_EXPL_BULL = """Versions des bulletins:<ul><li><bf>courte</bf>: moyennes des modules</li><li><bf>intermédiaire</bf>: moyennes des modules et notes des évaluations sélectionnées</li><li><bf>complète</bf>: toutes les notes</li><ul>"""
@ -1707,7 +1714,7 @@ def formsemestre_bulletins_pdf_choice(REQUEST, formsemestre_id, version=None):
pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf(
formsemestre_id, REQUEST, version=version
)
return scu.sendPDFFile(REQUEST, pdfdoc, filename)
return scu.sendPDFFile(pdfdoc, filename)
return formsemestre_bulletins_choice(
REQUEST,
formsemestre_id,
@ -1725,7 +1732,7 @@ def etud_bulletins_pdf(etudid, REQUEST, version="selectedevals"):
pdfdoc, filename = sco_bulletins_pdf.get_etud_bulletins_pdf(
etudid, REQUEST, version=version
)
return scu.sendPDFFile(REQUEST, pdfdoc, filename)
return scu.sendPDFFile(pdfdoc, filename)
@bp.route("/formsemestre_bulletins_mailetuds_choice")
@ -1776,7 +1783,7 @@ def formsemestre_bulletins_choice(
<form name="f" method="GET" action="%s">
<input type="hidden" name="formsemestre_id" value="%s"></input>
"""
% (REQUEST.URL0, formsemestre_id),
% (request.base_url, formsemestre_id),
]
H.append("""<select name="version" class="noprint">""")
for (v, e) in (
@ -1928,7 +1935,7 @@ def appreciation_add_form(
else:
initvalues = {}
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
descr,
initvalues=initvalues,
@ -2010,8 +2017,7 @@ def formsemestre_validation_etud(
"Enregistre choix jury pour un étudiant"
if not sco_permissions_check.can_validate_sem(formsemestre_id):
return scu.confirm_dialog(
message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER,
message="<p>Opération non autorisée pour %s</h2>" % current_user,
dest_url=scu.ScoURL(),
)
@ -2043,8 +2049,7 @@ def formsemestre_validation_etud_manu(
"Enregistre choix jury pour un étudiant"
if not sco_permissions_check.can_validate_sem(formsemestre_id):
return scu.confirm_dialog(
message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER,
message="<p>Opération non autorisée pour %s</h2>" % current_user,
dest_url=scu.ScoURL(),
)
@ -2069,8 +2074,7 @@ def formsemestre_validate_previous_ue(formsemestre_id, etudid=None, REQUEST=None
"Form. saisie UE validée hors ScoDoc"
if not sco_permissions_check.can_validate_sem(formsemestre_id):
return scu.confirm_dialog(
message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER,
message="<p>Opération non autorisée pour %s</h2>" % current_user,
dest_url=scu.ScoURL(),
)
return sco_formsemestre_validation.formsemestre_validate_previous_ue(
@ -2094,8 +2098,7 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid=None, REQUEST=N
"Form. edition UE semestre extérieur"
if not sco_permissions_check.can_validate_sem(formsemestre_id):
return scu.confirm_dialog(
message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER,
message="<p>Opération non autorisée pour %s</h2>" % current_user,
dest_url=scu.ScoURL(),
)
return sco_formsemestre_exterieurs.formsemestre_ext_edit_ue_validations(
@ -2118,8 +2121,7 @@ def etud_ue_suppress_validation(etudid, formsemestre_id, ue_id, REQUEST=None):
"""Suppress a validation (ue_id, etudid) and redirect to formsemestre"""
if not sco_permissions_check.can_validate_sem(formsemestre_id):
return scu.confirm_dialog(
message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER,
message="<p>Opération non autorisée pour %s</h2>" % current_user,
dest_url=scu.ScoURL(),
)
return sco_formsemestre_validation.etud_ue_suppress_validation(
@ -2135,8 +2137,7 @@ def formsemestre_validation_auto(formsemestre_id, REQUEST):
"Formulaire saisie automatisee des decisions d'un semestre"
if not sco_permissions_check.can_validate_sem(formsemestre_id):
return scu.confirm_dialog(
message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER,
message="<p>Opération non autorisée pour %s</h2>" % current_user,
dest_url=scu.ScoURL(),
)
@ -2153,8 +2154,7 @@ def do_formsemestre_validation_auto(formsemestre_id, REQUEST):
"Formulaire saisie automatisee des decisions d'un semestre"
if not sco_permissions_check.can_validate_sem(formsemestre_id):
return scu.confirm_dialog(
message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER,
message="<p>Opération non autorisée pour %s</h2>" % current_user,
dest_url=scu.ScoURL(),
)
@ -2173,8 +2173,7 @@ def formsemestre_validation_suppress_etud(
"""Suppression des decisions de jury pour un etudiant."""
if not sco_permissions_check.can_validate_sem(formsemestre_id):
return scu.confirm_dialog(
message="<p>Opération non autorisée pour %s</h2>"
% REQUEST.AUTHENTICATED_USER,
message="<p>Opération non autorisée pour %s</h2>" % current_user,
dest_url=scu.ScoURL(),
)
if not dialog_confirmed:
@ -2260,11 +2259,13 @@ sco_publish(
"/view_apo_csv_store",
sco_etape_apogee_view.view_apo_csv_store,
Permission.ScoEditApo,
methods=["GET", "POST"],
)
sco_publish(
"/view_apo_csv_download_and_store",
sco_etape_apogee_view.view_apo_csv_download_and_store,
Permission.ScoEditApo,
methods=["GET", "POST"],
)
sco_publish(
"/view_apo_csv_delete",
@ -2510,7 +2511,7 @@ def check_form_integrity(formation_id, fix=False, REQUEST=None):
log("check_form_integrity: formation_id=%s\ninconsistencies:" % formation_id)
log(txt)
# Notify by e-mail
sendAlarm("Notes: formation incoherente !", txt)
send_scodoc_alarm("Notes: formation incoherente !", txt)
else:
txth = "OK"
log("ok")

View File

@ -68,13 +68,8 @@ from app.scodoc.sco_exceptions import (
AccessDenied,
ScoException,
ScoValueError,
ScoInvalidDateError,
ScoLockedFormError,
ScoGenError,
ScoInvalidDept,
)
from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
import sco_version
import app
from app.scodoc.gen_tables import GenTable
from app.scodoc import html_sco_header
@ -267,7 +262,7 @@ def showEtudLog(etudid, format="html", REQUEST=None):
rows=ops,
html_sortable=True,
html_class="table_leftalign",
base_url="%s?etudid=%s" % (REQUEST.URL0, etudid),
base_url="%s?etudid=%s" % (request.base_url, etudid),
page_title="Opérations sur %(nomprenom)s" % etud,
html_title="<h2>Opérations effectuées sur l'étudiant %(nomprenom)s</h2>" % etud,
filename="log_" + scu.make_filename(etud["nomprenom"]),
@ -341,7 +336,7 @@ def getEtudInfo(etudid=False, code_nip=False, filled=False, REQUEST=None, format
if format is None:
return etud
else:
return scu.sendResult(REQUEST, etud, name="etud", format=format)
return scu.sendResult(etud, name="etud", format=format)
sco_publish(
@ -396,7 +391,7 @@ def etud_info(etudid=None, format="xml", REQUEST=None):
"error": "code etudiant inconnu",
}
return scu.sendResult(
REQUEST, d, name="etudiant", format=format, force_outer_xml_tag=False
d, name="etudiant", format=format, force_outer_xml_tag=False
)
d = {}
etud = etuds[0]
@ -464,9 +459,7 @@ def etud_info(etudid=None, format="xml", REQUEST=None):
)
log("etud_info (%gs)" % (time.time() - t0))
return scu.sendResult(
REQUEST, d, name="etudiant", format=format, force_outer_xml_tag=False
)
return scu.sendResult(d, name="etudiant", format=format, force_outer_xml_tag=False)
# -------------------------- FICHE ETUDIANT --------------------------
@ -617,7 +610,7 @@ def formChangeCoordonnees(etudid, REQUEST):
)
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("adresse_id", {"input_type": "hidden"}),
@ -823,7 +816,7 @@ def formChangePhoto(etudid=None, REQUEST=None):
""",
]
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
("etudid", {"default": etudid, "input_type": "hidden"}),
@ -1496,7 +1489,7 @@ def _etudident_create_or_edit_form(REQUEST, edit):
]
initvalues["dont_check_homonyms"] = False
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
descr,
submitlabel=submitlabel,
@ -1748,7 +1741,7 @@ def check_group_apogee(group_id, REQUEST=None, etat=None, fix=False, fixmail=Fal
<p><a href="Notes/formsemestre_status?formsemestre_id=%s"> Retour au semestre</a>
"""
% (
REQUEST.URL0,
request.base_url,
formsemestre_id,
scu.strnone(group_id),
scu.strnone(etat),
@ -1767,7 +1760,7 @@ def check_group_apogee(group_id, REQUEST=None, etat=None, fix=False, fixmail=Fal
<p><a href="Notes/formsemestre_status?formsemestre_id=%s"> Retour au semestre</a>
"""
% (
REQUEST.URL0,
request.base_url,
formsemestre_id,
scu.strnone(group_id),
scu.strnone(etat),
@ -1856,7 +1849,7 @@ def form_students_import_excel(REQUEST, formsemestre_id=None):
F = html_sco_header.sco_footer()
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
(
@ -1937,8 +1930,9 @@ def import_generate_excel_sample(REQUEST, with_codesemestre="1"):
data = sco_import_etuds.sco_import_generate_excel_sample(
format, with_codesemestre, exclude_cols=["photo_filename"]
)
breakpoint()
return scu.send_file(data, "ImportEtudiants", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True)
return scu.send_file(
data, "ImportEtudiants", scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE
)
# return sco_excel.send_excel_file(REQUEST, data, "ImportEtudiants" + scu.XLSX_SUFFIX)
@ -1957,8 +1951,9 @@ def import_generate_admission_sample(REQUEST, formsemestre_id):
exclude_cols=["nationalite", "foto", "photo_filename"],
group_ids=[group["group_id"]],
)
breakpoint()
return scu.send_file(data, "AdmissionEtudiants", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True)
return scu.send_file(
data, "AdmissionEtudiants", scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE
)
# return sco_excel.send_excel_file(REQUEST, data, "AdmissionEtudiants" + scu.XLSX_SUFFIX)
@ -1969,7 +1964,7 @@ def import_generate_admission_sample(REQUEST, formsemestre_id):
@scodoc7func
def form_students_import_infos_admissions(REQUEST, formsemestre_id=None):
"formulaire import xls"
authuser = REQUEST.AUTHENTICATED_USER
authuser = current_user
F = html_sco_header.sco_footer()
if not authuser.has_permission(Permission.ScoEtudInscrit):
# autorise juste l'export
@ -2023,7 +2018,7 @@ def form_students_import_infos_admissions(REQUEST, formsemestre_id=None):
)
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
(

View File

@ -88,7 +88,7 @@ def index_html(REQUEST, all_depts=False, with_inactives=False, format="html"):
@scodoc7func
def user_info(user_name, format="json", REQUEST=None):
info = sco_users.user_info(user_name)
return scu.sendResult(REQUEST, info, name="user", format=format)
return scu.sendResult(info, name="user", format=format)
@bp.route("/create_user_form", methods=["GET", "POST"])
@ -342,7 +342,7 @@ def create_user_form(REQUEST, user_name=None, edit=0, all_roles=1):
)
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
descr,
initvalues=initvalues,
@ -488,9 +488,7 @@ def create_user_form(REQUEST, user_name=None, edit=0, all_roles=1):
def import_users_generate_excel_sample(REQUEST):
"une feuille excel pour importation utilisateurs"
data = sco_import_users.generate_excel_sample()
breakpoint()
return scu.send_file(data, "ImportUtilisateurs", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True)
# return sco_excel.send_excel_file(REQUEST, data, "ImportUtilisateurs" + scu.XLSX_SUFFIX)
return scu.send_file(data, "ImportUtilisateurs", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE)
@bp.route("/import_users_form", methods=["GET", "POST"])
@ -530,7 +528,7 @@ def import_users_form(REQUEST=None):
)
F = html_sco_header.sco_footer()
tf = TrivialFormulator(
REQUEST.URL0,
request.base_url,
REQUEST.form,
(
(
@ -544,7 +542,7 @@ def import_users_form(REQUEST=None):
if tf[0] == 0:
return "\n".join(H) + tf[1] + "</li></ol>" + help + F
elif tf[0] == -1:
return flask.redirect(back_url)
return flask.redirect(url_for("scolar.index_html", docodc_dept=g.scodoc_dept))
else:
# IMPORT
ok, diag, nb_created = sco_import_users.import_excel_file(tf[2]["xlsfile"])
@ -652,8 +650,8 @@ def change_password(user_name, password, password2, REQUEST):
if not can_handle_passwd(u):
# access denied
log(
"change_password: access denied (authuser=%s, user_name=%s, ip=%s)"
% (REQUEST.AUTHENTICATED_USER, user_name, REQUEST.REMOTE_ADDR)
"change_password: access denied (authuser=%s, user_name=%s)"
% (current_user, user_name)
)
raise AccessDenied("vous n'avez pas la permission de changer ce mot de passe")
H = []

View File

@ -64,7 +64,7 @@ class TestConfig(DevConfig):
TESTING = True
DEBUG = True
SQLALCHEMY_DATABASE_URI = (
os.environ.get("SCODOC_DATABASE_URI") or "postgresql:///SCODOC_TEST"
os.environ.get("SCODOC_TEST_DATABASE_URI") or "postgresql:///SCODOC_TEST"
)
SERVER_NAME = os.environ.get("SCODOC_TEST_SERVER_NAME") or "test.gr"
DEPT_TEST = "TEST_" # nom du département, ne pas l'utiliser pour un "vrai"

View File

@ -0,0 +1,30 @@
"""modif contrainte sur formations
Revision ID: f86c013c9fbd
Revises: 669065fb2d20
Create Date: 2021-09-19 21:30:42.240422
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'f86c013c9fbd'
down_revision = '669065fb2d20'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint('notes_formations_acronyme_titre_version_key', 'notes_formations', type_='unique')
op.create_unique_constraint(None, 'notes_formations', ['dept_id', 'acronyme', 'titre', 'version'])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'notes_formations', type_='unique')
op.create_unique_constraint('notes_formations_acronyme_titre_version_key', 'notes_formations', ['acronyme', 'titre', 'version'])
# ### end Alembic commands ###

View File

@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION = "9.0.28"
SCOVERSION = "9.0.35"
SCONAME = "ScoDoc"

View File

@ -264,6 +264,17 @@ def create_dept(dept): # create-dept
return 0
@app.cli.command()
@click.argument("depts", nargs=-1)
def list_depts(depts=""): # list-dept
"""If dept exists, print it, else nothing.
Called without arguments, list all depts along with their ids.
"""
for dept in models.Departement.query.order_by(models.Departement.id):
if not depts or dept.acronym in depts:
print(f"{dept.id}\t{dept.acronym}")
@app.cli.command()
@with_appcontext
def import_scodoc7_users(): # import-scodoc7-users

View File

@ -246,8 +246,10 @@ def test_abs_basic(test_client):
liste_abs = sco_abs_views.ListeAbsEtud(
etudid, format="json", absjust_only=1, sco_year="2020"
)
liste_abs2 = sco_abs_views.ListeAbsEtud(etudid, format="json", sco_year="2020")
).get_data(as_text=True)
liste_abs2 = sco_abs_views.ListeAbsEtud(
etudid, format="json", sco_year="2020"
).get_data(as_text=True)
load_liste_abs = json.loads(liste_abs)
load_liste_abs2 = json.loads(liste_abs2)
@ -283,7 +285,7 @@ def test_abs_basic(test_client):
format="json",
)
# grp1_abs est une Response car on a appelé une vue (1er appel)
load_grp1_abs = json.loads(grp1_abs.get_data().decode("utf-8"))
load_grp1_abs = json.loads(grp1_abs.get_data(as_text=True))
assert len(load_grp1_abs) == 10
@ -327,7 +329,9 @@ def test_abs_basic(test_client):
code_ine=etuds[0]["code_ine"],
)
li_bi = absences.listeBilletsEtud(etudid=etudid, format="json")
li_bi = absences.listeBilletsEtud(etudid=etudid, format="json").get_data(
as_text=True
)
assert isinstance(li_bi, str)
load_li_bi = json.loads(li_bi)

View File

@ -172,7 +172,7 @@ def test_formations(test_client):
assert load_lif[0]["formation_id"] == f["formation_id"]
assert load_lif[0]["titre"] == f["titre"]
lif2 = notes.formation_list(format="json")
lif2 = notes.formation_list(format="json").get_data(as_text=True)
# lif2 est un chaine
assert isinstance(lif2, str)
load_lif2 = json.loads(lif2)
@ -182,7 +182,9 @@ def test_formations(test_client):
# --- Export de formation_id
exp = sco_formations.formation_export(formation_id=f["formation_id"], format="json")
exp = sco_formations.formation_export(
formation_id=f["formation_id"], format="json"
).get_data(as_text=True)
assert isinstance(exp, str)
load_exp = json.loads(exp)
@ -200,7 +202,7 @@ def test_formations(test_client):
li_sem1 = notes.formsemestre_list(
formsemestre_id=sem1["formsemestre_id"], format="json"
)
).get_data(as_text=True)
assert isinstance(li_sem1, str)
load_li_sem1 = json.loads(li_sem1) # uniquement le semestre 1 dans la liste
@ -212,7 +214,7 @@ def test_formations(test_client):
li_semf = notes.formsemestre_list(
formation_id=f["formation_id"],
format="json",
)
).get_data(as_text=True)
assert isinstance(li_semf, str)
load_li_semf = json.loads(li_semf)
@ -220,7 +222,7 @@ def test_formations(test_client):
assert len(load_li_semf) == 2
assert load_li_semf[1]["semestre_id"] == sem2["semestre_id"]
li_sem = notes.formsemestre_list(format="json")
li_sem = notes.formsemestre_list(format="json").get_data(as_text=True)
load_li_sem = json.loads(li_sem)
assert len(load_li_sem) == 3
@ -328,7 +330,7 @@ def test_formations(test_client):
)
sco_edit_formation.do_formation_delete(oid=f2["formation_id"])
lif3 = notes.formation_list(format="json")
lif3 = notes.formation_list(format="json").get_data(as_text=True)
assert isinstance(lif3, str)
load_lif3 = json.loads(lif3)
assert len(load_lif3) == 1
@ -371,5 +373,7 @@ def test_import_formation(test_client):
assert mi["module_id"] == mod["module_id"]
# --- Export formation en XML
doc1 = sco_formations.formation_export(formation_id, format="xml")
doc1 = sco_formations.formation_export(formation_id, format="xml").get_data(
as_text=True
)
assert isinstance(doc1, str)

View File

@ -1,4 +1,4 @@
#!/opt/zope213/bin/python
#!/opt/scodoc/venv/bin/python
# -*- coding: utf-8 -*-
# -*- mode: python -*-
@ -68,42 +68,51 @@ anonymize_null = "NULL"
ANONYMIZED_FIELDS = {
"identite.nom": anonymize_name,
"identite.prenom": anonymize_name,
"identite.nom_usuel": anonymize_null,
"identite.civilite": "'X'",
"identite.date_naissance": anonymize_date,
"identite.lieu_naissance": anonymize_question_str,
"identite.dept_naissance": anonymize_question_str,
"identite.nationalite": anonymize_question_str,
"identite.foto": anonymize_null,
"identite.statut": anonymize_null,
"identite.boursier": anonymize_null,
"identite.photo_filename": anonymize_null,
"identite.code_nip": anonymize_null,
"identite.code_ine": anonymize_null,
"identite.nom_usuel": anonymize_null,
"identite.scodoc7_id": anonymize_null,
"adresse.email": "'ano@nyme.fr'",
"adresse.emailperso": anonymize_null,
"adresse.domicile": anonymize_null,
"adresse.codepostaldomicile": anonymize_null,
"adresse.villedomicile": anonymize_null,
"adresse.paysdomicile": anonymize_null,
"adresse.telephone": anonymize_null,
"adresse.telephonemobile": anonymize_null,
"adresse.fax": anonymize_null,
"admissions.nomlycee": anonymize_name,
"billet_absence.description": anonymize_null,
"etud_annotations.comment": anonymize_name,
"entreprises.nom": anonymize_name,
"entreprises.adresse": anonymize_null,
"entreprises.ville": anonymize_null,
"entreprises.codepostal": anonymize_null,
"entreprises.pays": anonymize_null,
"entreprises.contact_origine": anonymize_null,
"entreprises.secteur": anonymize_null,
"entreprises.note": anonymize_null,
"entreprises.privee": anonymize_null,
"entreprises.localisation": anonymize_null,
"entreprise_correspondant.nom": anonymize_name,
"entreprise_correspondant.prenom": anonymize_name,
"entreprise_correspondant.phone1": anonymize_null,
"entreprise_correspondant.phone2": anonymize_null,
"entreprise_correspondant.mobile": anonymize_null,
"entreprise_correspondant.mail1": anonymize_null,
"entreprise_correspondant.mail2": anonymize_null,
"entreprise_correspondant.note": anonymize_null,
"entreprise_correspondant.fax": anonymize_null,
"entreprise_contact.description": anonymize_null,
"entreprise_contact.enseignant": anonymize_null,
# "entreprises.nom": anonymize_name,
# "entreprises.adresse": anonymize_null,
# "entreprises.ville": anonymize_null,
# "entreprises.codepostal": anonymize_null,
# "entreprises.pays": anonymize_null,
# "entreprises.contact_origine": anonymize_null,
# "entreprises.secteur": anonymize_null,
# "entreprises.note": anonymize_null,
# "entreprises.privee": anonymize_null,
# "entreprises.localisation": anonymize_null,
# "entreprise_correspondant.nom": anonymize_name,
# "entreprise_correspondant.prenom": anonymize_name,
# "entreprise_correspondant.phone1": anonymize_null,
# "entreprise_correspondant.phone2": anonymize_null,
# "entreprise_correspondant.mobile": anonymize_null,
# "entreprise_correspondant.mail1": anonymize_null,
# "entreprise_correspondant.mail2": anonymize_null,
# "entreprise_correspondant.note": anonymize_null,
# "entreprise_correspondant.fax": anonymize_null,
# "entreprise_contact.description": anonymize_null,
# "entreprise_contact.enseignant": anonymize_null,
"notes_appreciations.comment": anonymize_name,
}

View File

@ -120,7 +120,7 @@ then
echo
echo "Création des tables et du compte admin"
echo
msg="Saisir le mot de passe de l\'administrateur \(admin\):"
msg="Saisir le mot de passe de l\'administrateur \(admin, via le web\):"
su -c "(cd /opt/scodoc; source venv/bin/activate; flask db upgrade; flask sco-db-init; echo; echo $msg; flask user-password admin)" "$SCODOC_USER" || die "Erreur: sco-db-init"
echo
echo "Base initialisée et admin créé."
@ -135,6 +135,7 @@ systemctl start scodoc9
echo
echo "Service configuré et démarré."
echo "Vous pouvez vous connecter en web et vous identifier comme \"admin\"."
echo "ou bien importer vos données et comptes de la version ScoDoc 7."
echo

View File

@ -52,7 +52,7 @@ else
SN=""
fi
CMD="curl --fail --connect-timeout 5 --silent http://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/version?mode=$mode\&release=${SCODOC_RELEASE}\&sn=${SN}"
CMD="curl --fail --connect-timeout 5 --silent https://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/version?mode=$mode\&release=${SCODOC_RELEASE}\&sn=${SN}"
SVERSION="$(${CMD})"
if [ "$?" == 0 ]; then

View File

@ -4,16 +4,18 @@
# Ne touche pas aux données (/opt/scodoc-data)
# N'enlève complètement /opt/scodoc qui si --purge
systemctl stop scodoc9
systemctl disable scodoc9
systemctl stop scodoc9 || echo "scodoc9 non lancé"
systemctl disable scodoc9 || echo "scodoc9 systemd non configuré"
if [ "$#" == 1 ] && [ "$1" == "purge" ]
then
/bin/rm -rf /opt/scodoc
/bin/rm -f scodoc9.service
/bin/rm -f /etc/systemd/system/scodoc9.service
/bin/rm -f /etc/systemd/system/scodoc-updater.service
/bin/rm -f /etc/systemd/system/scodoc-updater.timer
/bin/rm -f /etc/nginx/sites-enabled/scodoc9.nginx
systemctl daemon-reload
fi
systemctl reload nginx
systemctl reload nginx || echo 'nginx non fonctionnel'

View File

@ -28,6 +28,7 @@ server {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
client_max_body_size 16m;
}
location /ScoDoc/static {
# handle static files directly, without forwarding to the application

View File

@ -211,7 +211,7 @@ def get_class_for_table(table):
def get_boolean_columns(klass):
"return list of names of boolean attributes in this model"
"return list of names of boolean attributes in this (ScoDoc 9) model"
boolean_columns = []
column_names = sqlalchemy.inspect(klass).columns.keys()
for column_name in column_names:
@ -412,7 +412,8 @@ def convert_object(
obj[k] = uid
# Converti les booléens
for k in boolean_columns:
obj[k] = bool(obj[k])
if k in obj:
obj[k] = bool(obj[k])
# Ajoute le département si besoin:
if hasattr(klass, "dept_id"):

View File

@ -83,7 +83,7 @@ su -c "(cd $SCODOC_DIR && source venv/bin/activate && pip install wheel && pip i
# ------------
SCODOC_RELEASE=$(grep SCOVERSION sco_version.py | awk '{ print substr($3, 2, length($3)-2) }')
SVERSION=$(curl --silent http://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/version?mode=install\&release="$SCODOC_RELEASE")
SVERSION=$(curl --silent https://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/version?mode=install\&release="$SCODOC_RELEASE")
echo "$SVERSION" > "${SCODOC_VERSION_DIR}/scodoc.sn"

View File

@ -65,6 +65,27 @@ else
SCODOC7_HOME="$1" # racine de l'archive importée
fi
# --- 1. Vérifie qu'aucun des départements à importer n'existe déjà
check_existing_depts() {
sco7_depts=""
for f in "${SCODOC7_HOME}/var/scodoc/"/config/depts/*.cfg
do
dept=$(basename "${f%.*}") # le nom du dept peut-être en minuscules
sco9_name=$(echo "$dept" | tr "[:lower:]" "[:upper:]") # acronym ScoDoc 9 toujours en majuscule
sco7_depts="$sco7_depts $sco9_name"
done
nb_existing=$(echo "$sco7_depts" | su -c "cd $SCODOC_DIR && source venv/bin/activate && xargs flask list-depts" "$SCODOC_USER" | wc -l)
if [ "$nb_existing" -gt 0 ]
then
echo "Attention: il existe déjà $nb_existing départements de même nom que celles"
echo "que vous souhaitez importer !"
echo "Département qui allaient être importées: $sco7_depts"
echo "=> arrêt."
exit 2
fi
}
# --- 2. Propriétaire des bases de données pour import "en place"
# Bases appartenant à www-data: les attribue à "scodoc" pour le script de migration SQL
# qui tourne en tant que "scodoc"
@ -160,6 +181,8 @@ migrate_local_files() {
# ------ MAIN
check_existing_depts
change_scodoc_file_ownership
if [ "$INPLACE" == 1 ]
@ -190,10 +213,15 @@ su -c "(cd $SCODOC_DIR && source venv/bin/activate && flask import-scodoc7-users
# ----- Migration bases départements
# les départements ScoDoc7 ont été déplacés dans /opt/scodoc-data/config/dept
# (ils ne sont d'ailleurs plus utilisés par ScoDoc 9)
# (ils ne sont plus utilisés par ScoDoc 9)
# Le nom du dept peut-être en minuscules et/ou majuscules (Geii, GEII...)
# Le nom de BD ScoDoc7 est toujours en majuscules (SCOGEII)
# Rappel: les archives ScoDoc7 étaient .../archives/<dept_name>/... donc minuscules/majuscules
# alors qu'en ScoDoc9 elles seront .../archives/<dept_id>/ : le numéro interne du département,
# puisque l'acronyme peut changer.
for f in "$SCODOC_VAR_DIR"/config/depts/*.cfg
do
dept=$(basename "${f%.*}") # le nom du dept peut-être en minuscules et/ou majuscules (geii, GEII...)
dept=$(basename "${f%.*}") # le nom du dept peut-être en minuscules
db_name=$(echo "SCO$dept" | tr "[:lower:]" "[:upper:]") # nom de BD toujours en majuscule
echo
echo "----------------------------------------------"

View File

@ -4,6 +4,7 @@
import glob
import os
import shutil
import sys
from app.models import Departement
from app.models.formsemestre import FormSemestre
@ -25,7 +26,8 @@ def migrate_scodoc7_dept_archive(dept_name=""):
migrate_docetuds(dept)
# ApoCSVArchiver:
# /opt/scodoc-data/archives/apo_csv/<dept>/ ne bouge pas
# /opt/scodoc-data/archives/apo_csv/<dept>/ -> apo_csv/<dept_id>/
migrate_apo_csv(dept)
def migrate_sem_archives(dept):
@ -35,7 +37,7 @@ def migrate_sem_archives(dept):
for sem in FormSemestre.query.filter_by(dept_id=dept.id):
n += 1
arch_dir7 = f"/opt/scodoc-data/archives/{dept.acronym}/{sem.scodoc7_id}"
arch_dir9 = f"/opt/scodoc-data/archives/{dept.acronym}/{sem.id}"
arch_dir9 = f"/opt/scodoc-data/archives/{dept.id}/{sem.id}"
if os.path.exists(arch_dir7):
n_moves += 1
if not os.path.exists(arch_dir9):
@ -58,7 +60,7 @@ def migrate_docetuds(dept):
arch_dir7 = (
f"/opt/scodoc-data/archives/docetuds/{dept.acronym}/{etud.scodoc7_id}"
)
arch_dir9 = f"/opt/scodoc-data/archives/docetuds/{dept.acronym}/{etud.id}"
arch_dir9 = f"/opt/scodoc-data/archives/docetuds/{dept.id}/{etud.id}"
if os.path.exists(arch_dir7):
n_moves += 1
if not os.path.exists(arch_dir9):
@ -70,3 +72,25 @@ def migrate_docetuds(dept):
# print(f"\tmoving {arch}")
shutil.move(arch, arch_dir9)
# print(f"moved {n_moves}/{n} etuds")
def migrate_apo_csv(dept):
"/opt/scodoc-data/archives/apo_csv/<dept>/ -> .../apo_csv/<dept_id>/"
arch_dir7 = f"/opt/scodoc-data/archives/apo_csv/{dept.acronym}"
arch_dir7_upper = f"/opt/scodoc-data/archives/apo_csv/{dept.acronym.upper()}"
arch_dir9 = f"/opt/scodoc-data/archives/apo_csv/{dept.id}"
if os.path.exists(arch_dir7):
if os.path.exists(arch_dir9):
print(
f"Warning: {arch_dir9} exist ! not moving {arch_dir7}", file=sys.stderr
)
else:
shutil.move(arch_dir7, arch_dir9)
elif os.path.exists(arch_dir7_upper):
if os.path.exists(arch_dir9):
print(
f"Warning: {arch_dir9} exist ! not moving {arch_dir7_upper}",
file=sys.stderr,
)
else:
shutil.move(arch_dir7_upper, arch_dir9)