diff --git a/app/models/etudiants.py b/app/models/etudiants.py
index f4a9318f18..35c3fc8582 100644
--- a/app/models/etudiants.py
+++ b/app/models/etudiants.py
@@ -43,8 +43,8 @@ class Identite(db.Model):
boursier = db.Column(db.Boolean()) # True si boursier ('O' en ScoDoc7)
photo_filename = db.Column(db.Text())
# Codes INE et NIP pas unique car le meme etud peut etre ds plusieurs dept
- code_nip = db.Column(db.Text())
- code_ine = db.Column(db.Text())
+ code_nip = db.Column(db.Text(), index=True)
+ code_ine = db.Column(db.Text(), index=True)
# Ancien id ScoDoc7 pour les migrations de bases anciennes
# ne pas utiliser après migrate_scodoc7_dept_archives
scodoc7_id = db.Column(db.Text(), nullable=True)
diff --git a/app/scodoc/TrivialFormulator.py b/app/scodoc/TrivialFormulator.py
index acbe8d8ca9..722b7ec9a6 100644
--- a/app/scodoc/TrivialFormulator.py
+++ b/app/scodoc/TrivialFormulator.py
@@ -9,6 +9,12 @@
v 1.3 (python3)
"""
import html
+import re
+
+# re validant dd/mm/yyyy
+DMY_REGEXP = re.compile(
+ r"^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[13-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$"
+)
def TrivialFormulator(
@@ -66,8 +72,8 @@ def TrivialFormulator(
HTML elements:
input_type : 'text', 'textarea', 'password',
'radio', 'menu', 'checkbox',
- 'hidden', 'separator', 'file', 'date', 'boolcheckbox',
- 'text_suggest'
+ 'hidden', 'separator', 'file', 'date', 'datedmy' (avec validation),
+ 'boolcheckbox', 'text_suggest'
(default text)
size : text field width
rows, cols: textarea geometry
@@ -243,6 +249,8 @@ class TF(object):
"Le champ '%s' doit être renseigné" % descr.get("title", field)
)
ok = 0
+ elif val == "" or val == None:
+ continue # allowed empty field, skip
# type
typ = descr.get("type", "string")
if val != "" and val != None:
@@ -300,6 +308,10 @@ class TF(object):
if not descr["validator"](val, field):
msg.append("valeur invalide (%s) pour le champ '%s'" % (val, field))
ok = 0
+ elif descr.get("input_type") == "datedmy":
+ if not DMY_REGEXP.match(val):
+ msg.append("valeur invalide (%s) pour la date '%s'" % (val, field))
+ ok = 0
# boolean checkbox
if descr.get("input_type", None) == "boolcheckbox":
if int(val):
@@ -564,7 +576,9 @@ class TF(object):
''
% (field, size, values[field], attribs)
)
- elif input_type == "date": # JavaScript widget for date input
+ elif (
+ input_type == "date" or input_type == "datedmy"
+ ): # JavaScript widget for date input
lem.append(
''
% (field, values[field])
diff --git a/app/scodoc/html_sidebar.py b/app/scodoc/html_sidebar.py
index b3bf18a363..53efbfc955 100644
--- a/app/scodoc/html_sidebar.py
+++ b/app/scodoc/html_sidebar.py
@@ -35,13 +35,14 @@ from flask_login import current_user
import app.scodoc.sco_utils as scu
from app.scodoc import sco_preferences
from app.scodoc.sco_permissions import Permission
+from sco_version import SCOVERSION
def sidebar_common():
"partie commune à toutes les sidebar"
home_link = url_for("scodoc.index", scodoc_dept=g.scodoc_dept)
H = [
- f"""ScoDoc 9.1
+ f"""ScoDoc {SCOVERSION}
Accueil
+ Vous utilisez un lien invalide, qui correspond probablement
+ à une ancienne version du logiciel.
+ Au besoin, mettre à jour vos marque-pages.
+
Si le problème persiste, merci de contacter l'assistance + via la liste de diffusion Notes + ou le salon Discord. +
+Message serveur: {msg}
+ """ + super().__init__(msg) + + class ScoGenError(ScoException): "exception avec affichage d'une page explicative ad-hoc" diff --git a/app/scodoc/sco_formsemestre.py b/app/scodoc/sco_formsemestre.py index 629da14565..f51afd7914 100644 --- a/app/scodoc/sco_formsemestre.py +++ b/app/scodoc/sco_formsemestre.py @@ -27,21 +27,22 @@ """Operations de base sur les formsemestres """ -from app.scodoc.sco_exceptions import ScoValueError -import time from operator import itemgetter +import time from flask import g, request import app +from app import log from app.models import Departement + from app.scodoc import sco_codes_parcours from app.scodoc import sco_cache from app.scodoc import sco_formations from app.scodoc import sco_preferences from app.scodoc.gen_tables import GenTable -from app import log from app.scodoc.sco_codes_parcours import NO_SEMESTRE_ID +from app.scodoc.sco_exceptions import ScoValueError, ScoInvalidIdType from app.scodoc.sco_vdi import ApoEtapeVDI import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu @@ -97,7 +98,7 @@ def get_formsemestre(formsemestre_id, raise_soft_exc=False): if formsemestre_id in g.stored_get_formsemestre: return g.stored_get_formsemestre[formsemestre_id] if not isinstance(formsemestre_id, int): - raise ValueError("formsemestre_id must be an integer !") + raise ScoInvalidIdType("formsemestre_id must be an integer !") sems = do_formsemestre_list(args={"formsemestre_id": formsemestre_id}) if not sems: log("get_formsemestre: invalid formsemestre_id (%s)" % formsemestre_id) diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index 59e6d924c9..e6ac86e3a8 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -253,7 +253,7 @@ def do_formsemestre_createwithmodules(edit=False): "date_debut", { "title": "Date de début", # j/m/a - "input_type": "date", + "input_type": "datedmy", "explanation": "j/m/a", "size": 9, "allow_null": False, @@ -263,7 +263,7 @@ def do_formsemestre_createwithmodules(edit=False): "date_fin", { "title": "Date de fin", # j/m/a - "input_type": "date", + "input_type": "datedmy", "explanation": "j/m/a", "size": 9, "allow_null": False, @@ -912,7 +912,7 @@ def formsemestre_clone(formsemestre_id): "date_debut", { "title": "Date de début", # j/m/a - "input_type": "date", + "input_type": "datedmy", "explanation": "j/m/a", "size": 9, "allow_null": False, @@ -922,7 +922,7 @@ def formsemestre_clone(formsemestre_id): "date_fin", { "title": "Date de fin", # j/m/a - "input_type": "date", + "input_type": "datedmy", "explanation": "j/m/a", "size": 9, "allow_null": False, diff --git a/app/scodoc/sco_formsemestre_exterieurs.py b/app/scodoc/sco_formsemestre_exterieurs.py index b3d601de77..7e4e3da891 100644 --- a/app/scodoc/sco_formsemestre_exterieurs.py +++ b/app/scodoc/sco_formsemestre_exterieurs.py @@ -154,7 +154,7 @@ def formsemestre_ext_create_form(etudid, formsemestre_id): "date_debut", { "title": "Date de début", # j/m/a - "input_type": "date", + "input_type": "datedmy", "explanation": "j/m/a (peut être approximatif)", "size": 9, "allow_null": False, @@ -164,7 +164,7 @@ def formsemestre_ext_create_form(etudid, formsemestre_id): "date_fin", { "title": "Date de fin", # j/m/a - "input_type": "date", + "input_type": "datedmy", "explanation": "j/m/a (peut être approximatif)", "size": 9, "allow_null": False, diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index 0ecc1a2afb..8248491af1 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -284,7 +284,9 @@ def get_group_infos(group_id, etat=None): # was _getlisteetud cnx = ndb.GetDBConnexion() group = get_group(group_id) - sem = sco_formsemestre.get_formsemestre(group["formsemestre_id"]) + sem = sco_formsemestre.get_formsemestre( + group["formsemestre_id"], raise_soft_exc=True + ) members = get_group_members(group_id, etat=etat) # add human readable description of state: diff --git a/app/scodoc/sco_moduleimpl_status.py b/app/scodoc/sco_moduleimpl_status.py index 735ece8a05..904c74c9ae 100644 --- a/app/scodoc/sco_moduleimpl_status.py +++ b/app/scodoc/sco_moduleimpl_status.py @@ -36,6 +36,7 @@ from app.auth.models import User from app.models import ModuleImpl from app.models.evaluations import Evaluation import app.scodoc.sco_utils as scu +from app.scodoc.sco_exceptions import ScoInvalidIdType from app.scodoc.sco_permissions import Permission from app.scodoc import html_sco_header @@ -184,6 +185,8 @@ def _ue_coefs_html(coefs_lst) -> str: def moduleimpl_status(moduleimpl_id=None, partition_id=None): """Tableau de bord module (liste des evaluations etc)""" + if not isinstance(moduleimpl_id, int): + raise ScoInvalidIdType("moduleimpl_id must be an integer !") modimpl = ModuleImpl.query.get_or_404(moduleimpl_id) M = modimpl.to_dict() formsemestre_id = M["formsemestre_id"] diff --git a/app/scodoc/sco_permissions_check.py b/app/scodoc/sco_permissions_check.py index 5c166dbe3a..fe21b16723 100644 --- a/app/scodoc/sco_permissions_check.py +++ b/app/scodoc/sco_permissions_check.py @@ -168,7 +168,7 @@ def can_change_groups(formsemestre_id): "Vrai si l'utilisateur peut changer les groupes dans ce semestre" from app.scodoc import sco_formsemestre - sem = sco_formsemestre.get_formsemestre(formsemestre_id) + sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True) if not sem["etat"]: return False # semestre verrouillé if current_user.has_permission(Permission.ScoEtudChangeGroups): diff --git a/app/static/js/releve-but.js b/app/static/js/releve-but.js index 5042031b37..521e97da76 100644 --- a/app/static/js/releve-but.js +++ b/app/static/js/releve-but.js @@ -1,42 +1,49 @@ /* Module par Seb. L. */ class releveBUT extends HTMLElement { - constructor(){ + constructor() { super(); - this.shadow = this.attachShadow({mode: 'open'}); + this.shadow = this.attachShadow({ mode: 'open' }); /* Config par defaut */ this.config = { showURL: true }; - + /* Template du module */ this.shadow.innerHTML = this.template(); - + /* Style du module */ const styles = document.createElement('link'); styles.setAttribute('rel', 'stylesheet'); styles.setAttribute('href', '/ScoDoc/static/css/releve-but.css'); - this.shadow.appendChild(styles); + /* variante "ScoDoc" ou "Passerelle" (ENT) ? */ + if (location.href.split("/")[3] == "ScoDoc") { /* un peu osé... */ + styles.setAttribute('href', '/ScoDoc/static/css/releve-but.css'); + } else { + // Passerelle + styles.setAttribute('href', '/assets/styles/releve-but.css'); + } + this.shadow.appendChild(styles); } listeOnOff() { this.parentElement.parentElement.classList.toggle("listeOff"); - this.parentElement.parentElement.querySelectorAll(".moduleOnOff").forEach(e=>{ + this.parentElement.parentElement.querySelectorAll(".moduleOnOff").forEach(e => { e.classList.remove("moduleOnOff") }) } - moduleOnOff(){ + moduleOnOff() { this.parentElement.classList.toggle("moduleOnOff"); } - goTo(){ + goTo() { let module = this.dataset.module; this.parentElement.parentElement.parentElement.parentElement.querySelector("#Module_" + module).scrollIntoView(); } - set setConfig(config){ + set setConfig(config) { this.config.showURL = config.showURL ?? this.config.showURL; } - set showData(data) { + set showData(data) { this.showInformations(data); this.showSemestre(data); this.showSynthese(data); @@ -46,7 +53,7 @@ class releveBUT extends HTMLElement { this.shadow.querySelectorAll(".CTA_Liste").forEach(e => { e.addEventListener("click", this.listeOnOff) - }) + }) this.shadow.querySelectorAll(".ue, .module").forEach(e => { e.addEventListener("click", this.moduleOnOff) }) @@ -57,7 +64,7 @@ class releveBUT extends HTMLElement { this.shadow.children[0].classList.add("ready"); } - template(){ + template() { return `