diff --git a/app/auth/models.py b/app/auth/models.py
index 1330b9b7dd..899a5ab27e 100644
--- a/app/auth/models.py
+++ b/app/auth/models.py
@@ -12,6 +12,7 @@ from typing import Optional
import cracklib # pylint: disable=import-error
+import flask
from flask import current_app, g
from flask_login import UserMixin, AnonymousUserMixin
@@ -88,7 +89,8 @@ class User(UserMixin, db.Model):
"""
cas_last_login = db.Column(db.DateTime, nullable=True)
"""date du dernier login via CAS"""
-
+ edt_id = db.Column(db.Text(), index=True, nullable=True)
+ "identifiant emplois du temps (unicité non imposée)"
password_hash = db.Column(db.String(128))
password_scodoc7 = db.Column(db.String(42))
last_seen = db.Column(db.DateTime, default=datetime.utcnow)
@@ -172,7 +174,8 @@ class User(UserMixin, db.Model):
return False
# if CAS activated and forced, allow only super-user and users with cas_allow_scodoc_login
- if ScoDocSiteConfig.is_cas_enabled() and ScoDocSiteConfig.get("cas_force"):
+ cas_enabled = ScoDocSiteConfig.is_cas_enabled()
+ if cas_enabled and ScoDocSiteConfig.get("cas_force"):
if (not self.is_administrator()) and not self.cas_allow_scodoc_login:
return False
@@ -182,7 +185,18 @@ class User(UserMixin, db.Model):
return self._migrate_scodoc7_password(password)
return False
- return check_password_hash(self.password_hash, password)
+ password_ok = check_password_hash(self.password_hash, password)
+ if password_ok and cas_enabled and flask.session.get("CAS_EDT_ID"):
+ # essaie de récupérer l'edt_id s'il est présent
+ # cet ID peut être renvoyé par le CAS et extrait par ScoDoc
+ # via l'expression `cas_edt_id_from_xml_regexp`
+ # voir flask_cas.routing
+ edt_id = flask.session.get("CAS_EDT_ID")
+ log(f"Storing edt_id for {self.user_name}: '{edt_id}'")
+ self.edt_id = edt_id
+ db.session.add(self)
+ db.session.commit()
+ return password_ok
def _migrate_scodoc7_password(self, password) -> bool:
"""After migration, rehash password."""
diff --git a/app/models/config.py b/app/models/config.py
index 60ce884b4d..fe76bec888 100644
--- a/app/models/config.py
+++ b/app/models/config.py
@@ -4,8 +4,8 @@
"""
import json
-import urllib.parse
import re
+import urllib.parse
from flask import flash
from app import current_app, db, log
@@ -13,8 +13,6 @@ from app.comp import bonus_spo
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc import sco_utils as scu
-from datetime import time
-
from app.scodoc.codes_cursus import (
ABAN,
ABL,
@@ -105,6 +103,7 @@ class ScoDocSiteConfig(db.Model):
"cas_validate_route": str,
"cas_attribute_id": str,
"cas_uid_from_mail_regexp": str,
+ "cas_edt_id_from_xml_regexp": str,
# Assiduité
"morning_time": str,
"lunch_time": str,
@@ -174,7 +173,7 @@ class ScoDocSiteConfig(db.Model):
klass = bonus_spo.get_bonus_class_dict().get(class_name)
if klass is None:
flash(
- f"""Fonction de calcul bonus sport inexistante: {class_name}.
+ f"""Fonction de calcul bonus sport inexistante: {class_name}.
Changez là ou contactez votre administrateur local."""
)
return klass
diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py
index 1d99342c06..d020eb92a4 100644
--- a/app/models/formsemestre.py
+++ b/app/models/formsemestre.py
@@ -64,6 +64,8 @@ class FormSemestre(db.Model):
titre = db.Column(db.Text(), nullable=False)
date_debut = db.Column(db.Date(), nullable=False)
date_fin = db.Column(db.Date(), nullable=False)
+ edt_id: str | None = db.Column(db.Text(), index=True, nullable=True)
+ "identifiant emplois du temps (unicité non imposée)"
etat = db.Column(db.Boolean(), nullable=False, default=True, server_default="true")
"False si verrouillé"
modalite = db.Column(
diff --git a/app/models/groups.py b/app/models/groups.py
index a4a5792f6f..8d445eee4f 100644
--- a/app/models/groups.py
+++ b/app/models/groups.py
@@ -180,7 +180,7 @@ class Partition(db.Model):
"Crée un groupe dans cette partition"
if not self.formsemestre.can_change_groups():
raise AccessDenied(
- """Vous n'avez pas le droit d'effectuer cette opération,
+ """Vous n'avez pas le droit d'effectuer cette opération,
ou bien le semestre est verrouillé !"""
)
if group_name:
@@ -213,10 +213,12 @@ class GroupDescr(db.Model):
id = db.Column(db.Integer, primary_key=True)
group_id = db.synonym("id")
partition_id = db.Column(db.Integer, db.ForeignKey("partition.id"))
- # "A", "C2", ... (NULL for 'all'):
group_name = db.Column(db.String(GROUPNAME_STR_LEN))
- # Numero = ordre de presentation
+ """nom du groupe: "A", "C2", ... (NULL for 'all')"""
+ edt_id: str | None = db.Column(db.Text(), index=True, nullable=True)
+ "identifiant emplois du temps (unicité non imposée)"
numero = db.Column(db.Integer, nullable=False, default=0)
+ "Numero = ordre de presentation"
etuds = db.relationship(
"Identite",
@@ -272,6 +274,40 @@ class GroupDescr(db.Model):
return False
return True
+ def set_name(
+ self, group_name: str, edt_id: str | bool = False, dest_url: str = None
+ ):
+ """Set group name, and optionally edt_id.
+ Check permission and invalidate caches. Commit session.
+ dest_url is used for error messages.
+ """
+ if not self.partition.formsemestre.can_change_groups():
+ raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
+ if self.group_name is None:
+ raise ValueError("can't set a name to default group")
+
+ if group_name:
+ group_name = group_name.strip()
+ if not group_name:
+ raise ScoValueError("nom de groupe vide !", dest_url=dest_url)
+ if group_name != self.group_name and not GroupDescr.check_name(
+ self.partition, group_name
+ ):
+ raise ScoValueError(
+ "Le nom de groupe existe déjà dans la partition", dest_url=dest_url
+ )
+
+ self.group_name = group_name
+ if edt_id is not False:
+ if isinstance(edt_id, str):
+ edt_id = edt_id.strip() or None
+ self.edt_id = edt_id
+ db.session.add(self)
+ db.session.commit()
+ sco_cache.invalidate_formsemestre(
+ formsemestre_id=self.partition.formsemestre_id
+ )
+
def remove_etud(self, etud: "Identite"):
"Enlève l'étudiant de ce groupe s'il en fait partie (ne fait rien sinon)"
if etud in self.etuds:
diff --git a/app/models/modules.py b/app/models/modules.py
index b4aa00ad89..fe297ebd78 100644
--- a/app/models/modules.py
+++ b/app/models/modules.py
@@ -34,8 +34,10 @@ class Module(db.Model):
# note: en APC, le semestre qui fait autorité est celui de l'UE
semestre_id = db.Column(db.Integer, nullable=False, default=1, server_default="1")
numero = db.Column(db.Integer, nullable=False, default=0) # ordre de présentation
- # id de l'element pedagogique Apogee correspondant:
code_apogee = db.Column(db.String(APO_CODE_STR_LEN))
+ "id de l'element pedagogique Apogee correspondant"
+ edt_id: str | None = db.Column(db.Text(), index=True, nullable=True)
+ "identifiant emplois du temps (unicité non imposée)"
# Type: ModuleType.STANDARD, MALUS, RESSOURCE, SAE (enum)
module_type = db.Column(db.Integer, nullable=False, default=0, server_default="0")
# Relations:
diff --git a/app/scodoc/sco_edt_cal.py b/app/scodoc/sco_edt_cal.py
index 613428ed16..2174f25805 100644
--- a/app/scodoc/sco_edt_cal.py
+++ b/app/scodoc/sco_edt_cal.py
@@ -34,16 +34,13 @@ XXX incompatible avec les ics HyperPlanning Paris 13 (était pour GPU).
"""
import icalendar
-import pprint
-import traceback
+
import urllib
import app.scodoc.sco_utils as scu
from app import log
-from app.scodoc import html_sco_header
from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups
-from app.scodoc import sco_groups_view
from app.scodoc import sco_preferences
diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py
index 6a4d77e841..46b650b72c 100644
--- a/app/scodoc/sco_groups.py
+++ b/app/scodoc/sco_groups.py
@@ -42,7 +42,7 @@ from app import cache, db, log
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models import FormSemestre, Identite, Scolog
-from app.models import GROUPNAME_STR_LEN, SHORT_STR_LEN
+from app.models import SHORT_STR_LEN
from app.models.groups import GroupDescr, Partition
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
@@ -136,7 +136,7 @@ def get_partitions_list(formsemestre_id, with_default=True) -> list[dict]:
partitions = ndb.SimpleDictFetch(
"""SELECT p.id AS partition_id, p.*
FROM partition p
- WHERE formsemestre_id=%(formsemestre_id)s
+ WHERE formsemestre_id=%(formsemestre_id)s
ORDER BY numero""",
{"formsemestre_id": formsemestre_id},
)
@@ -258,14 +258,14 @@ def get_group_members(group_id, etat=None):
Trié par nom_usuel (ou nom) puis prénom
"""
req = """SELECT i.id as etudid, i.*, a.*, gm.*, ins.etat
- FROM identite i, adresse a, group_membership gm,
- group_descr gd, partition p, notes_formsemestre_inscription ins
- WHERE i.id = gm.etudid
- and a.etudid = i.id
- and ins.etudid = i.id
- and ins.formsemestre_id = p.formsemestre_id
- and p.id = gd.partition_id
- and gd.id = gm.group_id
+ FROM identite i, adresse a, group_membership gm,
+ group_descr gd, partition p, notes_formsemestre_inscription ins
+ WHERE i.id = gm.etudid
+ and a.etudid = i.id
+ and ins.etudid = i.id
+ and ins.formsemestre_id = p.formsemestre_id
+ and p.id = gd.partition_id
+ and gd.id = gm.group_id
and gm.group_id=%(group_id)s
"""
if etat is not None:
@@ -350,12 +350,12 @@ def get_etud_groups(etudid: int, formsemestre_id: int, exclude_default=False):
"""Infos sur groupes de l'etudiant dans ce semestre
[ group + partition_name ]
"""
- req = """SELECT p.id AS partition_id, p.*,
+ req = """SELECT p.id AS partition_id, p.*,
g.id AS group_id, g.numero as group_numero, g.group_name
- FROM group_descr g, partition p, group_membership gm
- WHERE gm.etudid=%(etudid)s
- and gm.group_id = g.id
- and g.partition_id = p.id
+ FROM group_descr g, partition p, group_membership gm
+ WHERE gm.etudid=%(etudid)s
+ and gm.group_id = g.id
+ and g.partition_id = p.id
and p.formsemestre_id = %(formsemestre_id)s
"""
if exclude_default:
@@ -393,7 +393,7 @@ def formsemestre_get_etud_groupnames(formsemestre_id, attr="group_name"):
p.id AS partition_id,
gd.group_name,
gd.id AS group_id
- FROM
+ FROM
notes_formsemestre_inscription i,
partition p,
group_descr gd,
@@ -967,8 +967,8 @@ def edit_partition_form(formsemestre_id=None):
for p in partitions:
if p["partition_name"] is not None:
H.append(
- f"""
| {suppricon} | """
)
@@ -1299,85 +1299,6 @@ def partition_set_name(partition_id, partition_name, redirect=1):
)
-def group_set_name(group: GroupDescr, group_name: str, redirect=True):
- """Set group name"""
- if not group.partition.formsemestre.can_change_groups():
- raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
- if group.group_name is None:
- raise ValueError("can't set a name to default group")
- destination = url_for(
- "scolar.affect_groups",
- scodoc_dept=g.scodoc_dept,
- partition_id=group.partition_id,
- )
- if group_name:
- group_name = group_name.strip()
- if not group_name:
- raise ScoValueError("nom de groupe vide !", dest_url=destination)
- if not GroupDescr.check_name(group.partition, group_name):
- raise ScoValueError(
- "Le nom de groupe existe déjà dans la partition", dest_url=destination
- )
-
- redirect = int(redirect)
- group.group_name = group_name
- db.session.add(group)
- db.session.commit()
- sco_cache.invalidate_formsemestre(formsemestre_id=group.partition.formsemestre_id)
-
- # redirect to partition edit page:
- if redirect:
- return flask.redirect(destination)
-
-
-def group_rename(group_id):
- """Form to rename a group"""
- group = GroupDescr.query.get_or_404(group_id)
- formsemestre_id = group.partition.formsemestre_id
- formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
- if not formsemestre.can_change_groups():
- raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
- H = [f"Renommer un groupe de {group.partition.partition_name or '-'}"]
- tf = TrivialFormulator(
- request.base_url,
- scu.get_request_args(),
- (
- ("group_id", {"default": group_id, "input_type": "hidden"}),
- (
- "group_name",
- {
- "title": "Nouveau nom",
- "default": group.group_name,
- "size": 12,
- "allow_null": False,
- "validator": lambda val, _: len(val) < GROUPNAME_STR_LEN,
- },
- ),
- ),
- submitlabel="Renommer",
- cancelbutton="Annuler",
- )
- if tf[0] == 0:
- return (
- html_sco_header.sco_header()
- + "\n".join(H)
- + "\n"
- + tf[1]
- + html_sco_header.sco_footer()
- )
- elif tf[0] == -1:
- return flask.redirect(
- url_for(
- "scolar.affect_groups",
- scodoc_dept=g.scodoc_dept,
- partition_id=group.partition_id,
- )
- )
- else:
- # form submission
- return group_set_name(group, tf[2]["group_name"])
-
-
def groups_auto_repartition(partition: Partition):
"""Réparti les etudiants dans des groupes dans une partition, en respectant le niveau
et la mixité.
@@ -1570,7 +1491,7 @@ def do_evaluation_listeetuds_groups(
return [] # no groups, so no students
rg = ["gm.group_id = '%(group_id)s'" % g for g in groups]
rq = """and Isem.etudid = gm.etudid
- and gd.partition_id = p.id
+ and gd.partition_id = p.id
and p.formsemestre_id = Isem.formsemestre_id
"""
r = rq + " AND (" + " or ".join(rg) + " )"
@@ -1583,9 +1504,9 @@ def do_evaluation_listeetuds_groups(
"SELECT distinct Im.etudid, Isem.etat FROM "
+ ", ".join(fromtables)
+ """ WHERE Isem.etudid = Im.etudid
- and Im.moduleimpl_id = M.id
- and Isem.formsemestre_id = M.formsemestre_id
- and E.moduleimpl_id = M.id
+ and Im.moduleimpl_id = M.id
+ and Isem.formsemestre_id = M.formsemestre_id
+ and E.moduleimpl_id = M.id
and E.id = %(evaluation_id)s
"""
)
@@ -1612,7 +1533,7 @@ def do_evaluation_listegroupes(evaluation_id, include_default=False):
cursor = cnx.cursor()
cursor.execute(
"""SELECT DISTINCT gd.id AS group_id
- FROM group_descr gd, group_membership gm, partition p,
+ FROM group_descr gd, group_membership gm, partition p,
notes_moduleimpl m, notes_evaluation e
WHERE gm.group_id = gd.id
and gd.partition_id = p.id
diff --git a/app/scodoc/sco_groups_edit.py b/app/scodoc/sco_groups_edit.py
index 4fd1e103cb..0a9173ec27 100644
--- a/app/scodoc/sco_groups_edit.py
+++ b/app/scodoc/sco_groups_edit.py
@@ -27,11 +27,15 @@
"""Formulaires gestion des groupes
"""
-from flask import render_template
+import flask
+from flask import flash, g, render_template, request, url_for
-from app.models import Partition
+from app.models import FormSemestre, GroupDescr, Partition
+from app.models import GROUPNAME_STR_LEN
from app.scodoc import html_sco_header
from app.scodoc.sco_exceptions import AccessDenied
+import app.scodoc.sco_utils as scu
+from app.scodoc.TrivialFormulator import TrivialFormulator
def affect_groups(partition_id):
@@ -59,3 +63,64 @@ def affect_groups(partition_id):
),
formsemestre_id=formsemestre.id,
)
+
+
+def group_rename(group_id):
+ """Form to rename a group"""
+ group: GroupDescr = GroupDescr.query.get_or_404(group_id)
+ formsemestre_id = group.partition.formsemestre_id
+ formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
+ if not formsemestre.can_change_groups():
+ raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
+ H = [f"Renommer un groupe de {group.partition.partition_name or '-'}"]
+ tf = TrivialFormulator(
+ request.base_url,
+ scu.get_request_args(),
+ (
+ ("group_id", {"default": group_id, "input_type": "hidden"}),
+ (
+ "group_name",
+ {
+ "title": "Nouveau nom",
+ "default": group.group_name,
+ "size": 12,
+ "allow_null": False,
+ "validator": lambda val, _: len(val) < GROUPNAME_STR_LEN,
+ "explanation": "doit être unique dans cette partition",
+ },
+ ),
+ (
+ "edt_id",
+ {
+ "title": "Id EDT",
+ "default": group.edt_id or "",
+ "size": 12,
+ "allow_null": True,
+ "explanation": "optionnel : identifiant du groupe dans le logiciel d'emploi du temps",
+ },
+ ),
+ ),
+ submitlabel="Renommer",
+ cancelbutton="Annuler",
+ )
+ dest_url = url_for(
+ "scolar.partition_editor",
+ scodoc_dept=g.scodoc_dept,
+ formsemestre_id=group.partition.formsemestre_id,
+ edit_partition=1,
+ )
+ if tf[0] == 0:
+ return (
+ html_sco_header.sco_header()
+ + "\n".join(H)
+ + "\n"
+ + tf[1]
+ + html_sco_header.sco_footer()
+ )
+ elif tf[0] == -1:
+ return flask.redirect(dest_url)
+ else:
+ # form submission
+ group.set_name(tf[2]["group_name"], edt_id=tf[2]["edt_id"], dest_url=dest_url)
+ flash("groupe modifié")
+ return flask.redirect(dest_url)
diff --git a/app/scodoc/sco_groups_view.py b/app/scodoc/sco_groups_view.py
index c44e3c0cd1..2f20b6ce3a 100644
--- a/app/scodoc/sco_groups_view.py
+++ b/app/scodoc/sco_groups_view.py
@@ -31,11 +31,8 @@
# Re-ecriture en 2014 (re-organisation de l'interface, modernisation du code)
-import collections
import datetime
-import urllib
from urllib.parse import parse_qs
-import time
from flask import url_for, g, request
@@ -45,7 +42,6 @@ from app import db
from app.models import FormSemestre
import app.scodoc.sco_utils as scu
from app.scodoc import html_sco_header
-from app.scodoc import sco_cal
from app.scodoc import sco_excel
from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups
diff --git a/app/static/css/partition_editor.css b/app/static/css/partition_editor.css
index 09f042fefc..52576891e7 100644
--- a/app/static/css/partition_editor.css
+++ b/app/static/css/partition_editor.css
@@ -302,6 +302,10 @@ body.editionActivated .filtres>div>div>div>div {
display: none;
}
+#zonePartitions span.editing a {
+ text-decoration: none;
+}
+
.editionActivated #zonePartitions .filtres .config {
display: block;
}
@@ -598,4 +602,4 @@ h3 {
#zoneGroupes .groupe[data-idgroupe=aucun]>div:nth-child(1) {
color: red;
-}
\ No newline at end of file
+}
diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css
index c31d0de605..e5db153a41 100644
--- a/app/static/css/scodoc.css
+++ b/app/static/css/scodoc.css
@@ -1327,7 +1327,7 @@ table.gt_table tr.etuddem td a {
table.gt_table tr.etuddem td.etudinfo:first-child::after {
color: red;
content: " (dém.)";
-}
+}
td.etudabs,
td.etudabs a.discretelink,
@@ -3921,9 +3921,9 @@ div#update_warning>div:nth-child(2) {
padding-left: 8ex;
}
-/*
+/*
Titres des tabs:
- .nav-tabs li a {
+ .nav-tabs li a {
font-variant: small-caps;
font-size: 13pt;
}
@@ -4354,7 +4354,7 @@ button.unselect {
/* Non supproté par les navigateurs (en Fev. 2023)
.table_recap button:has(span a.clearreaload) {
-}
+}
*/
div.table_recap table.table_recap,
@@ -4833,4 +4833,8 @@ div.cas_etat_certif_ssl {
margin-bottom: 8px;
font-style: italic;
color: rgb(231, 0, 0);
-}
\ No newline at end of file
+}
+
+.edt_id {
+ color: rgb(85, 255, 24);
+}
diff --git a/app/static/js/assiduites.js b/app/static/js/assiduites.js
index 0447082497..3285d39090 100644
--- a/app/static/js/assiduites.js
+++ b/app/static/js/assiduites.js
@@ -449,7 +449,7 @@ function validateSelectors(btn) {
);
});
- if (getModuleImplId() == null && window.forceModule) {
+ if (getModuleImplId() == null && window.forceModule && !readOnly) {
const HTML = `
Attention, le module doit obligatoirement être renseigné.
Cela vient de la configuration du semestre ou plus largement du département.
diff --git a/app/templates/scolar/partition_editor.j2 b/app/templates/scolar/partition_editor.j2
index 4aa03fc240..23ddc68669 100644
--- a/app/templates/scolar/partition_editor.j2
+++ b/app/templates/scolar/partition_editor.j2
@@ -8,7 +8,8 @@
Filtres
-
+
Configuration
@@ -246,15 +247,15 @@
let div = document.createElement("button");
div.classList.add("dt-button");
div.dataset.idgroupe = groupe.id;
+ let edt_id_str = groupe.edt_id ? ` [${groupe.edt_id}]` : "";
div.innerHTML = `
||
- ${groupe.group_name}
- ✏️
+ ${groupe.group_name} ${edt_id_str}
+ ✏️
❌`;
div.addEventListener("click", filtre);
div.querySelector(".move").addEventListener("mousedown", moveStart);
- div.querySelector(".modif").addEventListener("click", editText);
div.querySelector(".suppr").addEventListener("click", suppr);
return div;
@@ -945,4 +946,4 @@
}
-
\ No newline at end of file
+
diff --git a/app/views/scolar.py b/app/views/scolar.py
index 0388c4c2e1..ac908ce768 100644
--- a/app/views/scolar.py
+++ b/app/views/scolar.py
@@ -842,7 +842,7 @@ sco_publish("/setGroups", sco_groups.setGroups, Permission.ScoView, methods=["PO
sco_publish(
"/group_rename",
- sco_groups.group_rename,
+ sco_groups_edit.group_rename,
Permission.ScoView,
methods=["GET", "POST"],
)
diff --git a/flask_cas/routing.py b/flask_cas/routing.py
index ec9eef6396..6078b79cad 100644
--- a/flask_cas/routing.py
+++ b/flask_cas/routing.py
@@ -1,19 +1,21 @@
+"""
+Routes for CAS authentication
+Modified for ScoDoc
+"""
+import re
import ssl
+from urllib.error import URLError
+from urllib.request import urlopen
import flask
-from xmltodict import parse
from flask import current_app
+from xmltodict import parse
+
from .cas_urls import create_cas_login_url
from .cas_urls import create_cas_logout_url
from .cas_urls import create_cas_validate_url
-try:
- from urllib import urlopen # python 2
-except ImportError:
- from urllib.request import urlopen # python 3
-from urllib.error import URLError
-
blueprint = flask.Blueprint("cas", __name__)
@@ -53,7 +55,6 @@ def login():
flask.session[cas_token_session_key] = flask.request.args["ticket"]
if cas_token_session_key in flask.session:
-
if validate(flask.session[cas_token_session_key]):
if "CAS_AFTER_LOGIN_SESSION_URL" in flask.session:
redirect_url = flask.session.pop("CAS_AFTER_LOGIN_SESSION_URL")
@@ -64,7 +65,7 @@ def login():
else:
flask.session.pop(cas_token_session_key, None)
- current_app.logger.debug("Redirecting to: {redirect_url}")
+ current_app.logger.debug(f"cas.login: redirecting to {redirect_url}")
return flask.redirect(redirect_url)
@@ -84,6 +85,7 @@ def logout():
flask.session.pop(cas_username_session_key, None)
flask.session.pop(cas_attributes_session_key, None)
flask.session.pop(cas_token_session_key, None) # added by EV
+ flask.session.pop("CAS_EDT_ID", None) # added by EV
cas_after_logout = current_app.config["CAS_AFTER_LOGOUT"]
if cas_after_logout is not None:
@@ -102,7 +104,7 @@ def logout():
else:
redirect_url = create_cas_logout_url(current_app.config["CAS_SERVER"], None)
- current_app.logger.debug(f"Redirecting to: {redirect_url}")
+ current_app.logger.debug(f"cas.logout: redirecting to {redirect_url}")
return flask.redirect(redirect_url)
@@ -114,11 +116,12 @@ def validate(ticket):
key `CAS_USERNAME_SESSION_KEY` while the validated attributes dictionary
is saved under the key 'CAS_ATTRIBUTES_SESSION_KEY'.
"""
+ from app.models.config import ScoDocSiteConfig
cas_username_session_key = current_app.config["CAS_USERNAME_SESSION_KEY"]
cas_attributes_session_key = current_app.config["CAS_ATTRIBUTES_SESSION_KEY"]
cas_error_callback = current_app.config.get("CAS_ERROR_CALLBACK")
- current_app.logger.debug("validating token {0}".format(ticket))
+ current_app.logger.debug(f"validating token {ticket}")
cas_validate_url = create_cas_validate_url(
current_app.config["CAS_SERVER"],
@@ -182,7 +185,7 @@ def validate(ticket):
attributes = xml_from_dict.get("cas:attributes", {})
if attributes and "cas:memberOf" in attributes:
- if isinstance(attributes["cas:memberOf"], basestring):
+ if isinstance(attributes["cas:memberOf"], str):
attributes["cas:memberOf"] = (
attributes["cas:memberOf"].lstrip("[").rstrip("]").split(",")
)
@@ -190,6 +193,15 @@ def validate(ticket):
attributes["cas:memberOf"][group_number] = (
attributes["cas:memberOf"][group_number].lstrip(" ").rstrip(" ")
)
+ # Extract auxiliary informations (utilisé pour edt_id)
+ exp = ScoDocSiteConfig.get("cas_edt_id_from_xml_regexp")
+ if exp:
+ m = re.search(exp, xmldump)
+ if m and len(m.groups()) > 0:
+ cas_edt_id = m.group(1)
+ if cas_edt_id:
+ flask.session["CAS_EDT_ID"] = cas_edt_id
+
flask.session[cas_username_session_key] = username
flask.session[cas_attributes_session_key] = attributes
else:
diff --git a/migrations/versions/6fb956addd69_edt_id.py b/migrations/versions/6fb956addd69_edt_id.py
new file mode 100644
index 0000000000..801ba62b5e
--- /dev/null
+++ b/migrations/versions/6fb956addd69_edt_id.py
@@ -0,0 +1,58 @@
+"""edt_id
+
+Revision ID: 6fb956addd69
+Revises: fd805feb7ba8
+Create Date: 2023-11-06 12:14:42.808476
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = "6fb956addd69"
+down_revision = "fd805feb7ba8"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ with op.batch_alter_table("group_descr", schema=None) as batch_op:
+ batch_op.add_column(sa.Column("edt_id", sa.Text(), nullable=True))
+ batch_op.create_index(
+ batch_op.f("ix_group_descr_edt_id"), ["edt_id"], unique=False
+ )
+
+ with op.batch_alter_table("notes_formsemestre", schema=None) as batch_op:
+ batch_op.add_column(sa.Column("edt_id", sa.Text(), nullable=True))
+ batch_op.create_index(
+ batch_op.f("ix_notes_formsemestre_edt_id"), ["edt_id"], unique=False
+ )
+
+ with op.batch_alter_table("notes_modules", schema=None) as batch_op:
+ batch_op.add_column(sa.Column("edt_id", sa.Text(), nullable=True))
+ batch_op.create_index(
+ batch_op.f("ix_notes_modules_edt_id"), ["edt_id"], unique=False
+ )
+
+ with op.batch_alter_table("user", schema=None) as batch_op:
+ batch_op.add_column(sa.Column("edt_id", sa.Text(), nullable=True))
+ batch_op.create_index(batch_op.f("ix_user_edt_id"), ["edt_id"], unique=False)
+
+
+def downgrade():
+ with op.batch_alter_table("user", schema=None) as batch_op:
+ batch_op.drop_index(batch_op.f("ix_user_edt_id"))
+ batch_op.drop_column("edt_id")
+
+ with op.batch_alter_table("notes_modules", schema=None) as batch_op:
+ batch_op.drop_index(batch_op.f("ix_notes_modules_edt_id"))
+ batch_op.drop_column("edt_id")
+
+ with op.batch_alter_table("notes_formsemestre", schema=None) as batch_op:
+ batch_op.drop_index(batch_op.f("ix_notes_formsemestre_edt_id"))
+ batch_op.drop_column("edt_id")
+
+ with op.batch_alter_table("group_descr", schema=None) as batch_op:
+ batch_op.drop_index(batch_op.f("ix_group_descr_edt_id"))
+ batch_op.drop_column("edt_id")
diff --git a/sco_version.py b/sco_version.py
index 3fa1a68597..7771d1df9d 100644
--- a/sco_version.py
+++ b/sco_version.py
@@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
-SCOVERSION = "9.6.50"
+SCOVERSION = "9.6.51"
SCONAME = "ScoDoc"
diff --git a/tools/anonymize_db.py b/tools/anonymize_db.py
index 1d95a11b07..cc7a5a042b 100755
--- a/tools/anonymize_db.py
+++ b/tools/anonymize_db.py
@@ -55,6 +55,7 @@ def usage():
anonymize_name = "random_text_md5(8)"
anonymize_date = "'1970-01-01'"
+anonymize_false = "FALSE"
anonymize_question_str = "'?'"
anonymize_null = "NULL"
@@ -69,13 +70,14 @@ ANONYMIZED_FIELDS = {
"identite.nom": anonymize_name,
"identite.prenom": anonymize_name,
"identite.nom_usuel": anonymize_null,
- "identite.civilite": "'X'",
+ "identite.civilite_etat_civil" : anonymize_null,
+ "identite.prenom_etat_civil" : anonymize_null,
"identite.date_naissance": anonymize_date,
"identite.lieu_naissance": anonymize_question_str,
"identite.dept_naissance": anonymize_question_str,
"identite.nationalite": anonymize_question_str,
"identite.statut": anonymize_null,
- "identite.boursier": anonymize_null,
+ "identite.boursier": anonymize_false,
"identite.photo_filename": anonymize_null,
"identite.code_nip": anonymize_null,
"identite.code_ine": anonymize_null,
|