forked from ScoDoc/ScoDoc
maj
This commit is contained in:
commit
1be2ba1498
@ -40,7 +40,7 @@ from app.scodoc.sco_permissions import Permission
|
|||||||
def sidebar_common():
|
def sidebar_common():
|
||||||
"partie commune à toutes les sidebar"
|
"partie commune à toutes les sidebar"
|
||||||
H = [
|
H = [
|
||||||
f"""<a class="scodoc_title" href="{url_for("scodoc.about", scodoc_dept=g.scodoc_dept)}">ScoDoc 9</a>
|
f"""<a class="scodoc_title" href="{url_for("scodoc.index", scodoc_dept=g.scodoc_dept)}">ScoDoc 9</a>
|
||||||
<div id="authuser"><a id="authuserlink" href="{
|
<div id="authuser"><a id="authuserlink" href="{
|
||||||
url_for("users.user_info_page",
|
url_for("users.user_info_page",
|
||||||
scodoc_dept=g.scodoc_dept, user_name=current_user.user_name)
|
scodoc_dept=g.scodoc_dept, user_name=current_user.user_name)
|
||||||
|
@ -27,9 +27,12 @@
|
|||||||
|
|
||||||
"""Various HTML generation functions
|
"""Various HTML generation functions
|
||||||
"""
|
"""
|
||||||
|
from html.parser import HTMLParser
|
||||||
|
from html.entities import name2codepoint
|
||||||
|
import re
|
||||||
|
|
||||||
from flask import g, url_for
|
from flask import g, url_for
|
||||||
|
|
||||||
import app.scodoc.sco_utils as scu
|
|
||||||
from . import listhistogram
|
from . import listhistogram
|
||||||
|
|
||||||
|
|
||||||
@ -130,3 +133,63 @@ def make_menu(title, items, css_class="", alone=False):
|
|||||||
if alone:
|
if alone:
|
||||||
H.append("</ul>")
|
H.append("</ul>")
|
||||||
return "".join(H)
|
return "".join(H)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
HTML <-> text conversions.
|
||||||
|
http://stackoverflow.com/questions/328356/extracting-text-from-html-file-using-python
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class _HTMLToText(HTMLParser):
|
||||||
|
def __init__(self):
|
||||||
|
HTMLParser.__init__(self)
|
||||||
|
self._buf = []
|
||||||
|
self.hide_output = False
|
||||||
|
|
||||||
|
def handle_starttag(self, tag, attrs):
|
||||||
|
if tag in ("p", "br") and not self.hide_output:
|
||||||
|
self._buf.append("\n")
|
||||||
|
elif tag in ("script", "style"):
|
||||||
|
self.hide_output = True
|
||||||
|
|
||||||
|
def handle_startendtag(self, tag, attrs):
|
||||||
|
if tag == "br":
|
||||||
|
self._buf.append("\n")
|
||||||
|
|
||||||
|
def handle_endtag(self, tag):
|
||||||
|
if tag == "p":
|
||||||
|
self._buf.append("\n")
|
||||||
|
elif tag in ("script", "style"):
|
||||||
|
self.hide_output = False
|
||||||
|
|
||||||
|
def handle_data(self, text):
|
||||||
|
if text and not self.hide_output:
|
||||||
|
self._buf.append(re.sub(r"\s+", " ", text))
|
||||||
|
|
||||||
|
def handle_entityref(self, name):
|
||||||
|
if name in name2codepoint and not self.hide_output:
|
||||||
|
c = chr(name2codepoint[name])
|
||||||
|
self._buf.append(c)
|
||||||
|
|
||||||
|
def handle_charref(self, name):
|
||||||
|
if not self.hide_output:
|
||||||
|
n = int(name[1:], 16) if name.startswith("x") else int(name)
|
||||||
|
self._buf.append(chr(n))
|
||||||
|
|
||||||
|
def get_text(self):
|
||||||
|
return re.sub(r" +", " ", "".join(self._buf))
|
||||||
|
|
||||||
|
|
||||||
|
def html_to_text(html):
|
||||||
|
"""
|
||||||
|
Given a piece of HTML, return the plain text it contains.
|
||||||
|
This handles entities and char refs, but not javascript and stylesheets.
|
||||||
|
"""
|
||||||
|
parser = _HTMLToText()
|
||||||
|
try:
|
||||||
|
parser.feed(html)
|
||||||
|
parser.close()
|
||||||
|
except: # HTMLParseError: No good replacement?
|
||||||
|
pass
|
||||||
|
return parser.get_text()
|
||||||
|
@ -630,7 +630,7 @@ class NotesTable(object):
|
|||||||
matiere_sum_notes += val * coef
|
matiere_sum_notes += val * coef
|
||||||
matiere_sum_coefs += coef
|
matiere_sum_coefs += coef
|
||||||
matiere_id_last = matiere_id
|
matiere_id_last = matiere_id
|
||||||
except: # val == "NI" "NA"
|
except TypeError: # val == "NI" "NA"
|
||||||
assert val == "NI" or val == "NA"
|
assert val == "NI" or val == "NA"
|
||||||
nb_missing = nb_missing + 1
|
nb_missing = nb_missing + 1
|
||||||
coefs.append(0)
|
coefs.append(0)
|
||||||
|
@ -597,6 +597,22 @@ def float_null_is_null(x):
|
|||||||
return float(x)
|
return float(x)
|
||||||
|
|
||||||
|
|
||||||
|
BOOL_STR = {
|
||||||
|
"": False,
|
||||||
|
"false": False,
|
||||||
|
"0": False,
|
||||||
|
"1": True,
|
||||||
|
"true": "true",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def bool_or_str(x):
|
||||||
|
"""a boolean, may also be encoded as a string "0", "False", "1", "True" """
|
||||||
|
if isinstance(x, str):
|
||||||
|
return BOOL_STR[x.lower()]
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
# post filtering
|
# post filtering
|
||||||
#
|
#
|
||||||
def UniqListofDicts(L, key):
|
def UniqListofDicts(L, key):
|
||||||
|
@ -30,7 +30,8 @@
|
|||||||
les dossiers d'admission et autres pièces utiles.
|
les dossiers d'admission et autres pièces utiles.
|
||||||
"""
|
"""
|
||||||
import flask
|
import flask
|
||||||
from flask import url_for, g, request
|
from flask import url_for, render_template
|
||||||
|
from flask import g, request
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
@ -328,9 +329,9 @@ def etudarchive_import_files_form(group_id):
|
|||||||
|
|
||||||
if tf[0] == 0:
|
if tf[0] == 0:
|
||||||
return "\n".join(H) + tf[1] + "</li></ol>" + F
|
return "\n".join(H) + tf[1] + "</li></ol>" + F
|
||||||
elif tf[0] == -1:
|
# retrouve le semestre à partir du groupe:
|
||||||
# retrouve le semestre à partir du groupe:
|
group = sco_groups.get_group(group_id)
|
||||||
group = sco_groups.get_group(group_id)
|
if tf[0] == -1:
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
url_for(
|
url_for(
|
||||||
"notes.formsemestre_status",
|
"notes.formsemestre_status",
|
||||||
@ -340,21 +341,41 @@ def etudarchive_import_files_form(group_id):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return etudarchive_import_files(
|
return etudarchive_import_files(
|
||||||
group_id=tf[2]["group_id"],
|
formsemestre_id=group["formsemestre_id"],
|
||||||
xlsfile=tf[2]["xlsfile"],
|
xlsfile=tf[2]["xlsfile"],
|
||||||
zipfile=tf[2]["zipfile"],
|
zipfile=tf[2]["zipfile"],
|
||||||
description=tf[2]["description"],
|
description=tf[2]["description"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def etudarchive_import_files(group_id=None, xlsfile=None, zipfile=None, description=""):
|
def etudarchive_import_files(
|
||||||
|
formsemestre_id=None, xlsfile=None, zipfile=None, description=""
|
||||||
|
):
|
||||||
|
"Importe des fichiers"
|
||||||
|
|
||||||
def callback(etud, data, filename):
|
def callback(etud, data, filename):
|
||||||
_store_etud_file_to_new_archive(etud["etudid"], data, filename, description)
|
_store_etud_file_to_new_archive(etud["etudid"], data, filename, description)
|
||||||
|
|
||||||
filename_title = "fichier_a_charger"
|
# Utilise la fontion developpée au depart pour les photos
|
||||||
page_title = "Téléchargement de fichiers associés aux étudiants"
|
(
|
||||||
# Utilise la fontion au depart developpee pour les photos
|
ignored_zipfiles,
|
||||||
r = sco_trombino.zip_excel_import_files(
|
unmatched_files,
|
||||||
xlsfile, zipfile, callback, filename_title, page_title
|
stored_etud_filename,
|
||||||
|
) = sco_trombino.zip_excel_import_files(
|
||||||
|
xlsfile=xlsfile,
|
||||||
|
zipfile=zipfile,
|
||||||
|
callback=callback,
|
||||||
|
filename_title="fichier_a_charger",
|
||||||
|
)
|
||||||
|
return render_template(
|
||||||
|
"scolar/photos_import_files.html",
|
||||||
|
page_title="Téléchargement de fichiers associés aux étudiants",
|
||||||
|
ignored_zipfiles=ignored_zipfiles,
|
||||||
|
unmatched_files=unmatched_files,
|
||||||
|
stored_etud_filename=stored_etud_filename,
|
||||||
|
next_page=url_for(
|
||||||
|
"scolar.groups_view",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formsemestre_id=formsemestre_id,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
return r + html_sco_header.sco_footer()
|
|
||||||
|
@ -190,7 +190,7 @@ def do_matiere_delete(oid):
|
|||||||
|
|
||||||
|
|
||||||
def matiere_delete(matiere_id=None):
|
def matiere_delete(matiere_id=None):
|
||||||
"""Delete an UE"""
|
"""Delete matière"""
|
||||||
from app.scodoc import sco_edit_ue
|
from app.scodoc import sco_edit_ue
|
||||||
|
|
||||||
M = matiere_list(args={"matiere_id": matiere_id})[0]
|
M = matiere_list(args={"matiere_id": matiere_id})[0]
|
||||||
@ -200,7 +200,11 @@ def matiere_delete(matiere_id=None):
|
|||||||
"<h2>Suppression de la matière %(titre)s" % M,
|
"<h2>Suppression de la matière %(titre)s" % M,
|
||||||
" dans l'UE (%(acronyme)s))</h2>" % UE,
|
" dans l'UE (%(acronyme)s))</h2>" % UE,
|
||||||
]
|
]
|
||||||
dest_url = scu.NotesURL() + "/ue_list?formation_id=" + str(UE["formation_id"])
|
dest_url = url_for(
|
||||||
|
"notes.ue_table",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formation_id=str(UE["formation_id"]),
|
||||||
|
)
|
||||||
tf = TrivialFormulator(
|
tf = TrivialFormulator(
|
||||||
request.base_url,
|
request.base_url,
|
||||||
scu.get_request_args(),
|
scu.get_request_args(),
|
||||||
@ -227,13 +231,13 @@ def matiere_edit(matiere_id=None):
|
|||||||
if not F:
|
if not F:
|
||||||
raise ScoValueError("Matière inexistante !")
|
raise ScoValueError("Matière inexistante !")
|
||||||
F = F[0]
|
F = F[0]
|
||||||
U = sco_edit_ue.ue_list(args={"ue_id": F["ue_id"]})
|
ues = sco_edit_ue.ue_list(args={"ue_id": F["ue_id"]})
|
||||||
if not F:
|
if not ues:
|
||||||
raise ScoValueError("UE inexistante !")
|
raise ScoValueError("UE inexistante !")
|
||||||
U = U[0]
|
ue = ues[0]
|
||||||
Fo = sco_formations.formation_list(args={"formation_id": U["formation_id"]})[0]
|
Fo = sco_formations.formation_list(args={"formation_id": ue["formation_id"]})[0]
|
||||||
|
|
||||||
ues = sco_edit_ue.ue_list(args={"formation_id": U["formation_id"]})
|
ues = sco_edit_ue.ue_list(args={"formation_id": ue["formation_id"]})
|
||||||
ue_names = ["%(acronyme)s (%(titre)s)" % u for u in ues]
|
ue_names = ["%(acronyme)s (%(titre)s)" % u for u in ues]
|
||||||
ue_ids = [u["ue_id"] for u in ues]
|
ue_ids = [u["ue_id"] for u in ues]
|
||||||
H = [
|
H = [
|
||||||
@ -278,8 +282,11 @@ associé.
|
|||||||
submitlabel="Modifier les valeurs",
|
submitlabel="Modifier les valeurs",
|
||||||
)
|
)
|
||||||
|
|
||||||
dest_url = scu.NotesURL() + "/ue_list?formation_id=" + str(U["formation_id"])
|
dest_url = url_for(
|
||||||
|
"notes.ue_table",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formation_id=str(ue["formation_id"]),
|
||||||
|
)
|
||||||
if tf[0] == 0:
|
if tf[0] == 0:
|
||||||
return "\n".join(H) + tf[1] + help + html_sco_header.sco_footer()
|
return "\n".join(H) + tf[1] + help + html_sco_header.sco_footer()
|
||||||
elif tf[0] == -1:
|
elif tf[0] == -1:
|
||||||
|
@ -285,21 +285,25 @@ def module_delete(module_id=None):
|
|||||||
"""Delete a module"""
|
"""Delete a module"""
|
||||||
if not module_id:
|
if not module_id:
|
||||||
raise ScoValueError("invalid module !")
|
raise ScoValueError("invalid module !")
|
||||||
Mods = module_list(args={"module_id": module_id})
|
modules = module_list(args={"module_id": module_id})
|
||||||
if not Mods:
|
if not modules:
|
||||||
raise ScoValueError("Module inexistant !")
|
raise ScoValueError("Module inexistant !")
|
||||||
Mod = Mods[0]
|
mod = modules[0]
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(page_title="Suppression d'un module"),
|
html_sco_header.sco_header(page_title="Suppression d'un module"),
|
||||||
"""<h2>Suppression du module %(titre)s (%(code)s)</h2>""" % Mod,
|
"""<h2>Suppression du module %(titre)s (%(code)s)</h2>""" % mod,
|
||||||
]
|
]
|
||||||
|
|
||||||
dest_url = scu.NotesURL() + "/ue_list?formation_id=" + str(Mod["formation_id"])
|
dest_url = url_for(
|
||||||
|
"notes.ue_table",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formation_id=str(mod["formation_id"]),
|
||||||
|
)
|
||||||
tf = TrivialFormulator(
|
tf = TrivialFormulator(
|
||||||
request.base_url,
|
request.base_url,
|
||||||
scu.get_request_args(),
|
scu.get_request_args(),
|
||||||
(("module_id", {"input_type": "hidden"}),),
|
(("module_id", {"input_type": "hidden"}),),
|
||||||
initvalues=Mod,
|
initvalues=mod,
|
||||||
submitlabel="Confirmer la suppression",
|
submitlabel="Confirmer la suppression",
|
||||||
cancelbutton="Annuler",
|
cancelbutton="Annuler",
|
||||||
)
|
)
|
||||||
@ -367,9 +371,11 @@ def module_edit(module_id=None):
|
|||||||
Mod["ue_matiere_id"] = "%s!%s" % (Mod["ue_id"], Mod["matiere_id"])
|
Mod["ue_matiere_id"] = "%s!%s" % (Mod["ue_id"], Mod["matiere_id"])
|
||||||
|
|
||||||
semestres_indices = list(range(1, parcours.NB_SEM + 1))
|
semestres_indices = list(range(1, parcours.NB_SEM + 1))
|
||||||
|
dest_url = url_for(
|
||||||
dest_url = scu.NotesURL() + "/ue_list?formation_id=" + str(Mod["formation_id"])
|
"notes.ue_table",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formation_id=str(Mod["formation_id"]),
|
||||||
|
)
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(
|
html_sco_header.sco_header(
|
||||||
page_title="Modification du module %(titre)s" % Mod,
|
page_title="Modification du module %(titre)s" % Mod,
|
||||||
@ -588,9 +594,9 @@ def formation_add_malus_modules(formation_id, titre=None, redirect=True):
|
|||||||
"""Création d'un module de "malus" dans chaque UE d'une formation"""
|
"""Création d'un module de "malus" dans chaque UE d'une formation"""
|
||||||
from app.scodoc import sco_edit_ue
|
from app.scodoc import sco_edit_ue
|
||||||
|
|
||||||
ue_list = sco_edit_ue.ue_list(args={"formation_id": formation_id})
|
ues = sco_edit_ue.ue_list(args={"formation_id": formation_id})
|
||||||
|
|
||||||
for ue in ue_list:
|
for ue in ues:
|
||||||
# Un seul module de malus par UE:
|
# Un seul module de malus par UE:
|
||||||
nb_mod_malus = len(
|
nb_mod_malus = len(
|
||||||
[
|
[
|
||||||
@ -603,7 +609,11 @@ def formation_add_malus_modules(formation_id, titre=None, redirect=True):
|
|||||||
ue_add_malus_module(ue["ue_id"], titre=titre)
|
ue_add_malus_module(ue["ue_id"], titre=titre)
|
||||||
|
|
||||||
if redirect:
|
if redirect:
|
||||||
return flask.redirect("ue_list?formation_id=" + str(formation_id))
|
return flask.redirect(
|
||||||
|
url_for(
|
||||||
|
"notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def ue_add_malus_module(ue_id, titre=None, code=None):
|
def ue_add_malus_module(ue_id, titre=None, code=None):
|
||||||
|
@ -75,7 +75,7 @@ _ueEditor = ndb.EditableTable(
|
|||||||
sortkey="numero",
|
sortkey="numero",
|
||||||
input_formators={
|
input_formators={
|
||||||
"type": ndb.int_null_is_zero,
|
"type": ndb.int_null_is_zero,
|
||||||
"is_external": bool,
|
"is_external": ndb.bool_or_str,
|
||||||
},
|
},
|
||||||
output_formators={
|
output_formators={
|
||||||
"numero": ndb.int_null_is_zero,
|
"numero": ndb.int_null_is_zero,
|
||||||
@ -139,7 +139,11 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
|
|||||||
% (len(validations), ue["acronyme"], ue["titre"]),
|
% (len(validations), ue["acronyme"], ue["titre"]),
|
||||||
dest_url="",
|
dest_url="",
|
||||||
target_variable="delete_validations",
|
target_variable="delete_validations",
|
||||||
cancel_url="ue_list?formation_id=%s" % ue["formation_id"],
|
cancel_url=url_for(
|
||||||
|
"notes.ue_table",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formation_id=str(ue["formation_id"]),
|
||||||
|
),
|
||||||
parameters={"ue_id": ue_id, "dialog_confirmed": 1},
|
parameters={"ue_id": ue_id, "dialog_confirmed": 1},
|
||||||
)
|
)
|
||||||
if delete_validations:
|
if delete_validations:
|
||||||
@ -294,6 +298,14 @@ def ue_edit(ue_id=None, create=False, formation_id=None):
|
|||||||
"explanation": "(optionnel) code élément pédagogique Apogée ou liste de codes ELP séparés par des virgules",
|
"explanation": "(optionnel) code élément pédagogique Apogée ou liste de codes ELP séparés par des virgules",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"is_external",
|
||||||
|
{
|
||||||
|
"input_type": "boolcheckbox",
|
||||||
|
"title": "UE externe",
|
||||||
|
"explanation": "réservé pour les capitalisations d'UE effectuées à l'extérieur de l'établissement",
|
||||||
|
},
|
||||||
|
),
|
||||||
]
|
]
|
||||||
if parcours.UE_IS_MODULE:
|
if parcours.UE_IS_MODULE:
|
||||||
# demande le semestre pour creer le module immediatement:
|
# demande le semestre pour creer le module immediatement:
|
||||||
@ -374,12 +386,12 @@ def ue_edit(ue_id=None, create=False, formation_id=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _add_ue_semestre_id(ue_list):
|
def _add_ue_semestre_id(ues):
|
||||||
"""ajoute semestre_id dans les ue, en regardant le premier module de chacune.
|
"""ajoute semestre_id dans les ue, en regardant le premier module de chacune.
|
||||||
Les UE sans modules se voient attribuer le numero UE_SEM_DEFAULT (1000000),
|
Les UE sans modules se voient attribuer le numero UE_SEM_DEFAULT (1000000),
|
||||||
qui les place à la fin de la liste.
|
qui les place à la fin de la liste.
|
||||||
"""
|
"""
|
||||||
for ue in ue_list:
|
for ue in ues:
|
||||||
Modlist = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]})
|
Modlist = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]})
|
||||||
if Modlist:
|
if Modlist:
|
||||||
ue["semestre_id"] = Modlist[0]["semestre_id"]
|
ue["semestre_id"] = Modlist[0]["semestre_id"]
|
||||||
@ -391,34 +403,38 @@ def next_ue_numero(formation_id, semestre_id=None):
|
|||||||
"""Numero d'une nouvelle UE dans cette formation.
|
"""Numero d'une nouvelle UE dans cette formation.
|
||||||
Si le semestre est specifie, cherche les UE ayant des modules de ce semestre
|
Si le semestre est specifie, cherche les UE ayant des modules de ce semestre
|
||||||
"""
|
"""
|
||||||
ue_list = ue_list(args={"formation_id": formation_id})
|
ues = ue_list(args={"formation_id": formation_id})
|
||||||
if not ue_list:
|
if not ues:
|
||||||
return 0
|
return 0
|
||||||
if semestre_id is None:
|
if semestre_id is None:
|
||||||
return ue_list[-1]["numero"] + 1000
|
return ues[-1]["numero"] + 1000
|
||||||
else:
|
else:
|
||||||
# Avec semestre: (prend le semestre du 1er module de l'UE)
|
# Avec semestre: (prend le semestre du 1er module de l'UE)
|
||||||
_add_ue_semestre_id(ue_list)
|
_add_ue_semestre_id(ues)
|
||||||
ue_list_semestre = [ue for ue in ue_list if ue["semestre_id"] == semestre_id]
|
ue_list_semestre = [ue for ue in ues if ue["semestre_id"] == semestre_id]
|
||||||
if ue_list_semestre:
|
if ue_list_semestre:
|
||||||
return ue_list_semestre[-1]["numero"] + 10
|
return ue_list_semestre[-1]["numero"] + 10
|
||||||
else:
|
else:
|
||||||
return ue_list[-1]["numero"] + 1000
|
return ues[-1]["numero"] + 1000
|
||||||
|
|
||||||
|
|
||||||
def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False):
|
def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False):
|
||||||
"""Delete an UE"""
|
"""Delete an UE"""
|
||||||
ue = ue_list(args={"ue_id": ue_id})
|
ues = ue_list(args={"ue_id": ue_id})
|
||||||
if not ue:
|
if not ues:
|
||||||
raise ScoValueError("UE inexistante !")
|
raise ScoValueError("UE inexistante !")
|
||||||
ue = ue[0]
|
ue = ues[0]
|
||||||
|
|
||||||
if not dialog_confirmed:
|
if not dialog_confirmed:
|
||||||
return scu.confirm_dialog(
|
return scu.confirm_dialog(
|
||||||
"<h2>Suppression de l'UE %(titre)s (%(acronyme)s))</h2>" % ue,
|
"<h2>Suppression de l'UE %(titre)s (%(acronyme)s))</h2>" % ue,
|
||||||
dest_url="",
|
dest_url="",
|
||||||
parameters={"ue_id": ue_id},
|
parameters={"ue_id": ue_id},
|
||||||
cancel_url="ue_list?formation_id=%s" % ue["formation_id"],
|
cancel_url=url_for(
|
||||||
|
"notes.ue_table",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formation_id=str(ue["formation_id"]),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
return do_ue_delete(ue_id, delete_validations=delete_validations)
|
return do_ue_delete(ue_id, delete_validations=delete_validations)
|
||||||
@ -438,21 +454,24 @@ def ue_table(formation_id=None, msg=""): # was ue_list
|
|||||||
parcours = sco_codes_parcours.get_parcours_from_code(F["type_parcours"])
|
parcours = sco_codes_parcours.get_parcours_from_code(F["type_parcours"])
|
||||||
locked = sco_formations.formation_has_locked_sems(formation_id)
|
locked = sco_formations.formation_has_locked_sems(formation_id)
|
||||||
|
|
||||||
ue_list = ue_list(args={"formation_id": formation_id})
|
ues = ue_list(args={"formation_id": formation_id, "is_external": False})
|
||||||
|
ues_externes = ue_list(args={"formation_id": formation_id, "is_external": True})
|
||||||
# tri par semestre et numero:
|
# tri par semestre et numero:
|
||||||
_add_ue_semestre_id(ue_list)
|
_add_ue_semestre_id(ues)
|
||||||
ue_list.sort(key=lambda u: (u["semestre_id"], u["numero"]))
|
_add_ue_semestre_id(ues_externes)
|
||||||
has_duplicate_ue_codes = len(set([ue["ue_code"] for ue in ue_list])) != len(ue_list)
|
ues.sort(key=lambda u: (u["semestre_id"], u["numero"]))
|
||||||
|
ues_externes.sort(key=lambda u: (u["semestre_id"], u["numero"]))
|
||||||
|
has_duplicate_ue_codes = len(set([ue["ue_code"] for ue in ues])) != len(ues)
|
||||||
|
|
||||||
perm_change = current_user.has_permission(Permission.ScoChangeFormation)
|
has_perm_change = current_user.has_permission(Permission.ScoChangeFormation)
|
||||||
# editable = (not locked) and perm_change
|
# editable = (not locked) and has_perm_change
|
||||||
# On autorise maintanant la modification des formations qui ont des semestres verrouillés,
|
# On autorise maintanant la modification des formations qui ont des semestres verrouillés,
|
||||||
# sauf si cela affect les notes passées (verrouillées):
|
# sauf si cela affect les notes passées (verrouillées):
|
||||||
# - pas de modif des modules utilisés dans des semestres verrouillés
|
# - pas de modif des modules utilisés dans des semestres verrouillés
|
||||||
# - pas de changement des codes d'UE utilisés dans des semestres verrouillés
|
# - pas de changement des codes d'UE utilisés dans des semestres verrouillés
|
||||||
editable = perm_change
|
editable = has_perm_change
|
||||||
tag_editable = (
|
tag_editable = (
|
||||||
current_user.has_permission(Permission.ScoEditFormationTags) or perm_change
|
current_user.has_permission(Permission.ScoEditFormationTags) or has_perm_change
|
||||||
)
|
)
|
||||||
if locked:
|
if locked:
|
||||||
lockicon = scu.icontag("lock32_img", title="verrouillé")
|
lockicon = scu.icontag("lock32_img", title="verrouillé")
|
||||||
@ -556,213 +575,20 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
|
|||||||
H.append(
|
H.append(
|
||||||
'<form><input type="checkbox" class="sco_tag_checkbox">montrer les tags</input></form>'
|
'<form><input type="checkbox" class="sco_tag_checkbox">montrer les tags</input></form>'
|
||||||
)
|
)
|
||||||
|
H.append(
|
||||||
cur_ue_semestre_id = None
|
_ue_table_ues(
|
||||||
iue = 0
|
parcours,
|
||||||
for UE in ue_list:
|
ues,
|
||||||
if UE["ects"]:
|
editable,
|
||||||
UE["ects_str"] = ", %g ECTS" % UE["ects"]
|
tag_editable,
|
||||||
else:
|
has_perm_change,
|
||||||
UE["ects_str"] = ""
|
arrow_up,
|
||||||
if editable:
|
arrow_down,
|
||||||
klass = "span_apo_edit"
|
arrow_none,
|
||||||
else:
|
delete_icon,
|
||||||
klass = ""
|
delete_disabled_icon,
|
||||||
UE["code_apogee_str"] = (
|
|
||||||
""", Apo: <span class="%s" data-url="edit_ue_set_code_apogee" id="%s" data-placeholder="%s">"""
|
|
||||||
% (klass, UE["ue_id"], scu.APO_MISSING_CODE_STR)
|
|
||||||
+ (UE["code_apogee"] or "")
|
|
||||||
+ "</span>"
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
if cur_ue_semestre_id != UE["semestre_id"]:
|
|
||||||
cur_ue_semestre_id = UE["semestre_id"]
|
|
||||||
if iue > 0:
|
|
||||||
H.append("</ul>")
|
|
||||||
if UE["semestre_id"] == sco_codes_parcours.UE_SEM_DEFAULT:
|
|
||||||
lab = "Pas d'indication de semestre:"
|
|
||||||
else:
|
|
||||||
lab = "Semestre %s:" % UE["semestre_id"]
|
|
||||||
H.append('<div class="ue_list_tit_sem">%s</div>' % lab)
|
|
||||||
H.append('<ul class="notes_ue_list">')
|
|
||||||
H.append('<li class="notes_ue_list">')
|
|
||||||
if iue != 0 and editable:
|
|
||||||
H.append(
|
|
||||||
'<a href="ue_move?ue_id=%s&after=0" class="aud">%s</a>'
|
|
||||||
% (UE["ue_id"], arrow_up)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
H.append(arrow_none)
|
|
||||||
if iue < len(ue_list) - 1 and editable:
|
|
||||||
H.append(
|
|
||||||
'<a href="ue_move?ue_id=%s&after=1" class="aud">%s</a>'
|
|
||||||
% (UE["ue_id"], arrow_down)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
H.append(arrow_none)
|
|
||||||
iue += 1
|
|
||||||
UE["acro_titre"] = str(UE["acronyme"])
|
|
||||||
if UE["titre"] != UE["acronyme"]:
|
|
||||||
UE["acro_titre"] += " " + str(UE["titre"])
|
|
||||||
H.append(
|
|
||||||
"""%(acro_titre)s <span class="ue_code">(code %(ue_code)s%(ects_str)s, coef. %(coefficient)3.2f%(code_apogee_str)s)</span>
|
|
||||||
<span class="ue_coef"></span>
|
|
||||||
"""
|
|
||||||
% UE
|
|
||||||
)
|
|
||||||
|
|
||||||
if UE["type"] != sco_codes_parcours.UE_STANDARD:
|
|
||||||
H.append(
|
|
||||||
'<span class="ue_type">%s</span>'
|
|
||||||
% sco_codes_parcours.UE_TYPE_NAME[UE["type"]]
|
|
||||||
)
|
|
||||||
ue_editable = editable and not ue_is_locked(UE["ue_id"])
|
|
||||||
if ue_editable:
|
|
||||||
H.append(
|
|
||||||
'<a class="stdlink" href="ue_edit?ue_id=%(ue_id)s">modifier</a>' % UE
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
H.append('<span class="locked">[verrouillé]</span>')
|
|
||||||
if not parcours.UE_IS_MODULE:
|
|
||||||
H.append('<ul class="notes_matiere_list">')
|
|
||||||
Matlist = sco_edit_matiere.matiere_list(args={"ue_id": UE["ue_id"]})
|
|
||||||
for Mat in Matlist:
|
|
||||||
if not parcours.UE_IS_MODULE:
|
|
||||||
H.append('<li class="notes_matiere_list">')
|
|
||||||
if editable and not sco_edit_matiere.matiere_is_locked(
|
|
||||||
Mat["matiere_id"]
|
|
||||||
):
|
|
||||||
H.append(
|
|
||||||
f"""<a class="stdlink" href="{
|
|
||||||
url_for("notes.matiere_edit",
|
|
||||||
scodoc_dept=g.scodoc_dept, matiere_id=Mat["matiere_id"])
|
|
||||||
}">
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
H.append("%(titre)s" % Mat)
|
|
||||||
if editable and not sco_edit_matiere.matiere_is_locked(
|
|
||||||
Mat["matiere_id"]
|
|
||||||
):
|
|
||||||
H.append("</a>")
|
|
||||||
|
|
||||||
H.append('<ul class="notes_module_list">')
|
|
||||||
Modlist = sco_edit_module.module_list(
|
|
||||||
args={"matiere_id": Mat["matiere_id"]}
|
|
||||||
)
|
|
||||||
im = 0
|
|
||||||
for Mod in Modlist:
|
|
||||||
Mod["nb_moduleimpls"] = sco_edit_module.module_count_moduleimpls(
|
|
||||||
Mod["module_id"]
|
|
||||||
)
|
|
||||||
klass = "notes_module_list"
|
|
||||||
if Mod["module_type"] == scu.MODULE_MALUS:
|
|
||||||
klass += " module_malus"
|
|
||||||
H.append('<li class="%s">' % klass)
|
|
||||||
|
|
||||||
H.append('<span class="notes_module_list_buts">')
|
|
||||||
if im != 0 and editable:
|
|
||||||
H.append(
|
|
||||||
'<a href="module_move?module_id=%s&after=0" class="aud">%s</a>'
|
|
||||||
% (Mod["module_id"], arrow_up)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
H.append(arrow_none)
|
|
||||||
if im < len(Modlist) - 1 and editable:
|
|
||||||
H.append(
|
|
||||||
'<a href="module_move?module_id=%s&after=1" class="aud">%s</a>'
|
|
||||||
% (Mod["module_id"], arrow_down)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
H.append(arrow_none)
|
|
||||||
im += 1
|
|
||||||
if Mod["nb_moduleimpls"] == 0 and editable:
|
|
||||||
H.append(
|
|
||||||
'<a class="smallbutton" href="module_delete?module_id=%s">%s</a>'
|
|
||||||
% (Mod["module_id"], delete_icon)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
H.append(delete_disabled_icon)
|
|
||||||
H.append("</span>")
|
|
||||||
|
|
||||||
mod_editable = editable # and not sco_edit_module.module_is_locked( Mod['module_id'])
|
|
||||||
if mod_editable:
|
|
||||||
H.append(
|
|
||||||
'<a class="discretelink" title="Modifier le module numéro %(numero)s, utilisé par %(nb_moduleimpls)d sessions" href="module_edit?module_id=%(module_id)s">'
|
|
||||||
% Mod
|
|
||||||
)
|
|
||||||
H.append(
|
|
||||||
'<span class="formation_module_tit">%s</span>'
|
|
||||||
% scu.join_words(Mod["code"], Mod["titre"])
|
|
||||||
)
|
|
||||||
if mod_editable:
|
|
||||||
H.append("</a>")
|
|
||||||
heurescoef = (
|
|
||||||
"%(heures_cours)s/%(heures_td)s/%(heures_tp)s, coef. %(coefficient)s"
|
|
||||||
% Mod
|
|
||||||
)
|
|
||||||
if mod_editable:
|
|
||||||
klass = "span_apo_edit"
|
|
||||||
else:
|
|
||||||
klass = ""
|
|
||||||
heurescoef += (
|
|
||||||
', Apo: <span class="%s" data-url="edit_module_set_code_apogee" id="%s" data-placeholder="%s">'
|
|
||||||
% (klass, Mod["module_id"], scu.APO_MISSING_CODE_STR)
|
|
||||||
+ (Mod["code_apogee"] or "")
|
|
||||||
+ "</span>"
|
|
||||||
)
|
|
||||||
if tag_editable:
|
|
||||||
tag_cls = "module_tag_editor"
|
|
||||||
else:
|
|
||||||
tag_cls = "module_tag_editor_ro"
|
|
||||||
tag_mk = """<span class="sco_tag_edit"><form><textarea data-module_id="{}" class="{}">{}</textarea></form></span>"""
|
|
||||||
tag_edit = tag_mk.format(
|
|
||||||
Mod["module_id"],
|
|
||||||
tag_cls,
|
|
||||||
",".join(sco_tag_module.module_tag_list(Mod["module_id"])),
|
|
||||||
)
|
|
||||||
H.append(
|
|
||||||
" %s %s" % (parcours.SESSION_NAME, Mod["semestre_id"])
|
|
||||||
+ " (%s)" % heurescoef
|
|
||||||
+ tag_edit
|
|
||||||
)
|
|
||||||
H.append("</li>")
|
|
||||||
if not Modlist:
|
|
||||||
H.append("<li>Aucun module dans cette matière !")
|
|
||||||
if editable:
|
|
||||||
H.append(
|
|
||||||
f"""<a class="stdlink" href="{
|
|
||||||
url_for("notes.matiere_delete",
|
|
||||||
scodoc_dept=g.scodoc_dept, matiere_id=Mat["matiere_id"])}"
|
|
||||||
>supprimer cette matière</a>
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
H.append("</li>")
|
|
||||||
if editable: # and ((not parcours.UE_IS_MODULE) or len(Modlist) == 0):
|
|
||||||
H.append(
|
|
||||||
f"""<li> <a class="stdlink" href="{
|
|
||||||
url_for("notes.module_create",
|
|
||||||
scodoc_dept=g.scodoc_dept, matiere_id=Mat["matiere_id"])}"
|
|
||||||
>créer un module</a></li>
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
H.append("</ul>")
|
|
||||||
H.append("</li>")
|
|
||||||
if not Matlist:
|
|
||||||
H.append("<li>Aucune matière dans cette UE ! ")
|
|
||||||
if editable:
|
|
||||||
H.append(
|
|
||||||
"""<a class="stdlink" href="ue_delete?ue_id=%(ue_id)s">supprimer l'UE</a>"""
|
|
||||||
% UE
|
|
||||||
)
|
|
||||||
H.append("</li>")
|
|
||||||
if editable and not parcours.UE_IS_MODULE:
|
|
||||||
H.append(
|
|
||||||
'<li><a class="stdlink" href="matiere_create?ue_id=%(ue_id)s">créer une matière</a> </li>'
|
|
||||||
% UE
|
|
||||||
)
|
|
||||||
if not parcours.UE_IS_MODULE:
|
|
||||||
H.append("</ul>")
|
|
||||||
H.append("</ul>")
|
|
||||||
if editable:
|
if editable:
|
||||||
H.append(
|
H.append(
|
||||||
'<ul><li><a class="stdlink" href="ue_create?formation_id=%s">Ajouter une UE</a></li>'
|
'<ul><li><a class="stdlink" href="ue_create?formation_id=%s">Ajouter une UE</a></li>'
|
||||||
@ -774,6 +600,27 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
|
|||||||
)
|
)
|
||||||
H.append("</div>") # formation_ue_list
|
H.append("</div>") # formation_ue_list
|
||||||
|
|
||||||
|
if ues_externes:
|
||||||
|
H.append('<div class="formation_ue_list formation_ue_list_externes">')
|
||||||
|
H.append(
|
||||||
|
'<div class="ue_list_tit">UE externes déclarées (pour information):</div>'
|
||||||
|
)
|
||||||
|
H.append(
|
||||||
|
_ue_table_ues(
|
||||||
|
parcours,
|
||||||
|
ues_externes,
|
||||||
|
editable,
|
||||||
|
tag_editable,
|
||||||
|
has_perm_change,
|
||||||
|
arrow_up,
|
||||||
|
arrow_down,
|
||||||
|
arrow_none,
|
||||||
|
delete_icon,
|
||||||
|
delete_disabled_icon,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
H.append("</div>") # formation_ue_list
|
||||||
|
|
||||||
H.append("<p><ul>")
|
H.append("<p><ul>")
|
||||||
if editable:
|
if editable:
|
||||||
H.append(
|
H.append(
|
||||||
@ -795,7 +642,7 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
|
|||||||
</p>"""
|
</p>"""
|
||||||
% F
|
% F
|
||||||
)
|
)
|
||||||
if perm_change:
|
if has_perm_change:
|
||||||
H.append(
|
H.append(
|
||||||
"""
|
"""
|
||||||
<h3> <a name="sems">Semestres ou sessions de cette formation</a></h3>
|
<h3> <a name="sems">Semestres ou sessions de cette formation</a></h3>
|
||||||
@ -836,6 +683,294 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
|
|||||||
return "".join(H)
|
return "".join(H)
|
||||||
|
|
||||||
|
|
||||||
|
def _ue_table_ues(
|
||||||
|
parcours,
|
||||||
|
ues,
|
||||||
|
editable,
|
||||||
|
tag_editable,
|
||||||
|
has_perm_change,
|
||||||
|
arrow_up,
|
||||||
|
arrow_down,
|
||||||
|
arrow_none,
|
||||||
|
delete_icon,
|
||||||
|
delete_disabled_icon,
|
||||||
|
):
|
||||||
|
"""Édition de programme: liste des UEs (avec leurs matières et modules)."""
|
||||||
|
H = []
|
||||||
|
cur_ue_semestre_id = None
|
||||||
|
iue = 0
|
||||||
|
for ue in ues:
|
||||||
|
if ue["ects"]:
|
||||||
|
ue["ects_str"] = ", %g ECTS" % ue["ects"]
|
||||||
|
else:
|
||||||
|
ue["ects_str"] = ""
|
||||||
|
if editable:
|
||||||
|
klass = "span_apo_edit"
|
||||||
|
else:
|
||||||
|
klass = ""
|
||||||
|
ue["code_apogee_str"] = (
|
||||||
|
""", Apo: <span class="%s" data-url="edit_ue_set_code_apogee" id="%s" data-placeholder="%s">"""
|
||||||
|
% (klass, ue["ue_id"], scu.APO_MISSING_CODE_STR)
|
||||||
|
+ (ue["code_apogee"] or "")
|
||||||
|
+ "</span>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if cur_ue_semestre_id != ue["semestre_id"]:
|
||||||
|
cur_ue_semestre_id = ue["semestre_id"]
|
||||||
|
if iue > 0:
|
||||||
|
H.append("</ul>")
|
||||||
|
if ue["semestre_id"] == sco_codes_parcours.UE_SEM_DEFAULT:
|
||||||
|
lab = "Pas d'indication de semestre:"
|
||||||
|
else:
|
||||||
|
lab = "Semestre %s:" % ue["semestre_id"]
|
||||||
|
H.append('<div class="ue_list_tit_sem">%s</div>' % lab)
|
||||||
|
H.append('<ul class="notes_ue_list">')
|
||||||
|
H.append('<li class="notes_ue_list">')
|
||||||
|
if iue != 0 and editable:
|
||||||
|
H.append(
|
||||||
|
'<a href="ue_move?ue_id=%s&after=0" class="aud">%s</a>'
|
||||||
|
% (ue["ue_id"], arrow_up)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
H.append(arrow_none)
|
||||||
|
if iue < len(ues) - 1 and editable:
|
||||||
|
H.append(
|
||||||
|
'<a href="ue_move?ue_id=%s&after=1" class="aud">%s</a>'
|
||||||
|
% (ue["ue_id"], arrow_down)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
H.append(arrow_none)
|
||||||
|
iue += 1
|
||||||
|
ue["acro_titre"] = str(ue["acronyme"])
|
||||||
|
if ue["titre"] != ue["acronyme"]:
|
||||||
|
ue["acro_titre"] += " " + str(ue["titre"])
|
||||||
|
H.append(
|
||||||
|
"""%(acro_titre)s <span class="ue_code">(code %(ue_code)s%(ects_str)s, coef. %(coefficient)3.2f%(code_apogee_str)s)</span>
|
||||||
|
<span class="ue_coef"></span>
|
||||||
|
"""
|
||||||
|
% ue
|
||||||
|
)
|
||||||
|
if ue["type"] != sco_codes_parcours.UE_STANDARD:
|
||||||
|
H.append(
|
||||||
|
'<span class="ue_type">%s</span>'
|
||||||
|
% sco_codes_parcours.UE_TYPE_NAME[ue["type"]]
|
||||||
|
)
|
||||||
|
if ue["is_external"]:
|
||||||
|
# Cas spécial: si l'UE externe a plus d'un module, c'est peut être une UE
|
||||||
|
# qui a été déclarée externe par erreur (ou suite à un bug d'import/export xml)
|
||||||
|
# Dans ce cas, propose de changer le type (même si verrouillée)
|
||||||
|
if len(sco_moduleimpl.moduleimpls_in_external_ue(ue["ue_id"])) > 1:
|
||||||
|
H.append('<span class="ue_is_external">')
|
||||||
|
if has_perm_change:
|
||||||
|
H.append(
|
||||||
|
f"""<a class="stdlink" href="{
|
||||||
|
url_for("notes.ue_set_internal", scodoc_dept=g.scodoc_dept, ue_id=ue["ue_id"])
|
||||||
|
}">transformer en UE ordinaire</a> """
|
||||||
|
)
|
||||||
|
H.append("</span>")
|
||||||
|
ue_editable = editable and not ue_is_locked(ue["ue_id"])
|
||||||
|
if ue_editable:
|
||||||
|
H.append(
|
||||||
|
'<a class="stdlink" href="ue_edit?ue_id=%(ue_id)s">modifier</a>' % ue
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
H.append('<span class="locked">[verrouillé]</span>')
|
||||||
|
H.append(
|
||||||
|
_ue_table_matieres(
|
||||||
|
parcours,
|
||||||
|
ue,
|
||||||
|
editable,
|
||||||
|
tag_editable,
|
||||||
|
arrow_up,
|
||||||
|
arrow_down,
|
||||||
|
arrow_none,
|
||||||
|
delete_icon,
|
||||||
|
delete_disabled_icon,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return "\n".join(H)
|
||||||
|
|
||||||
|
|
||||||
|
def _ue_table_matieres(
|
||||||
|
parcours,
|
||||||
|
ue,
|
||||||
|
editable,
|
||||||
|
tag_editable,
|
||||||
|
arrow_up,
|
||||||
|
arrow_down,
|
||||||
|
arrow_none,
|
||||||
|
delete_icon,
|
||||||
|
delete_disabled_icon,
|
||||||
|
):
|
||||||
|
"""Édition de programme: liste des matières (et leurs modules) d'une UE."""
|
||||||
|
H = []
|
||||||
|
if not parcours.UE_IS_MODULE:
|
||||||
|
H.append('<ul class="notes_matiere_list">')
|
||||||
|
matieres = sco_edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]})
|
||||||
|
for mat in matieres:
|
||||||
|
if not parcours.UE_IS_MODULE:
|
||||||
|
H.append('<li class="notes_matiere_list">')
|
||||||
|
if editable and not sco_edit_matiere.matiere_is_locked(mat["matiere_id"]):
|
||||||
|
H.append(
|
||||||
|
f"""<a class="stdlink" href="{
|
||||||
|
url_for("notes.matiere_edit",
|
||||||
|
scodoc_dept=g.scodoc_dept, matiere_id=mat["matiere_id"])
|
||||||
|
}">
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
H.append("%(titre)s" % mat)
|
||||||
|
if editable and not sco_edit_matiere.matiere_is_locked(mat["matiere_id"]):
|
||||||
|
H.append("</a>")
|
||||||
|
|
||||||
|
modules = sco_edit_module.module_list(args={"matiere_id": mat["matiere_id"]})
|
||||||
|
H.append(
|
||||||
|
_ue_table_modules(
|
||||||
|
parcours,
|
||||||
|
mat,
|
||||||
|
modules,
|
||||||
|
editable,
|
||||||
|
tag_editable,
|
||||||
|
arrow_up,
|
||||||
|
arrow_down,
|
||||||
|
arrow_none,
|
||||||
|
delete_icon,
|
||||||
|
delete_disabled_icon,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not matieres:
|
||||||
|
H.append("<li>Aucune matière dans cette UE ! ")
|
||||||
|
if editable:
|
||||||
|
H.append(
|
||||||
|
"""<a class="stdlink" href="ue_delete?ue_id=%(ue_id)s">supprimer l'UE</a>"""
|
||||||
|
% ue
|
||||||
|
)
|
||||||
|
H.append("</li>")
|
||||||
|
if editable and not parcours.UE_IS_MODULE:
|
||||||
|
H.append(
|
||||||
|
'<li><a class="stdlink" href="matiere_create?ue_id=%(ue_id)s">créer une matière</a> </li>'
|
||||||
|
% ue
|
||||||
|
)
|
||||||
|
if not parcours.UE_IS_MODULE:
|
||||||
|
H.append("</ul>")
|
||||||
|
return "\n".join(H)
|
||||||
|
|
||||||
|
|
||||||
|
def _ue_table_modules(
|
||||||
|
parcours,
|
||||||
|
mat,
|
||||||
|
modules,
|
||||||
|
editable,
|
||||||
|
tag_editable,
|
||||||
|
arrow_up,
|
||||||
|
arrow_down,
|
||||||
|
arrow_none,
|
||||||
|
delete_icon,
|
||||||
|
delete_disabled_icon,
|
||||||
|
):
|
||||||
|
"""Édition de programme: liste des modules d'une matière d'une UE"""
|
||||||
|
H = ['<ul class="notes_module_list">']
|
||||||
|
im = 0
|
||||||
|
for mod in modules:
|
||||||
|
mod["nb_moduleimpls"] = sco_edit_module.module_count_moduleimpls(
|
||||||
|
mod["module_id"]
|
||||||
|
)
|
||||||
|
klass = "notes_module_list"
|
||||||
|
if mod["module_type"] == scu.MODULE_MALUS:
|
||||||
|
klass += " module_malus"
|
||||||
|
H.append('<li class="%s">' % klass)
|
||||||
|
|
||||||
|
H.append('<span class="notes_module_list_buts">')
|
||||||
|
if im != 0 and editable:
|
||||||
|
H.append(
|
||||||
|
'<a href="module_move?module_id=%s&after=0" class="aud">%s</a>'
|
||||||
|
% (mod["module_id"], arrow_up)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
H.append(arrow_none)
|
||||||
|
if im < len(modules) - 1 and editable:
|
||||||
|
H.append(
|
||||||
|
'<a href="module_move?module_id=%s&after=1" class="aud">%s</a>'
|
||||||
|
% (mod["module_id"], arrow_down)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
H.append(arrow_none)
|
||||||
|
im += 1
|
||||||
|
if mod["nb_moduleimpls"] == 0 and editable:
|
||||||
|
H.append(
|
||||||
|
'<a class="smallbutton" href="module_delete?module_id=%s">%s</a>'
|
||||||
|
% (mod["module_id"], delete_icon)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
H.append(delete_disabled_icon)
|
||||||
|
H.append("</span>")
|
||||||
|
|
||||||
|
mod_editable = (
|
||||||
|
editable # and not sco_edit_module.module_is_locked( Mod['module_id'])
|
||||||
|
)
|
||||||
|
if mod_editable:
|
||||||
|
H.append(
|
||||||
|
'<a class="discretelink" title="Modifier le module numéro %(numero)s, utilisé par %(nb_moduleimpls)d sessions" href="module_edit?module_id=%(module_id)s">'
|
||||||
|
% mod
|
||||||
|
)
|
||||||
|
H.append(
|
||||||
|
'<span class="formation_module_tit">%s</span>'
|
||||||
|
% scu.join_words(mod["code"], mod["titre"])
|
||||||
|
)
|
||||||
|
if mod_editable:
|
||||||
|
H.append("</a>")
|
||||||
|
heurescoef = (
|
||||||
|
"%(heures_cours)s/%(heures_td)s/%(heures_tp)s, coef. %(coefficient)s" % mod
|
||||||
|
)
|
||||||
|
if mod_editable:
|
||||||
|
klass = "span_apo_edit"
|
||||||
|
else:
|
||||||
|
klass = ""
|
||||||
|
heurescoef += (
|
||||||
|
', Apo: <span class="%s" data-url="edit_module_set_code_apogee" id="%s" data-placeholder="%s">'
|
||||||
|
% (klass, mod["module_id"], scu.APO_MISSING_CODE_STR)
|
||||||
|
+ (mod["code_apogee"] or "")
|
||||||
|
+ "</span>"
|
||||||
|
)
|
||||||
|
if tag_editable:
|
||||||
|
tag_cls = "module_tag_editor"
|
||||||
|
else:
|
||||||
|
tag_cls = "module_tag_editor_ro"
|
||||||
|
tag_mk = """<span class="sco_tag_edit"><form><textarea data-module_id="{}" class="{}">{}</textarea></form></span>"""
|
||||||
|
tag_edit = tag_mk.format(
|
||||||
|
mod["module_id"],
|
||||||
|
tag_cls,
|
||||||
|
",".join(sco_tag_module.module_tag_list(mod["module_id"])),
|
||||||
|
)
|
||||||
|
H.append(
|
||||||
|
" %s %s" % (parcours.SESSION_NAME, mod["semestre_id"])
|
||||||
|
+ " (%s)" % heurescoef
|
||||||
|
+ tag_edit
|
||||||
|
)
|
||||||
|
H.append("</li>")
|
||||||
|
if not modules:
|
||||||
|
H.append("<li>Aucun module dans cette matière ! ")
|
||||||
|
if editable:
|
||||||
|
H.append(
|
||||||
|
f"""<a class="stdlink" href="{
|
||||||
|
url_for("notes.matiere_delete",
|
||||||
|
scodoc_dept=g.scodoc_dept, matiere_id=mat["matiere_id"])}"
|
||||||
|
>la supprimer</a>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
H.append("</li>")
|
||||||
|
if editable: # and ((not parcours.UE_IS_MODULE) or len(Modlist) == 0):
|
||||||
|
H.append(
|
||||||
|
f"""<li> <a class="stdlink" href="{
|
||||||
|
url_for("notes.module_create",
|
||||||
|
scodoc_dept=g.scodoc_dept, matiere_id=mat["matiere_id"])}"
|
||||||
|
>créer un module</a></li>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
H.append("</ul>")
|
||||||
|
H.append("</li>")
|
||||||
|
return "\n".join(H)
|
||||||
|
|
||||||
|
|
||||||
def ue_sharing_code(ue_code=None, ue_id=None, hide_ue_id=None):
|
def ue_sharing_code(ue_code=None, ue_id=None, hide_ue_id=None):
|
||||||
"""HTML list of UE sharing this code
|
"""HTML list of UE sharing this code
|
||||||
Either ue_code or ue_id may be specified.
|
Either ue_code or ue_id may be specified.
|
||||||
@ -964,9 +1099,9 @@ def formation_table_recap(formation_id, format="html"):
|
|||||||
raise ScoValueError("invalid formation_id")
|
raise ScoValueError("invalid formation_id")
|
||||||
F = F[0]
|
F = F[0]
|
||||||
T = []
|
T = []
|
||||||
ue_list = ue_list(args={"formation_id": formation_id})
|
ues = ue_list(args={"formation_id": formation_id})
|
||||||
for UE in ue_list:
|
for ue in ues:
|
||||||
Matlist = sco_edit_matiere.matiere_list(args={"ue_id": UE["ue_id"]})
|
Matlist = sco_edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]})
|
||||||
for Mat in Matlist:
|
for Mat in Matlist:
|
||||||
Modlist = sco_edit_module.module_list(
|
Modlist = sco_edit_module.module_list(
|
||||||
args={"matiere_id": Mat["matiere_id"]}
|
args={"matiere_id": Mat["matiere_id"]}
|
||||||
@ -978,7 +1113,7 @@ def formation_table_recap(formation_id, format="html"):
|
|||||||
#
|
#
|
||||||
T.append(
|
T.append(
|
||||||
{
|
{
|
||||||
"UE_acro": UE["acronyme"],
|
"UE_acro": ue["acronyme"],
|
||||||
"Mat_tit": Mat["titre"],
|
"Mat_tit": Mat["titre"],
|
||||||
"Mod_tit": Mod["abbrev"] or Mod["titre"],
|
"Mod_tit": Mod["abbrev"] or Mod["titre"],
|
||||||
"Mod_code": Mod["code"],
|
"Mod_code": Mod["code"],
|
||||||
|
@ -356,7 +356,7 @@ def apo_semset_maq_status(
|
|||||||
H.append(
|
H.append(
|
||||||
", ".join(
|
", ".join(
|
||||||
[
|
[
|
||||||
'<a class="stdlink" href="ue_list?formation_id=%(formation_id)s">%(acronyme)s v%(version)s</a>'
|
'<a class="stdlink" href="ue_table?formation_id=%(formation_id)s">%(acronyme)s v%(version)s</a>'
|
||||||
% f
|
% f
|
||||||
for f in formations
|
for f in formations
|
||||||
]
|
]
|
||||||
|
@ -152,7 +152,7 @@ def format_nom(s, uppercase=True):
|
|||||||
def input_civilite(s):
|
def input_civilite(s):
|
||||||
"""Converts external representation of civilite to internal:
|
"""Converts external representation of civilite to internal:
|
||||||
'M', 'F', or 'X' (and nothing else).
|
'M', 'F', or 'X' (and nothing else).
|
||||||
Raises valueError if conversion fails.
|
Raises ScoValueError if conversion fails.
|
||||||
"""
|
"""
|
||||||
s = s.upper().strip()
|
s = s.upper().strip()
|
||||||
if s in ("M", "M.", "MR", "H"):
|
if s in ("M", "M.", "MR", "H"):
|
||||||
@ -161,12 +161,13 @@ def input_civilite(s):
|
|||||||
return "F"
|
return "F"
|
||||||
elif s == "X" or not s:
|
elif s == "X" or not s:
|
||||||
return "X"
|
return "X"
|
||||||
raise ValueError("valeur invalide pour la civilité: %s" % s)
|
raise ScoValueError("valeur invalide pour la civilité: %s" % s)
|
||||||
|
|
||||||
|
|
||||||
def format_civilite(civilite):
|
def format_civilite(civilite):
|
||||||
"""returns 'M.' ou 'Mme' ou '' (pour le genre neutre,
|
"""returns 'M.' ou 'Mme' ou '' (pour le genre neutre,
|
||||||
personne ne souhaitant pas d'affichage)
|
personne ne souhaitant pas d'affichage).
|
||||||
|
Raises ScoValueError if conversion fails.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return {
|
return {
|
||||||
@ -175,7 +176,7 @@ def format_civilite(civilite):
|
|||||||
"X": "",
|
"X": "",
|
||||||
}[civilite]
|
}[civilite]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ValueError("valeur invalide pour la civilité: %s" % civilite)
|
raise ScoValueError("valeur invalide pour la civilité: %s" % civilite)
|
||||||
|
|
||||||
|
|
||||||
def format_lycee(nomlycee):
|
def format_lycee(nomlycee):
|
||||||
|
@ -93,12 +93,21 @@ def formation_has_locked_sems(formation_id):
|
|||||||
return sems
|
return sems
|
||||||
|
|
||||||
|
|
||||||
def formation_export(formation_id, export_ids=False, export_tags=True, format=None):
|
def formation_export(
|
||||||
|
formation_id,
|
||||||
|
export_ids=False,
|
||||||
|
export_tags=True,
|
||||||
|
export_external_ues=False,
|
||||||
|
format=None,
|
||||||
|
):
|
||||||
"""Get a formation, with UE, matieres, modules
|
"""Get a formation, with UE, matieres, modules
|
||||||
in desired format
|
in desired format
|
||||||
"""
|
"""
|
||||||
F = formation_list(args={"formation_id": formation_id})[0]
|
F = formation_list(args={"formation_id": formation_id})[0]
|
||||||
ues = sco_edit_ue.ue_list({"formation_id": formation_id})
|
selector = {"formation_id": formation_id}
|
||||||
|
if not export_external_ues:
|
||||||
|
selector["is_external"] = False
|
||||||
|
ues = sco_edit_ue.ue_list(selector)
|
||||||
F["ue"] = ues
|
F["ue"] = ues
|
||||||
for ue in ues:
|
for ue in ues:
|
||||||
ue_id = ue["ue_id"]
|
ue_id = ue["ue_id"]
|
||||||
@ -254,7 +263,11 @@ def formation_list_table(formation_id=None, args={}):
|
|||||||
).NAME
|
).NAME
|
||||||
except:
|
except:
|
||||||
f["parcours_name"] = ""
|
f["parcours_name"] = ""
|
||||||
f["_titre_target"] = "ue_list?formation_id=%(formation_id)s" % f
|
f["_titre_target"] = url_for(
|
||||||
|
"notes.ue_table",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formation_id=str(f["formation_id"]),
|
||||||
|
)
|
||||||
f["_titre_link_class"] = "stdlink"
|
f["_titre_link_class"] = "stdlink"
|
||||||
f["_titre_id"] = "titre-%s" % f["acronyme"].lower().replace(" ", "-")
|
f["_titre_id"] = "titre-%s" % f["acronyme"].lower().replace(" ", "-")
|
||||||
# Ajoute les semestres associés à chaque formation:
|
# Ajoute les semestres associés à chaque formation:
|
||||||
|
@ -675,7 +675,7 @@ def do_formsemestre_createwithmodules(edit=False):
|
|||||||
|
|
||||||
if tf[0] == 0 or msg:
|
if tf[0] == 0 or msg:
|
||||||
return (
|
return (
|
||||||
'<p>Formation <a class="discretelink" href="ue_list?formation_id=%(formation_id)s"><em>%(titre)s</em> (%(acronyme)s), version %(version)s, code %(formation_code)s</a></p>'
|
'<p>Formation <a class="discretelink" href="ue_table?formation_id=%(formation_id)s"><em>%(titre)s</em> (%(acronyme)s), version %(version)s, code %(formation_code)s</a></p>'
|
||||||
% F
|
% F
|
||||||
+ msg
|
+ msg
|
||||||
+ str(tf[1])
|
+ str(tf[1])
|
||||||
|
@ -221,12 +221,11 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid):
|
|||||||
"""
|
"""
|
||||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||||
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
||||||
ue_list = _list_ue_with_coef_and_validations(sem, etudid)
|
ues = _list_ue_with_coef_and_validations(sem, etudid)
|
||||||
descr = _ue_form_description(ue_list, scu.get_request_args())
|
descr = _ue_form_description(ues, scu.get_request_args())
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
initvalues = {
|
initvalues = {
|
||||||
"note_" + str(ue["ue_id"]): ue["validation"].get("moy_ue", "")
|
"note_" + str(ue["ue_id"]): ue["validation"].get("moy_ue", "") for ue in ues
|
||||||
for ue in ue_list
|
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
initvalues = {}
|
initvalues = {}
|
||||||
@ -247,15 +246,13 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid):
|
|||||||
return "\n".join(H)
|
return "\n".join(H)
|
||||||
else: # soumission
|
else: # soumission
|
||||||
# simule erreur
|
# simule erreur
|
||||||
ok, message = _check_values(ue_list, tf[2])
|
ok, message = _check_values(ues, tf[2])
|
||||||
if not ok:
|
if not ok:
|
||||||
H = _make_page(etud, sem, tf, message=message)
|
H = _make_page(etud, sem, tf, message=message)
|
||||||
return "\n".join(H)
|
return "\n".join(H)
|
||||||
else:
|
else:
|
||||||
# Submit
|
# Submit
|
||||||
_record_ue_validations_and_coefs(
|
_record_ue_validations_and_coefs(formsemestre_id, etudid, ues, tf[2])
|
||||||
formsemestre_id, etudid, ue_list, tf[2]
|
|
||||||
)
|
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
"formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s"
|
"formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s"
|
||||||
% (formsemestre_id, etudid)
|
% (formsemestre_id, etudid)
|
||||||
@ -303,7 +300,7 @@ _UE_VALID_CODES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _ue_form_description(ue_list, values):
|
def _ue_form_description(ues, values):
|
||||||
"""Description du formulaire de saisie des UE / validations
|
"""Description du formulaire de saisie des UE / validations
|
||||||
Pour chaque UE, on peut saisir: son code jury, sa note, son coefficient.
|
Pour chaque UE, on peut saisir: son code jury, sa note, son coefficient.
|
||||||
"""
|
"""
|
||||||
@ -320,7 +317,7 @@ def _ue_form_description(ue_list, values):
|
|||||||
("formsemestre_id", {"input_type": "hidden"}),
|
("formsemestre_id", {"input_type": "hidden"}),
|
||||||
("etudid", {"input_type": "hidden"}),
|
("etudid", {"input_type": "hidden"}),
|
||||||
]
|
]
|
||||||
for ue in ue_list:
|
for ue in ues:
|
||||||
# Menu pour code validation UE:
|
# Menu pour code validation UE:
|
||||||
# Ne propose que ADM, CMP et "Non inscrit"
|
# Ne propose que ADM, CMP et "Non inscrit"
|
||||||
select_name = "valid_" + str(ue["ue_id"])
|
select_name = "valid_" + str(ue["ue_id"])
|
||||||
@ -439,8 +436,8 @@ def _list_ue_with_coef_and_validations(sem, etudid):
|
|||||||
"""
|
"""
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
formsemestre_id = sem["formsemestre_id"]
|
formsemestre_id = sem["formsemestre_id"]
|
||||||
ue_list = sco_edit_ue.ue_list({"formation_id": sem["formation_id"]})
|
ues = sco_edit_ue.ue_list({"formation_id": sem["formation_id"]})
|
||||||
for ue in ue_list:
|
for ue in ues:
|
||||||
# add coefficient
|
# add coefficient
|
||||||
uecoef = sco_formsemestre.formsemestre_uecoef_list(
|
uecoef = sco_formsemestre.formsemestre_uecoef_list(
|
||||||
cnx, args={"formsemestre_id": formsemestre_id, "ue_id": ue["ue_id"]}
|
cnx, args={"formsemestre_id": formsemestre_id, "ue_id": ue["ue_id"]}
|
||||||
@ -462,11 +459,11 @@ def _list_ue_with_coef_and_validations(sem, etudid):
|
|||||||
ue["validation"] = validation[0]
|
ue["validation"] = validation[0]
|
||||||
else:
|
else:
|
||||||
ue["validation"] = {}
|
ue["validation"] = {}
|
||||||
return ue_list
|
return ues
|
||||||
|
|
||||||
|
|
||||||
def _record_ue_validations_and_coefs(formsemestre_id, etudid, ue_list, values):
|
def _record_ue_validations_and_coefs(formsemestre_id, etudid, ues, values):
|
||||||
for ue in ue_list:
|
for ue in ues:
|
||||||
code = values.get("valid_" + str(ue["ue_id"]), False)
|
code = values.get("valid_" + str(ue["ue_id"]), False)
|
||||||
if code == "None":
|
if code == "None":
|
||||||
code = None
|
code = None
|
||||||
|
@ -89,8 +89,8 @@ def get_group(group_id):
|
|||||||
"""Returns group object, with partition"""
|
"""Returns group object, with partition"""
|
||||||
r = ndb.SimpleDictFetch(
|
r = ndb.SimpleDictFetch(
|
||||||
"""SELECT gd.id AS group_id, gd.*, p.id AS partition_id, p.*
|
"""SELECT gd.id AS group_id, gd.*, p.id AS partition_id, p.*
|
||||||
FROM group_descr gd, partition p
|
FROM group_descr gd, partition p
|
||||||
WHERE gd.id=%(group_id)s
|
WHERE gd.id=%(group_id)s
|
||||||
AND p.id = gd.partition_id
|
AND p.id = gd.partition_id
|
||||||
""",
|
""",
|
||||||
{"group_id": group_id},
|
{"group_id": group_id},
|
||||||
@ -112,8 +112,8 @@ def group_delete(group, force=False):
|
|||||||
|
|
||||||
def get_partition(partition_id):
|
def get_partition(partition_id):
|
||||||
r = ndb.SimpleDictFetch(
|
r = ndb.SimpleDictFetch(
|
||||||
"""SELECT p.id AS partition_id, p.*
|
"""SELECT p.id AS partition_id, p.*
|
||||||
FROM partition p
|
FROM partition p
|
||||||
WHERE p.id = %(partition_id)s
|
WHERE p.id = %(partition_id)s
|
||||||
""",
|
""",
|
||||||
{"partition_id": partition_id},
|
{"partition_id": partition_id},
|
||||||
@ -126,7 +126,7 @@ def get_partition(partition_id):
|
|||||||
def get_partitions_list(formsemestre_id, with_default=True):
|
def get_partitions_list(formsemestre_id, with_default=True):
|
||||||
"""Liste des partitions pour ce semestre (list of dicts)"""
|
"""Liste des partitions pour ce semestre (list of dicts)"""
|
||||||
partitions = ndb.SimpleDictFetch(
|
partitions = ndb.SimpleDictFetch(
|
||||||
"""SELECT p.id AS partition_id, p.*
|
"""SELECT p.id AS partition_id, p.*
|
||||||
FROM partition p
|
FROM partition p
|
||||||
WHERE formsemestre_id=%(formsemestre_id)s
|
WHERE formsemestre_id=%(formsemestre_id)s
|
||||||
ORDER BY numero""",
|
ORDER BY numero""",
|
||||||
@ -143,7 +143,7 @@ def get_default_partition(formsemestre_id):
|
|||||||
"""Get partition for 'all' students (this one always exists, with NULL name)"""
|
"""Get partition for 'all' students (this one always exists, with NULL name)"""
|
||||||
r = ndb.SimpleDictFetch(
|
r = ndb.SimpleDictFetch(
|
||||||
"""SELECT p.id AS partition_id, p.* FROM partition p
|
"""SELECT p.id AS partition_id, p.* FROM partition p
|
||||||
WHERE formsemestre_id=%(formsemestre_id)s
|
WHERE formsemestre_id=%(formsemestre_id)s
|
||||||
AND partition_name is NULL
|
AND partition_name is NULL
|
||||||
""",
|
""",
|
||||||
{"formsemestre_id": formsemestre_id},
|
{"formsemestre_id": formsemestre_id},
|
||||||
@ -170,10 +170,10 @@ def get_partition_groups(partition):
|
|||||||
"""List of groups in this partition (list of dicts).
|
"""List of groups in this partition (list of dicts).
|
||||||
Some groups may be empty."""
|
Some groups may be empty."""
|
||||||
return ndb.SimpleDictFetch(
|
return ndb.SimpleDictFetch(
|
||||||
"""SELECT gd.id AS group_id, p.id AS partition_id, gd.*, p.*
|
"""SELECT gd.id AS group_id, p.id AS partition_id, gd.*, p.*
|
||||||
FROM group_descr gd, partition p
|
FROM group_descr gd, partition p
|
||||||
WHERE gd.partition_id=%(partition_id)s
|
WHERE gd.partition_id=%(partition_id)s
|
||||||
AND gd.partition_id=p.id
|
AND gd.partition_id=p.id
|
||||||
ORDER BY group_name
|
ORDER BY group_name
|
||||||
""",
|
""",
|
||||||
partition,
|
partition,
|
||||||
@ -184,9 +184,9 @@ def get_default_group(formsemestre_id, fix_if_missing=False):
|
|||||||
"""Returns group_id for default ('tous') group"""
|
"""Returns group_id for default ('tous') group"""
|
||||||
r = ndb.SimpleDictFetch(
|
r = ndb.SimpleDictFetch(
|
||||||
"""SELECT gd.id AS group_id
|
"""SELECT gd.id AS group_id
|
||||||
FROM group_descr gd, partition p
|
FROM group_descr gd, partition p
|
||||||
WHERE p.formsemestre_id=%(formsemestre_id)s
|
WHERE p.formsemestre_id=%(formsemestre_id)s
|
||||||
AND p.partition_name is NULL
|
AND p.partition_name is NULL
|
||||||
AND p.id = gd.partition_id
|
AND p.id = gd.partition_id
|
||||||
""",
|
""",
|
||||||
{"formsemestre_id": formsemestre_id},
|
{"formsemestre_id": formsemestre_id},
|
||||||
@ -218,8 +218,8 @@ def get_sem_groups(formsemestre_id):
|
|||||||
"""Returns groups for this sem (in all partitions)."""
|
"""Returns groups for this sem (in all partitions)."""
|
||||||
return ndb.SimpleDictFetch(
|
return ndb.SimpleDictFetch(
|
||||||
"""SELECT gd.id AS group_id, p.id AS partition_id, gd.*, p.*
|
"""SELECT gd.id AS group_id, p.id AS partition_id, gd.*, p.*
|
||||||
FROM group_descr gd, partition p
|
FROM group_descr gd, partition p
|
||||||
WHERE p.formsemestre_id=%(formsemestre_id)s
|
WHERE p.formsemestre_id=%(formsemestre_id)s
|
||||||
AND p.id = gd.partition_id
|
AND p.id = gd.partition_id
|
||||||
""",
|
""",
|
||||||
{"formsemestre_id": formsemestre_id},
|
{"formsemestre_id": formsemestre_id},
|
||||||
@ -340,7 +340,7 @@ def get_etud_groups(etudid, sem, exclude_default=False):
|
|||||||
"""Infos sur groupes de l'etudiant dans ce semestre
|
"""Infos sur groupes de l'etudiant dans ce semestre
|
||||||
[ group + partition_name ]
|
[ group + partition_name ]
|
||||||
"""
|
"""
|
||||||
req = """SELECT p.id AS partition_id, p.*, g.id AS group_id, g.*
|
req = """SELECT p.id AS partition_id, p.*, g.id AS group_id, g.*
|
||||||
FROM group_descr g, partition p, group_membership gm
|
FROM group_descr g, partition p, group_membership gm
|
||||||
WHERE gm.etudid=%(etudid)s
|
WHERE gm.etudid=%(etudid)s
|
||||||
and gm.group_id = g.id
|
and gm.group_id = g.id
|
||||||
@ -377,10 +377,10 @@ def formsemestre_get_etud_groupnames(formsemestre_id, attr="group_name"):
|
|||||||
{ etudid : { partition_id : group_name }} (attr=group_name or group_id)
|
{ etudid : { partition_id : group_name }} (attr=group_name or group_id)
|
||||||
"""
|
"""
|
||||||
infos = ndb.SimpleDictFetch(
|
infos = ndb.SimpleDictFetch(
|
||||||
"""SELECT i.id AS etudid, p.id AS partition_id,
|
"""SELECT i.id AS etudid, p.id AS partition_id,
|
||||||
gd.group_name, gd.id AS group_id
|
gd.group_name, gd.id AS group_id
|
||||||
FROM notes_formsemestre_inscription i, partition p,
|
FROM notes_formsemestre_inscription i, partition p,
|
||||||
group_descr gd, group_membership gm
|
group_descr gd, group_membership gm
|
||||||
WHERE i.formsemestre_id=%(formsemestre_id)s
|
WHERE i.formsemestre_id=%(formsemestre_id)s
|
||||||
and i.formsemestre_id = p.formsemestre_id
|
and i.formsemestre_id = p.formsemestre_id
|
||||||
and p.id = gd.partition_id
|
and p.id = gd.partition_id
|
||||||
@ -413,7 +413,7 @@ def etud_add_group_infos(etud, sem, sep=" "):
|
|||||||
FROM group_descr g, partition p, group_membership gm WHERE gm.etudid=%(etudid)s
|
FROM group_descr g, partition p, group_membership gm WHERE gm.etudid=%(etudid)s
|
||||||
and gm.group_id = g.id
|
and gm.group_id = g.id
|
||||||
and g.partition_id = p.id
|
and g.partition_id = p.id
|
||||||
and p.formsemestre_id = %(formsemestre_id)s
|
and p.formsemestre_id = %(formsemestre_id)s
|
||||||
ORDER BY p.numero
|
ORDER BY p.numero
|
||||||
""",
|
""",
|
||||||
{"etudid": etud["etudid"], "formsemestre_id": sem["formsemestre_id"]},
|
{"etudid": etud["etudid"], "formsemestre_id": sem["formsemestre_id"]},
|
||||||
@ -806,8 +806,21 @@ def partition_create(
|
|||||||
)
|
)
|
||||||
|
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
|
if numero is None:
|
||||||
|
numero = (
|
||||||
|
ndb.SimpleQuery(
|
||||||
|
"SELECT MAX(id) FROM partition WHERE formsemestre_id=%(formsemestre_id)s",
|
||||||
|
{"formsemestre_id": formsemestre_id},
|
||||||
|
).fetchone()[0]
|
||||||
|
or 0
|
||||||
|
)
|
||||||
partition_id = partitionEditor.create(
|
partition_id = partitionEditor.create(
|
||||||
cnx, {"formsemestre_id": formsemestre_id, "partition_name": partition_name}
|
cnx,
|
||||||
|
{
|
||||||
|
"formsemestre_id": formsemestre_id,
|
||||||
|
"partition_name": partition_name,
|
||||||
|
"numero": numero,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
log("createPartition: created partition_id=%s" % partition_id)
|
log("createPartition: created partition_id=%s" % partition_id)
|
||||||
#
|
#
|
||||||
@ -1041,7 +1054,7 @@ def partition_move(partition_id, after=0, redirect=1):
|
|||||||
others = get_partitions_list(formsemestre_id)
|
others = get_partitions_list(formsemestre_id)
|
||||||
if len(others) > 1:
|
if len(others) > 1:
|
||||||
pidx = [p["partition_id"] for p in others].index(partition_id)
|
pidx = [p["partition_id"] for p in others].index(partition_id)
|
||||||
log("partition_move: after=%s pidx=%s" % (after, pidx))
|
# log("partition_move: after=%s pidx=%s" % (after, pidx))
|
||||||
neigh = None # partition to swap with
|
neigh = None # partition to swap with
|
||||||
if after == 0 and pidx > 0:
|
if after == 0 and pidx > 0:
|
||||||
neigh = others[pidx - 1]
|
neigh = others[pidx - 1]
|
||||||
@ -1049,8 +1062,20 @@ def partition_move(partition_id, after=0, redirect=1):
|
|||||||
neigh = others[pidx + 1]
|
neigh = others[pidx + 1]
|
||||||
if neigh: #
|
if neigh: #
|
||||||
# swap numero between partition and its neighbor
|
# swap numero between partition and its neighbor
|
||||||
log("moving partition %s" % partition_id)
|
# log("moving partition %s" % partition_id)
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
|
# Si aucun numéro n'a été affecté, le met au minimum
|
||||||
|
min_numero = (
|
||||||
|
ndb.SimpleQuery(
|
||||||
|
"SELECT MIN(numero) FROM partition WHERE formsemestre_id=%(formsemestre_id)s",
|
||||||
|
{"formsemestre_id": formsemestre_id},
|
||||||
|
).fetchone()[0]
|
||||||
|
or 0
|
||||||
|
)
|
||||||
|
if neigh["numero"] is None:
|
||||||
|
neigh["numero"] = min_numero - 1
|
||||||
|
if partition["numero"] is None:
|
||||||
|
partition["numero"] = min_numero - 1 - after
|
||||||
partition["numero"], neigh["numero"] = neigh["numero"], partition["numero"]
|
partition["numero"], neigh["numero"] = neigh["numero"], partition["numero"]
|
||||||
partitionEditor.edit(cnx, partition)
|
partitionEditor.edit(cnx, partition)
|
||||||
partitionEditor.edit(cnx, neigh)
|
partitionEditor.edit(cnx, neigh)
|
||||||
@ -1116,13 +1141,13 @@ def partition_set_name(partition_id, partition_name, redirect=1):
|
|||||||
|
|
||||||
# check unicity
|
# check unicity
|
||||||
r = ndb.SimpleDictFetch(
|
r = ndb.SimpleDictFetch(
|
||||||
"""SELECT p.* FROM partition p
|
"""SELECT p.* FROM partition p
|
||||||
WHERE p.partition_name = %(partition_name)s
|
WHERE p.partition_name = %(partition_name)s
|
||||||
AND formsemestre_id = %(formsemestre_id)s
|
AND formsemestre_id = %(formsemestre_id)s
|
||||||
""",
|
""",
|
||||||
{"partition_name": partition_name, "formsemestre_id": formsemestre_id},
|
{"partition_name": partition_name, "formsemestre_id": formsemestre_id},
|
||||||
)
|
)
|
||||||
if len(r) > 1 or (len(r) == 1 and r[0]["partition_id"] != partition_id):
|
if len(r) > 1 or (len(r) == 1 and r[0]["id"] != partition_id):
|
||||||
raise ScoValueError(
|
raise ScoValueError(
|
||||||
"Partition %s déjà existante dans ce semestre !" % partition_name
|
"Partition %s déjà existante dans ce semestre !" % partition_name
|
||||||
)
|
)
|
||||||
@ -1469,9 +1494,9 @@ def listgroups(group_ids):
|
|||||||
groups = []
|
groups = []
|
||||||
for group_id in group_ids:
|
for group_id in group_ids:
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""SELECT gd.id AS group_id, gd.*, p.id AS partition_id, p.*
|
"""SELECT gd.id AS group_id, gd.*, p.id AS partition_id, p.*
|
||||||
FROM group_descr gd, partition p
|
FROM group_descr gd, partition p
|
||||||
WHERE p.id = gd.partition_id
|
WHERE p.id = gd.partition_id
|
||||||
AND gd.id = %(group_id)s
|
AND gd.id = %(group_id)s
|
||||||
""",
|
""",
|
||||||
{"group_id": group_id},
|
{"group_id": group_id},
|
||||||
|
@ -178,6 +178,19 @@ def moduleimpl_withmodule_list(
|
|||||||
return modimpls
|
return modimpls
|
||||||
|
|
||||||
|
|
||||||
|
def moduleimpls_in_external_ue(ue_id):
|
||||||
|
"""List of modimpls in this ue"""
|
||||||
|
cursor = ndb.SimpleQuery(
|
||||||
|
"""SELECT DISTINCT mi.*
|
||||||
|
FROM notes_ue u, notes_moduleimpl mi, notes_modules m
|
||||||
|
WHERE u.is_external is true
|
||||||
|
AND mi.module_id = m.id AND m.ue_id = %(ue_id)s
|
||||||
|
""",
|
||||||
|
{"ue_id": ue_id},
|
||||||
|
)
|
||||||
|
return cursor.dictfetchall()
|
||||||
|
|
||||||
|
|
||||||
def do_moduleimpl_inscription_list(moduleimpl_id=None, etudid=None):
|
def do_moduleimpl_inscription_list(moduleimpl_id=None, etudid=None):
|
||||||
"list moduleimpl_inscriptions"
|
"list moduleimpl_inscriptions"
|
||||||
args = locals()
|
args = locals()
|
||||||
|
@ -348,7 +348,6 @@ def make_formsemestre_recapcomplet(
|
|||||||
if not hidemodules:
|
if not hidemodules:
|
||||||
h.append("")
|
h.append("")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if not hidemodules and not ue["is_external"]:
|
if not hidemodules and not ue["is_external"]:
|
||||||
for modimpl in modimpls:
|
for modimpl in modimpls:
|
||||||
if modimpl["module"]["ue_id"] == ue["ue_id"]:
|
if modimpl["module"]["ue_id"] == ue["ue_id"]:
|
||||||
|
@ -388,7 +388,7 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
|
|||||||
):
|
):
|
||||||
# Enseignant associé au module: ne peut supprimer que les notes qu'il a saisi
|
# Enseignant associé au module: ne peut supprimer que les notes qu'il a saisi
|
||||||
NotesDB = sco_evaluations.do_evaluation_get_all_notes(
|
NotesDB = sco_evaluations.do_evaluation_get_all_notes(
|
||||||
evaluation_id, by_uid=current_user.user_name
|
evaluation_id, by_uid=current_user.id
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise AccessDenied("Modification des notes impossible pour %s" % current_user)
|
raise AccessDenied("Modification des notes impossible pour %s" % current_user)
|
||||||
@ -399,7 +399,10 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
|
|||||||
nb_changed, nb_suppress, existing_decisions = _notes_add(
|
nb_changed, nb_suppress, existing_decisions = _notes_add(
|
||||||
current_user, evaluation_id, notes, do_it=False
|
current_user, evaluation_id, notes, do_it=False
|
||||||
)
|
)
|
||||||
msg = "<p>Confirmer la suppression des %d notes ?</p>" % nb_suppress
|
msg = (
|
||||||
|
"<p>Confirmer la suppression des %d notes ? <em>(peut affecter plusieurs groupes)</em></p>"
|
||||||
|
% nb_suppress
|
||||||
|
)
|
||||||
if existing_decisions:
|
if existing_decisions:
|
||||||
msg += """<p class="warning">Important: il y a déjà des décisions de jury enregistrées, qui seront potentiellement à revoir suite à cette modification !</p>"""
|
msg += """<p class="warning">Important: il y a déjà des décisions de jury enregistrées, qui seront potentiellement à revoir suite à cette modification !</p>"""
|
||||||
return scu.confirm_dialog(
|
return scu.confirm_dialog(
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
import io
|
import io
|
||||||
from zipfile import ZipFile, BadZipfile
|
from zipfile import ZipFile, BadZipfile
|
||||||
|
from flask.templating import render_template
|
||||||
import reportlab
|
import reportlab
|
||||||
from reportlab.lib.units import cm, mm
|
from reportlab.lib.units import cm, mm
|
||||||
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
|
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
|
||||||
@ -531,25 +532,33 @@ def photos_import_files_form(group_ids=[]):
|
|||||||
elif tf[0] == -1:
|
elif tf[0] == -1:
|
||||||
return flask.redirect(back_url)
|
return flask.redirect(back_url)
|
||||||
else:
|
else:
|
||||||
return photos_import_files(
|
|
||||||
group_ids=tf[2]["group_ids"],
|
def callback(etud, data, filename):
|
||||||
|
sco_photos.store_photo(etud, data)
|
||||||
|
|
||||||
|
(
|
||||||
|
ignored_zipfiles,
|
||||||
|
unmatched_files,
|
||||||
|
stored_etud_filename,
|
||||||
|
) = zip_excel_import_files(
|
||||||
xlsfile=tf[2]["xlsfile"],
|
xlsfile=tf[2]["xlsfile"],
|
||||||
zipfile=tf[2]["zipfile"],
|
zipfile=tf[2]["zipfile"],
|
||||||
|
callback=callback,
|
||||||
|
filename_title="fichier_photo",
|
||||||
|
)
|
||||||
|
return render_template(
|
||||||
|
"scolar/photos_import_files.html",
|
||||||
|
page_title="Téléchargement des photos des étudiants",
|
||||||
|
ignored_zipfiles=ignored_zipfiles,
|
||||||
|
unmatched_files=unmatched_files,
|
||||||
|
stored_etud_filename=stored_etud_filename,
|
||||||
|
next_page=url_for(
|
||||||
|
"scolar.groups_view",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formsemestre_id=groups_infos.formsemestre_id,
|
||||||
|
curtab="tab-photos",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def photos_import_files(group_ids=[], xlsfile=None, zipfile=None):
|
|
||||||
"""Importation des photos"""
|
|
||||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
|
||||||
back_url = "groups_view?%s&curtab=tab-photos" % groups_infos.groups_query_args
|
|
||||||
filename_title = "fichier_photo"
|
|
||||||
page_title = "Téléchargement des photos des étudiants"
|
|
||||||
|
|
||||||
def callback(etud, data, filename):
|
|
||||||
sco_photos.store_photo(etud, data)
|
|
||||||
|
|
||||||
zip_excel_import_files(xlsfile, zipfile, callback, filename_title, page_title)
|
|
||||||
return flask.redirect(back_url + "&head_message=photos%20 importees")
|
|
||||||
|
|
||||||
|
|
||||||
def zip_excel_import_files(
|
def zip_excel_import_files(
|
||||||
@ -557,19 +566,19 @@ def zip_excel_import_files(
|
|||||||
zipfile=None,
|
zipfile=None,
|
||||||
callback=None,
|
callback=None,
|
||||||
filename_title="", # doit obligatoirement etre specifié
|
filename_title="", # doit obligatoirement etre specifié
|
||||||
page_title="",
|
|
||||||
):
|
):
|
||||||
"""Importation de fichiers à partir d'un excel et d'un zip
|
"""Importation de fichiers à partir d'un excel et d'un zip
|
||||||
La fonction
|
La fonction
|
||||||
callback()
|
callback()
|
||||||
est appelé pour chaque fichier trouvé.
|
est appelée pour chaque fichier trouvé.
|
||||||
|
Fonction utilisée pour les photos et les fichiers étudiants (archives).
|
||||||
"""
|
"""
|
||||||
# 1- build mapping etudid -> filename
|
# 1- build mapping etudid -> filename
|
||||||
exceldata = xlsfile.read()
|
exceldata = xlsfile.read()
|
||||||
if not exceldata:
|
if not exceldata:
|
||||||
raise ScoValueError("Fichier excel vide ou invalide")
|
raise ScoValueError("Fichier excel vide ou invalide")
|
||||||
_, data = sco_excel.excel_bytes_to_list(exceldata)
|
_, data = sco_excel.excel_bytes_to_list(exceldata)
|
||||||
if not data: # probably a bug
|
if not data:
|
||||||
raise ScoValueError("Fichier excel vide !")
|
raise ScoValueError("Fichier excel vide !")
|
||||||
# on doit avoir une colonne etudid et une colonne filename_title ('fichier_photo')
|
# on doit avoir une colonne etudid et une colonne filename_title ('fichier_photo')
|
||||||
titles = data[0]
|
titles = data[0]
|
||||||
@ -591,30 +600,30 @@ def zip_excel_import_files(
|
|||||||
fn = fn.split("/")[-1] # use only last component, not directories
|
fn = fn.split("/")[-1] # use only last component, not directories
|
||||||
return fn
|
return fn
|
||||||
|
|
||||||
Filename2Etud = {} # filename : etudid
|
filename_to_etud = {} # filename : etudid
|
||||||
for l in data[1:]:
|
for l in data[1:]:
|
||||||
filename = l[filename_idx].strip()
|
filename = l[filename_idx].strip()
|
||||||
if filename:
|
if filename:
|
||||||
Filename2Etud[normfilename(filename)] = l[etudid_idx]
|
filename_to_etud[normfilename(filename)] = l[etudid_idx]
|
||||||
|
|
||||||
# 2- Ouvre le zip et
|
# 2- Ouvre le zip et
|
||||||
try:
|
try:
|
||||||
z = ZipFile(zipfile)
|
z = ZipFile(zipfile)
|
||||||
except BadZipfile:
|
except BadZipfile:
|
||||||
raise ScoValueError("Fichier ZIP incorrect !")
|
raise ScoValueError("Fichier ZIP incorrect !") from BadZipfile
|
||||||
ignored_zipfiles = []
|
ignored_zipfiles = []
|
||||||
stored = [] # [ (etud, filename) ]
|
stored_etud_filename = [] # [ (etud, filename) ]
|
||||||
for name in z.namelist():
|
for name in z.namelist():
|
||||||
if len(name) > 4 and name[-1] != "/" and "." in name:
|
if len(name) > 4 and name[-1] != "/" and "." in name:
|
||||||
data = z.read(name)
|
data = z.read(name)
|
||||||
# match zip filename with name given in excel
|
# match zip filename with name given in excel
|
||||||
normname = normfilename(name)
|
normname = normfilename(name)
|
||||||
if normname in Filename2Etud:
|
if normname in filename_to_etud:
|
||||||
etudid = Filename2Etud[normname]
|
etudid = filename_to_etud[normname]
|
||||||
# ok, store photo
|
# ok, store photo
|
||||||
try:
|
try:
|
||||||
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
||||||
del Filename2Etud[normname]
|
del filename_to_etud[normname]
|
||||||
except:
|
except:
|
||||||
raise ScoValueError("ID étudiant invalide: %s" % etudid)
|
raise ScoValueError("ID étudiant invalide: %s" % etudid)
|
||||||
|
|
||||||
@ -624,7 +633,7 @@ def zip_excel_import_files(
|
|||||||
normfilename(name, lowercase=False),
|
normfilename(name, lowercase=False),
|
||||||
)
|
)
|
||||||
|
|
||||||
stored.append((etud, name))
|
stored_etud_filename.append((etud, name))
|
||||||
else:
|
else:
|
||||||
log("zip: zip name %s not in excel !" % name)
|
log("zip: zip name %s not in excel !" % name)
|
||||||
ignored_zipfiles.append(name)
|
ignored_zipfiles.append(name)
|
||||||
@ -632,35 +641,9 @@ def zip_excel_import_files(
|
|||||||
if name[-1] != "/":
|
if name[-1] != "/":
|
||||||
ignored_zipfiles.append(name)
|
ignored_zipfiles.append(name)
|
||||||
log("zip: ignoring %s" % name)
|
log("zip: ignoring %s" % name)
|
||||||
if Filename2Etud:
|
if filename_to_etud:
|
||||||
# lignes excel non traitées
|
# lignes excel non traitées
|
||||||
unmatched_files = list(Filename2Etud.keys())
|
unmatched_files = list(filename_to_etud.keys())
|
||||||
else:
|
else:
|
||||||
unmatched_files = []
|
unmatched_files = []
|
||||||
# 3- Result page
|
return ignored_zipfiles, unmatched_files, stored_etud_filename
|
||||||
H = [
|
|
||||||
_trombino_html_header(),
|
|
||||||
"""<h2 class="formsemestre">%s</h2>
|
|
||||||
<h3>Opération effectuée</h3>
|
|
||||||
"""
|
|
||||||
% page_title,
|
|
||||||
]
|
|
||||||
if ignored_zipfiles:
|
|
||||||
H.append("<h4>Fichiers ignorés dans le zip:</h4><ul>")
|
|
||||||
for name in ignored_zipfiles:
|
|
||||||
H.append("<li>%s</li>" % name)
|
|
||||||
H.append("</ul>")
|
|
||||||
if unmatched_files:
|
|
||||||
H.append(
|
|
||||||
"<h4>Fichiers indiqués dans feuille mais non trouvés dans le zip:</h4><ul>"
|
|
||||||
)
|
|
||||||
for name in unmatched_files:
|
|
||||||
H.append("<li>%s</li>" % name)
|
|
||||||
H.append("</ul>")
|
|
||||||
if stored:
|
|
||||||
H.append("<h4>Fichiers chargés:</h4><ul>")
|
|
||||||
for (etud, name) in stored:
|
|
||||||
H.append("<li>%s: <tt>%s</tt></li>" % (etud["nomprenom"], name))
|
|
||||||
H.append("</ul>")
|
|
||||||
|
|
||||||
return "\n".join(H)
|
|
||||||
|
@ -572,17 +572,24 @@ def sendJSON(data, attached=False):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def sendXML(data, tagname=None, force_outer_xml_tag=True, attached=False):
|
def sendXML(data, tagname=None, force_outer_xml_tag=True, attached=False, quote=True):
|
||||||
if type(data) != list:
|
if type(data) != list:
|
||||||
data = [data] # always list-of-dicts
|
data = [data] # always list-of-dicts
|
||||||
if force_outer_xml_tag:
|
if force_outer_xml_tag:
|
||||||
data = [{tagname: data}]
|
data = [{tagname: data}]
|
||||||
tagname += "_list"
|
tagname += "_list"
|
||||||
doc = sco_xml.simple_dictlist2xml(data, tagname=tagname)
|
doc = sco_xml.simple_dictlist2xml(data, tagname=tagname, quote=quote)
|
||||||
return send_file(doc, filename="sco_data.xml", mime=XML_MIMETYPE, attached=attached)
|
return send_file(doc, filename="sco_data.xml", mime=XML_MIMETYPE, attached=attached)
|
||||||
|
|
||||||
|
|
||||||
def sendResult(data, name=None, format=None, force_outer_xml_tag=True, attached=False):
|
def sendResult(
|
||||||
|
data,
|
||||||
|
name=None,
|
||||||
|
format=None,
|
||||||
|
force_outer_xml_tag=True,
|
||||||
|
attached=False,
|
||||||
|
quote_xml=True,
|
||||||
|
):
|
||||||
if (format is None) or (format == "html"):
|
if (format is None) or (format == "html"):
|
||||||
return data
|
return data
|
||||||
elif format == "xml": # name is outer tagname
|
elif format == "xml": # name is outer tagname
|
||||||
@ -591,6 +598,7 @@ def sendResult(data, name=None, format=None, force_outer_xml_tag=True, attached=
|
|||||||
tagname=name,
|
tagname=name,
|
||||||
force_outer_xml_tag=force_outer_xml_tag,
|
force_outer_xml_tag=force_outer_xml_tag,
|
||||||
attached=attached,
|
attached=attached,
|
||||||
|
quote=quote_xml,
|
||||||
)
|
)
|
||||||
elif format == "json":
|
elif format == "json":
|
||||||
return sendJSON(data, attached=attached)
|
return sendJSON(data, attached=attached)
|
||||||
|
@ -82,25 +82,35 @@ def simple_dictlist2xml(dictlist, tagname=None, quote=False, pretty=True):
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def _repr_as_xml(v):
|
||||||
|
if isinstance(v, bool):
|
||||||
|
return str(int(v)) # booleans as "0" / "1"
|
||||||
|
return str(v)
|
||||||
|
|
||||||
|
|
||||||
def _dictlist2xml(dictlist, root=None, tagname=None, quote=False):
|
def _dictlist2xml(dictlist, root=None, tagname=None, quote=False):
|
||||||
scalar_types = (bytes, str, int, float)
|
scalar_types = (bytes, str, int, float, bool)
|
||||||
for d in dictlist:
|
for d in dictlist:
|
||||||
elem = ElementTree.Element(tagname)
|
elem = ElementTree.Element(tagname)
|
||||||
root.append(elem)
|
root.append(elem)
|
||||||
if isinstance(d, scalar_types) or isinstance(d, ApoEtapeVDI):
|
if isinstance(d, scalar_types) or isinstance(d, ApoEtapeVDI):
|
||||||
elem.set("code", str(d))
|
elem.set("code", _repr_as_xml(d))
|
||||||
else:
|
else:
|
||||||
if quote:
|
if quote:
|
||||||
d_scalar = dict(
|
d_scalar = dict(
|
||||||
[
|
[
|
||||||
(k, quote_xml_attr(v))
|
(k, quote_xml_attr(_repr_as_xml(v)))
|
||||||
for (k, v) in d.items()
|
for (k, v) in d.items()
|
||||||
if isinstance(v, scalar_types)
|
if isinstance(v, scalar_types)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
d_scalar = dict(
|
d_scalar = dict(
|
||||||
[(k, str(v)) for (k, v) in d.items() if isinstance(v, scalar_types)]
|
[
|
||||||
|
(k, _repr_as_xml(v))
|
||||||
|
for (k, v) in d.items()
|
||||||
|
if isinstance(v, scalar_types)
|
||||||
|
]
|
||||||
)
|
)
|
||||||
for k in d_scalar:
|
for k in d_scalar:
|
||||||
elem.set(k, d_scalar[k])
|
elem.set(k, d_scalar[k])
|
||||||
@ -134,4 +144,4 @@ def xml_to_dicts(element):
|
|||||||
for child in element.childNodes:
|
for child in element.childNodes:
|
||||||
if child.nodeType == ELEMENT_NODE:
|
if child.nodeType == ELEMENT_NODE:
|
||||||
childs.append(xml_to_dicts(child))
|
childs.append(xml_to_dicts(child))
|
||||||
return (element.nodeName, d, childs)
|
return (element.nodeName, d, childs)
|
||||||
|
@ -1511,6 +1511,19 @@ span.ue_type {
|
|||||||
margin-right: 1.5em;
|
margin-right: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.formation_ue_list_externes {
|
||||||
|
background-color: #98cc98;
|
||||||
|
}
|
||||||
|
div.formation_ue_list_externes ul.notes_ue_list, div.formation_ue_list_externes li.notes_ue_list {
|
||||||
|
background-color: #98cc98;
|
||||||
|
}
|
||||||
|
span.ue_is_external span {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
span.ue_is_external a {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
li.notes_matiere_list {
|
li.notes_matiere_list {
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
39
app/templates/scolar/photos_import_files.html
Normal file
39
app/templates/scolar/photos_import_files.html
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block app_content %}
|
||||||
|
|
||||||
|
<h2 class="formsemestre">{{ page_title }}</h2>
|
||||||
|
<h3>Opération effectuée</h3>
|
||||||
|
|
||||||
|
{% if ignored_zipfiles %}
|
||||||
|
<h4>Fichiers ignorés dans le zip:</h4>
|
||||||
|
<ul>
|
||||||
|
{% for name in ignored_zipfiles %}
|
||||||
|
<li>{{name}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if unmatched_files %}
|
||||||
|
<h4>Fichiers indiqués dans la feuille mais non trouvés dans le zip:</h4>
|
||||||
|
<ul>
|
||||||
|
{% for name in unmatched_files %}
|
||||||
|
<li>{{name}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if stored_etud_filename %}
|
||||||
|
<h4>Fichiers chargés:</h4>
|
||||||
|
<ul>
|
||||||
|
{% for (etud, name) in stored_etud_filename %}
|
||||||
|
<li>{{etud["nomprenom"]}}: <tt>{{name}}</tt></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p><a href="{{ next_page | safe }}">Continuer</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
23
app/templates/scolar/photos_import_files.txt
Executable file
23
app/templates/scolar/photos_import_files.txt
Executable file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
Importation des photo effectuée
|
||||||
|
|
||||||
|
{% if ignored_zipfiles %}
|
||||||
|
# Fichiers ignorés dans le zip:
|
||||||
|
{% for name in ignored_zipfiles %}
|
||||||
|
- {{name}}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if unmatched_files %}
|
||||||
|
# Fichiers indiqués dans la feuille mais non trouvés dans le zip:
|
||||||
|
{% for name in unmatched_files %}
|
||||||
|
- {{name}}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if stored_etud_filename %}
|
||||||
|
# Fichiers chargés:
|
||||||
|
{% for (etud, name) in stored_etud_filename %}
|
||||||
|
- {{etud["nomprenom"]}}: <tt>{{name}}</tt></li>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
@ -1,12 +1,12 @@
|
|||||||
<h2 class="insidebar">Dépt. {{ prefs["DeptName"] }}</h2>
|
<h2 class="insidebar">Dépt. {{ prefs["DeptName"] }}</h2>
|
||||||
<a href="{{ url_for('scodoc.index') }}" class="sidebar">Accueil</a> <br/>
|
<a href="{{ url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}" class="sidebar">Accueil</a> <br />
|
||||||
{% if prefs["DeptIntranetURL"] %}
|
{% if prefs["DeptIntranetURL"] %}
|
||||||
<a href="{{ prefs["DeptIntranetURL"] }}" class="sidebar">
|
<a href="{{ prefs[" DeptIntranetURL"] }}" class="sidebar">
|
||||||
{{ prefs["DeptIntranetTitle"] }}</a>
|
{{ prefs["DeptIntranetTitle"] }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<br/>
|
<br />
|
||||||
|
|
||||||
{#
|
{#
|
||||||
# Entreprises pas encore supporté en ScoDoc8
|
# Entreprises pas encore supporté en ScoDoc8
|
||||||
# <br/><a href="%(ScoURL)s/Entreprises" class="sidebar">Entreprises</a> <br/>
|
# <br /><a href="%(ScoURL)s/Entreprises" class="sidebar">Entreprises</a> <br />
|
||||||
#}
|
#}
|
@ -1046,9 +1046,9 @@ def EtatAbsencesDate(group_ids=[], date=None): # list of groups to display
|
|||||||
|
|
||||||
|
|
||||||
# ----- Gestion des "billets d'absence": signalement par les etudiants eux mêmes (à travers le portail)
|
# ----- Gestion des "billets d'absence": signalement par les etudiants eux mêmes (à travers le portail)
|
||||||
@bp.route("/AddBilletAbsence")
|
@bp.route("/AddBilletAbsence", methods=["GET", "POST"]) # API ScoDoc 7 compat
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoAbsAddBillet)
|
@permission_required_compat_scodoc7(Permission.ScoAbsAddBillet)
|
||||||
@scodoc7func
|
@scodoc7func
|
||||||
def AddBilletAbsence(
|
def AddBilletAbsence(
|
||||||
begin,
|
begin,
|
||||||
@ -1060,7 +1060,7 @@ def AddBilletAbsence(
|
|||||||
justified=True,
|
justified=True,
|
||||||
xml_reply=True,
|
xml_reply=True,
|
||||||
):
|
):
|
||||||
"""Memorise un "billet"
|
"""Mémorise un "billet"
|
||||||
begin et end sont au format ISO (eg "1999-01-08 04:05:06")
|
begin et end sont au format ISO (eg "1999-01-08 04:05:06")
|
||||||
"""
|
"""
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
@ -1251,9 +1251,9 @@ def XMLgetBilletsEtud(etudid=False):
|
|||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/listeBillets", methods=["GET", "POST"]) # pour compat anciens clients PHP
|
@bp.route("/listeBillets", methods=["GET"])
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required_compat_scodoc7(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@scodoc7func
|
@scodoc7func
|
||||||
def listeBillets():
|
def listeBillets():
|
||||||
"""Page liste des billets non traités et formulaire recherche d'un billet"""
|
"""Page liste des billets non traités et formulaire recherche d'un billet"""
|
||||||
|
@ -41,9 +41,12 @@ import flask
|
|||||||
from flask import url_for
|
from flask import url_for
|
||||||
from flask import current_app, g, request
|
from flask import current_app, g, request
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
from werkzeug.utils import redirect
|
||||||
|
|
||||||
from config import Config
|
from config import Config
|
||||||
|
|
||||||
|
from app import db
|
||||||
|
from app import models
|
||||||
from app.auth.models import User
|
from app.auth.models import User
|
||||||
|
|
||||||
from app.decorators import (
|
from app.decorators import (
|
||||||
@ -334,7 +337,36 @@ sco_publish(
|
|||||||
Permission.ScoChangeFormation,
|
Permission.ScoChangeFormation,
|
||||||
methods=["GET", "POST"],
|
methods=["GET", "POST"],
|
||||||
)
|
)
|
||||||
sco_publish("/ue_list", sco_edit_ue.ue_table, Permission.ScoView)
|
|
||||||
|
|
||||||
|
@bp.route("/ue_list") # backward compat
|
||||||
|
@bp.route("/ue_table")
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.ScoView)
|
||||||
|
@scodoc7func
|
||||||
|
def ue_table(formation_id=None, msg=""):
|
||||||
|
return sco_edit_ue.ue_table(formation_id=formation_id, msg=msg)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/ue_set_internal", methods=["GET", "POST"])
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.ScoChangeFormation)
|
||||||
|
@scodoc7func
|
||||||
|
def ue_set_internal(ue_id):
|
||||||
|
""""""
|
||||||
|
ue = models.formations.NotesUE.query.get(ue_id)
|
||||||
|
if not ue:
|
||||||
|
raise ScoValueError("invalid ue_id")
|
||||||
|
ue.is_external = False
|
||||||
|
db.session.add(ue)
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(
|
||||||
|
url_for(
|
||||||
|
"notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=ue.formation_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
sco_publish("/ue_sharing_code", sco_edit_ue.ue_sharing_code, Permission.ScoView)
|
sco_publish("/ue_sharing_code", sco_edit_ue.ue_sharing_code, Permission.ScoView)
|
||||||
sco_publish(
|
sco_publish(
|
||||||
"/edit_ue_set_code_apogee",
|
"/edit_ue_set_code_apogee",
|
||||||
|
@ -363,6 +363,8 @@ def search_etud_by_name():
|
|||||||
@scodoc7func
|
@scodoc7func
|
||||||
def etud_info(etudid=None, format="xml"):
|
def etud_info(etudid=None, format="xml"):
|
||||||
"Donne les informations sur un etudiant"
|
"Donne les informations sur un etudiant"
|
||||||
|
if not format in ("xml", "json"):
|
||||||
|
raise ScoValueError("format demandé non supporté par cette fonction.")
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
args = sco_etud.make_etud_args(etudid=etudid)
|
args = sco_etud.make_etud_args(etudid=etudid)
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
@ -413,12 +415,10 @@ def etud_info(etudid=None, format="xml"):
|
|||||||
"codelycee",
|
"codelycee",
|
||||||
"date_naissance_iso",
|
"date_naissance_iso",
|
||||||
):
|
):
|
||||||
d[a] = scu.quote_xml_attr(etud[a])
|
d[a] = etud[a] # ne pas quoter car ElementTree.tostring quote déjà
|
||||||
d["civilite"] = scu.quote_xml_attr(
|
d["civilite"] = etud["civilite_str"] # exception: ne sort pas la civilite brute
|
||||||
etud["civilite_str"]
|
|
||||||
) # exception: ne sort pas la civilite brute
|
|
||||||
d["sexe"] = d["civilite"] # backward compat pour anciens clients
|
d["sexe"] = d["civilite"] # backward compat pour anciens clients
|
||||||
d["photo_url"] = scu.quote_xml_attr(sco_photos.etud_photo_url(etud))
|
d["photo_url"] = sco_photos.etud_photo_url(etud)
|
||||||
|
|
||||||
sem = etud["cursem"]
|
sem = etud["cursem"]
|
||||||
if sem:
|
if sem:
|
||||||
@ -429,10 +429,8 @@ def etud_info(etudid=None, format="xml"):
|
|||||||
"formsemestre_id": sem["formsemestre_id"],
|
"formsemestre_id": sem["formsemestre_id"],
|
||||||
"date_debut": ndb.DateDMYtoISO(sem["date_debut"]),
|
"date_debut": ndb.DateDMYtoISO(sem["date_debut"]),
|
||||||
"date_fin": ndb.DateDMYtoISO(sem["date_fin"]),
|
"date_fin": ndb.DateDMYtoISO(sem["date_fin"]),
|
||||||
"etat": scu.quote_xml_attr(sem["ins"]["etat"]),
|
"etat": sem["ins"]["etat"],
|
||||||
"groupes": scu.quote_xml_attr(
|
"groupes": etud["groupes"], # slt pour semestre courant
|
||||||
etud["groupes"]
|
|
||||||
), # slt pour semestre courant
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
@ -444,12 +442,14 @@ def etud_info(etudid=None, format="xml"):
|
|||||||
"formsemestre_id": sem["formsemestre_id"],
|
"formsemestre_id": sem["formsemestre_id"],
|
||||||
"date_debut": ndb.DateDMYtoISO(sem["date_debut"]),
|
"date_debut": ndb.DateDMYtoISO(sem["date_debut"]),
|
||||||
"date_fin": ndb.DateDMYtoISO(sem["date_fin"]),
|
"date_fin": ndb.DateDMYtoISO(sem["date_fin"]),
|
||||||
"etat": scu.quote_xml_attr(sem["ins"]["etat"]),
|
"etat": sem["ins"]["etat"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
log("etud_info (%gs)" % (time.time() - t0))
|
log("etud_info (%gs)" % (time.time() - t0))
|
||||||
return scu.sendResult(d, name="etudiant", format=format, force_outer_xml_tag=False)
|
return scu.sendResult(
|
||||||
|
d, name="etudiant", format=format, force_outer_xml_tag=False, quote_xml=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# -------------------------- FICHE ETUDIANT --------------------------
|
# -------------------------- FICHE ETUDIANT --------------------------
|
||||||
|
@ -33,7 +33,7 @@ class Config:
|
|||||||
# evite confusion avec le log nginx scodoc_error.log:
|
# evite confusion avec le log nginx scodoc_error.log:
|
||||||
SCODOC_ERR_FILE = os.path.join(SCODOC_VAR_DIR, "log", "scodoc_exc.log")
|
SCODOC_ERR_FILE = os.path.join(SCODOC_VAR_DIR, "log", "scodoc_exc.log")
|
||||||
#
|
#
|
||||||
MAX_CONTENT_LENGTH = 10 * 1024 * 1024 # Flask uploads
|
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # Flask uploads (16Mo, en ligne avec nginx)
|
||||||
|
|
||||||
# STATIC_URL_PATH = "/ScoDoc/static"
|
# STATIC_URL_PATH = "/ScoDoc/static"
|
||||||
# static_folder = "stat"
|
# static_folder = "stat"
|
||||||
|
45
scodoc.py
45
scodoc.py
@ -13,6 +13,7 @@ import sys
|
|||||||
import click
|
import click
|
||||||
import flask
|
import flask
|
||||||
from flask.cli import with_appcontext
|
from flask.cli import with_appcontext
|
||||||
|
from flask.templating import render_template
|
||||||
|
|
||||||
from app import create_app, cli, db
|
from app import create_app, cli, db
|
||||||
from app import initialize_scodoc_database
|
from app import initialize_scodoc_database
|
||||||
@ -323,6 +324,50 @@ def migrate_scodoc7_dept_archive(dept: str): # migrate-scodoc7-dept-archive
|
|||||||
tools.migrate_scodoc7_dept_archive(dept)
|
tools.migrate_scodoc7_dept_archive(dept)
|
||||||
|
|
||||||
|
|
||||||
|
@app.cli.command()
|
||||||
|
@click.argument("formsemestre_id", type=click.INT)
|
||||||
|
@click.argument("xlsfile", type=click.File("rb"))
|
||||||
|
@click.argument("zipfile", type=click.File("rb"))
|
||||||
|
def photos_import_files(formsemestre_id: int, xlsfile: str, zipfile: str):
|
||||||
|
import app as mapp
|
||||||
|
from app.scodoc import sco_trombino, sco_photos
|
||||||
|
from app.scodoc import notesdb as ndb
|
||||||
|
from flask_login import login_user
|
||||||
|
from app.auth.models import get_super_admin
|
||||||
|
|
||||||
|
sem = mapp.models.formsemestre.FormSemestre.query.get(formsemestre_id)
|
||||||
|
if not sem:
|
||||||
|
sys.stderr.write("photos-import-files: numéro de semestre invalide\n")
|
||||||
|
return 2
|
||||||
|
|
||||||
|
with app.test_request_context():
|
||||||
|
mapp.set_sco_dept(sem.departement.acronym)
|
||||||
|
admin_user = get_super_admin()
|
||||||
|
login_user(admin_user)
|
||||||
|
|
||||||
|
def callback(etud, data, filename):
|
||||||
|
sco_photos.store_photo(etud, data)
|
||||||
|
|
||||||
|
(
|
||||||
|
ignored_zipfiles,
|
||||||
|
unmatched_files,
|
||||||
|
stored_etud_filename,
|
||||||
|
) = sco_trombino.zip_excel_import_files(
|
||||||
|
xlsfile=xlsfile,
|
||||||
|
zipfile=zipfile,
|
||||||
|
callback=callback,
|
||||||
|
filename_title="fichier_photo",
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
render_template(
|
||||||
|
"scolar/photos_import_files.txt",
|
||||||
|
ignored_zipfiles=ignored_zipfiles,
|
||||||
|
unmatched_files=unmatched_files,
|
||||||
|
stored_etud_filename=stored_etud_filename,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.cli.command()
|
@app.cli.command()
|
||||||
@with_appcontext
|
@with_appcontext
|
||||||
def clear_cache(): # clear-cache
|
def clear_cache(): # clear-cache
|
||||||
|
@ -324,7 +324,7 @@ class ScoFake(object):
|
|||||||
formation (dict), liste d'ue (dicts), liste de modules.
|
formation (dict), liste d'ue (dicts), liste de modules.
|
||||||
"""
|
"""
|
||||||
f = self.create_formation(acronyme=acronyme, titre=titre)
|
f = self.create_formation(acronyme=acronyme, titre=titre)
|
||||||
ue_list = []
|
ues = []
|
||||||
mod_list = []
|
mod_list = []
|
||||||
for semestre_id in range(1, nb_semestre + 1):
|
for semestre_id in range(1, nb_semestre + 1):
|
||||||
for n in range(1, nb_ue_per_semestre + 1):
|
for n in range(1, nb_ue_per_semestre + 1):
|
||||||
@ -333,7 +333,7 @@ class ScoFake(object):
|
|||||||
acronyme="TSU%s%s" % (semestre_id, n),
|
acronyme="TSU%s%s" % (semestre_id, n),
|
||||||
titre="ue test %s%s" % (semestre_id, n),
|
titre="ue test %s%s" % (semestre_id, n),
|
||||||
)
|
)
|
||||||
ue_list.append(ue)
|
ues.append(ue)
|
||||||
mat = self.create_matiere(ue_id=ue["ue_id"], titre="matière test")
|
mat = self.create_matiere(ue_id=ue["ue_id"], titre="matière test")
|
||||||
for _ in range(nb_module_per_ue):
|
for _ in range(nb_module_per_ue):
|
||||||
mod = self.create_module(
|
mod = self.create_module(
|
||||||
@ -346,7 +346,7 @@ class ScoFake(object):
|
|||||||
formation_id=f["formation_id"], # faiblesse de l'API
|
formation_id=f["formation_id"], # faiblesse de l'API
|
||||||
)
|
)
|
||||||
mod_list.append(mod)
|
mod_list.append(mod)
|
||||||
return f, ue_list, mod_list
|
return f, ues, mod_list
|
||||||
|
|
||||||
def setup_formsemestre(
|
def setup_formsemestre(
|
||||||
self,
|
self,
|
||||||
|
@ -339,6 +339,10 @@ def test_import_formation(test_client):
|
|||||||
f = sco_formations.formation_import_xml(doc)
|
f = sco_formations.formation_import_xml(doc)
|
||||||
assert len(f) == 3 # 3-uple
|
assert len(f) == 3 # 3-uple
|
||||||
formation_id = f[0]
|
formation_id = f[0]
|
||||||
|
# --- Vérification des UE
|
||||||
|
ues = sco_edit_ue.ue_list({"formation_id": formation_id})
|
||||||
|
assert len(ues) == 10
|
||||||
|
assert all(not ue["is_external"] for ue in ues) # aucune UE externe dans le XML
|
||||||
# --- Mise en place de 4 semestres
|
# --- Mise en place de 4 semestres
|
||||||
sems = [
|
sems = [
|
||||||
G.create_formsemestre(
|
G.create_formsemestre(
|
||||||
|
Loading…
Reference in New Issue
Block a user