This commit is contained in:
IDK 2021-10-17 23:24:18 +02:00
commit e249f45ce9
90 changed files with 1061 additions and 739 deletions

View File

@ -130,12 +130,12 @@ base de données (tous les départements, et les utilisateurs) avant de commence
On utilise SQLAlchemy avec Alembic et Flask-Migrate. On utilise SQLAlchemy avec Alembic et Flask-Migrate.
flask db migrate -m "ScoDoc 9.0.x: ..." # ajuster le message ! flask db migrate -m "message explicatif....."
flask db upgrade flask db upgrade
Ne pas oublier de commiter les migrations (`git add migrations` ...). Ne pas oublier de d'ajouter le script de migration à git (`git add migrations/...`).
Mémo pour développeurs: séquence re-création d'une base (vérifiez votre `.env` **Mémo**: séquence re-création d'une base (vérifiez votre `.env`
ou variables d'environnement pour interroger la bonne base !). ou variables d'environnement pour interroger la bonne base !).
dropdb SCODOC_DEV dropdb SCODOC_DEV

View File

@ -317,6 +317,8 @@ def set_sco_dept(scodoc_dept: str):
g.scodoc_dept_id = dept.id # l'id g.scodoc_dept_id = dept.id # l'id
if not hasattr(g, "db_conn"): if not hasattr(g, "db_conn"):
ndb.open_db_connection() ndb.open_db_connection()
if not hasattr(g, "stored_get_formsemestre"):
g.stored_get_formsemestre = {}
def user_db_init(): def user_db_init():

View File

@ -33,7 +33,7 @@ token_auth = HTTPTokenAuth()
@basic_auth.verify_password @basic_auth.verify_password
def verify_password(username, password): def verify_password(username, password):
user = User.query.filter_by(username=username).first() user = User.query.filter_by(user_name=username).first()
if user and user.check_password(password): if user and user.check_password(password):
return user return user

View File

@ -29,7 +29,7 @@
""" """
# PAS ENCORE IMPLEMENTEE, juste un essai # PAS ENCORE IMPLEMENTEE, juste un essai
# Pour P. Bouron, il faudrait en priorité l'équivalent de # Pour P. Bouron, il faudrait en priorité l'équivalent de
# Scolarite/Notes/do_moduleimpl_withmodule_list # Scolarite/Notes/moduleimpl_withmodule_list (alias scodoc7 do_moduleimpl_withmodule_list)
# Scolarite/Notes/evaluation_create # Scolarite/Notes/evaluation_create
# Scolarite/Notes/evaluation_delete # Scolarite/Notes/evaluation_delete
# Scolarite/Notes/formation_list # Scolarite/Notes/formation_list

View File

@ -8,7 +8,7 @@ TODO: à revoir complètement pour reprendre ZScoUsers et les pages d'authentifi
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
from app.auth.models import User from app.auth.models import User, is_valid_password
_ = lambda x: x # sans babel _ = lambda x: x # sans babel
@ -43,8 +43,11 @@ class UserCreationForm(FlaskForm):
class ResetPasswordRequestForm(FlaskForm): class ResetPasswordRequestForm(FlaskForm):
email = StringField(_l("Email"), validators=[DataRequired(), Email()]) email = StringField(
submit = SubmitField(_l("Request Password Reset")) _l("Adresse email associée à votre compte ScoDoc:"),
validators=[DataRequired(), Email()],
)
submit = SubmitField(_l("Envoyer"))
class ResetPasswordForm(FlaskForm): class ResetPasswordForm(FlaskForm):
@ -52,7 +55,11 @@ class ResetPasswordForm(FlaskForm):
password2 = PasswordField( password2 = PasswordField(
_l("Répéter"), validators=[DataRequired(), EqualTo("password")] _l("Répéter"), validators=[DataRequired(), EqualTo("password")]
) )
submit = SubmitField(_l("Request Password Reset")) submit = SubmitField(_l("Valider ce mot de passe"))
def validate_password(self, password):
if not is_valid_password(password.data):
raise ValidationError(f"Mot de passe trop simple, recommencez")
class DeactivateUserForm(FlaskForm): class DeactivateUserForm(FlaskForm):

View File

@ -10,6 +10,7 @@ import re
from time import time from time import time
from typing import Optional from typing import Optional
import cracklib # pylint: disable=import-error
from flask import current_app, url_for, g from flask import current_app, url_for, g
from flask_login import UserMixin, AnonymousUserMixin from flask_login import UserMixin, AnonymousUserMixin
@ -28,6 +29,23 @@ from app.scodoc import sco_etud # a deplacer dans scu
VALID_LOGIN_EXP = re.compile(r"^[a-zA-Z0-9@\\\-_\.]+$") VALID_LOGIN_EXP = re.compile(r"^[a-zA-Z0-9@\\\-_\.]+$")
def is_valid_password(cleartxt):
"""Check password.
returns True if OK.
"""
if (
hasattr(scu.CONFIG, "MIN_PASSWORD_LENGTH")
and scu.CONFIG.MIN_PASSWORD_LENGTH > 0
and len(cleartxt) < scu.CONFIG.MIN_PASSWORD_LENGTH
):
return False # invalid: too short
try:
_ = cracklib.FascistCheck(cleartxt)
return True
except ValueError:
return False
class User(UserMixin, db.Model): class User(UserMixin, db.Model):
"""ScoDoc users, handled by Flask / SQLAlchemy""" """ScoDoc users, handled by Flask / SQLAlchemy"""

View File

@ -46,7 +46,10 @@ def login():
if not next_page or url_parse(next_page).netloc != "": if not next_page or url_parse(next_page).netloc != "":
next_page = url_for("scodoc.index") next_page = url_for("scodoc.index")
return redirect(next_page) return redirect(next_page)
return render_template("auth/login.html", title=_("Sign In"), form=form) message = request.args.get("message", "")
return render_template(
"auth/login.html", title=_("Sign In"), form=form, message=message
)
@bp.route("/logout") @bp.route("/logout")
@ -95,7 +98,9 @@ def reset_password_request():
current_app.logger.info( current_app.logger.info(
"reset_password_request: for unkown user '{}'".format(form.email.data) "reset_password_request: for unkown user '{}'".format(form.email.data)
) )
flash(_("Voir les instructions envoyées par mail")) flash(
_("Voir les instructions envoyées par mail (pensez à regarder vos spams)")
)
return redirect(url_for("auth.login")) return redirect(url_for("auth.login"))
return render_template( return render_template(
"auth/reset_password_request.html", title=_("Reset Password"), form=form "auth/reset_password_request.html", title=_("Reset Password"), form=form
@ -113,6 +118,6 @@ def reset_password(token):
if form.validate_on_submit(): if form.validate_on_submit():
user.set_password(form.password.data) user.set_password(form.password.data)
db.session.commit() db.session.commit()
flash(_("Your password has been reset.")) flash(_("Votre mot de passe a été changé."))
return redirect(url_for("auth.login")) return redirect(url_for("auth.login"))
return render_template("auth/reset_password.html", form=form) return render_template("auth/reset_password.html", form=form, user=user)

View File

@ -10,12 +10,10 @@ import logging
import werkzeug import werkzeug
from werkzeug.exceptions import BadRequest from werkzeug.exceptions import BadRequest
import flask import flask
from flask import g from flask import g, current_app, request
from flask import abort, current_app from flask import abort, url_for, redirect
from flask import request
from flask_login import current_user from flask_login import current_user
from flask_login import login_required from flask_login import login_required
from flask import current_app
import flask_login import flask_login
import app import app
@ -52,6 +50,15 @@ def scodoc(func):
@wraps(func) @wraps(func)
def scodoc_function(*args, **kwargs): def scodoc_function(*args, **kwargs):
# interdit les POST si pas loggué
if request.method == "POST" and not current_user.is_authenticated:
current_app.logger.info("POST by non authenticated user")
return redirect(
url_for(
"auth.login",
message="La page a expiré. Identifiez-vous et recommencez l'opération",
)
)
if "scodoc_dept" in kwargs: if "scodoc_dept" in kwargs:
dept_acronym = kwargs["scodoc_dept"] dept_acronym = kwargs["scodoc_dept"]
# current_app.logger.info("setting dept to " + dept_acronym) # current_app.logger.info("setting dept to " + dept_acronym)
@ -81,7 +88,7 @@ def permission_required(permission):
def permission_required_compat_scodoc7(permission): def permission_required_compat_scodoc7(permission):
"""Décorateur pour les fonctions utilisée comme API dans ScoDoc 7 """Décorateur pour les fonctions utilisées comme API dans ScoDoc 7
Comme @permission_required mais autorise de passer directement Comme @permission_required mais autorise de passer directement
les informations d'auth en paramètres: les informations d'auth en paramètres:
__ac_name, __ac_password __ac_name, __ac_password

View File

@ -32,6 +32,7 @@ class NotesFormation(db.Model):
ues = db.relationship("NotesUE", backref="formation", lazy="dynamic") ues = db.relationship("NotesUE", backref="formation", lazy="dynamic")
formsemestres = db.relationship("FormSemestre", lazy="dynamic", backref="formation") formsemestres = db.relationship("FormSemestre", lazy="dynamic", backref="formation")
ues = db.relationship("NotesUE", lazy="dynamic", backref="formation")
def __repr__(self): def __repr__(self):
return f"<{self.__class__.__name__}(id={self.id}, dept_id={self.dept_id}, acronyme='{self.acronyme}')>" return f"<{self.__class__.__name__}(id={self.id}, dept_id={self.dept_id}, acronyme='{self.acronyme}')>"
@ -65,6 +66,10 @@ class NotesUE(db.Model):
# coef UE, utilise seulement si l'option use_ue_coefs est activée: # coef UE, utilise seulement si l'option use_ue_coefs est activée:
coefficient = db.Column(db.Float) coefficient = db.Column(db.Float)
# relations
matieres = db.relationship("NotesMatiere", lazy="dynamic", backref="ue")
modules = db.relationship("NotesModule", lazy="dynamic", backref="ue")
def __repr__(self): def __repr__(self):
return f"<{self.__class__.__name__}(id={self.id}, formation_id={self.formation_id}, acronyme='{self.acronyme}')>" return f"<{self.__class__.__name__}(id={self.id}, formation_id={self.formation_id}, acronyme='{self.acronyme}')>"
@ -84,6 +89,8 @@ class NotesMatiere(db.Model):
titre = db.Column(db.Text()) titre = db.Column(db.Text())
numero = db.Column(db.Integer) # ordre de présentation numero = db.Column(db.Integer) # ordre de présentation
modules = db.relationship("NotesModule", lazy="dynamic", backref="matiere")
class NotesModule(db.Model): class NotesModule(db.Model):
"""Module""" """Module"""
@ -110,6 +117,8 @@ class NotesModule(db.Model):
# id de l'element pedagogique Apogee correspondant: # id de l'element pedagogique Apogee correspondant:
code_apogee = db.Column(db.String(APO_CODE_STR_LEN)) code_apogee = db.Column(db.String(APO_CODE_STR_LEN))
module_type = db.Column(db.Integer) # NULL ou 0:defaut, 1: malus (NOTES_MALUS) module_type = db.Column(db.Integer) # NULL ou 0:defaut, 1: malus (NOTES_MALUS)
# Relations:
modimpls = db.relationship("NotesModuleImpl", backref="module", lazy="dynamic")
class NotesTag(db.Model): class NotesTag(db.Model):

View File

@ -70,9 +70,14 @@ class FormSemestre(db.Model):
# code element annee Apogee, eg 'VRT1A' ou 'V2INLA,V2INCA,...' # code element annee Apogee, eg 'VRT1A' ou 'V2INLA,V2INCA,...'
elt_annee_apo = db.Column(db.Text()) elt_annee_apo = db.Column(db.Text())
# Relations:
etapes = db.relationship( etapes = db.relationship(
"NotesFormsemestreEtape", cascade="all,delete", backref="notes_formsemestre" "NotesFormsemestreEtape", cascade="all,delete", backref="formsemestre"
) )
formsemestres = db.relationship(
"NotesModuleImpl", backref="formsemestre", lazy="dynamic"
)
# Ancien id ScoDoc7 pour les migrations de bases anciennes # Ancien id ScoDoc7 pour les migrations de bases anciennes
# ne pas utiliser après migrate_scodoc7_dept_archive # ne pas utiliser après migrate_scodoc7_dept_archive
scodoc7_id = db.Column(db.Text(), nullable=True) scodoc7_id = db.Column(db.Text(), nullable=True)

View File

@ -44,7 +44,6 @@ import unicodedata
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app import log from app import log
import six
PE_DEBUG = 0 PE_DEBUG = 0
@ -145,7 +144,7 @@ def escape_for_latex(s):
} }
exp = re.compile( exp = re.compile(
"|".join( "|".join(
re.escape(six.text_type(key)) re.escape(key)
for key in sorted(list(conv.keys()), key=lambda item: -len(item)) for key in sorted(list(conv.keys()), key=lambda item: -len(item))
) )
) )

View File

@ -194,7 +194,8 @@ def bonus_tours(notes_sport, coefs, infos=None):
def bonus_iutr(notes_sport, coefs, infos=None): def bonus_iutr(notes_sport, coefs, infos=None):
"""Calcul du bonus , regle de l'IUT de Roanne (contribuée par Raphael C., nov 2012) """Calcul du bonus , règle de l'IUT de Roanne
(contribuée par Raphael C., nov 2012)
Le bonus est compris entre 0 et 0.35 point. Le bonus est compris entre 0 et 0.35 point.
cette procédure modifie la moyenne de chaque UE capitalisable. cette procédure modifie la moyenne de chaque UE capitalisable.

View File

@ -752,6 +752,8 @@ if __name__ == "__main__":
) )
document.build(objects) document.build(objects)
data = doc.getvalue() data = doc.getvalue()
open("/tmp/gen_table.pdf", "wb").write(data) with open("/tmp/gen_table.pdf", "wb") as f:
f.write(data)
p = T.make_page(format="pdf") p = T.make_page(format="pdf")
open("toto.pdf", "wb").write(p) with open("toto.pdf", "wb") as f:
f.write(p)

View File

@ -104,6 +104,8 @@ def make_menu(title, items, css_class="", alone=False):
item["urlq"] = url_for( item["urlq"] = url_for(
item["endpoint"], scodoc_dept=g.scodoc_dept, **args item["endpoint"], scodoc_dept=g.scodoc_dept, **args
) )
elif "url" in item:
item["urlq"] = item["url"]
else: else:
item["urlq"] = "#" item["urlq"] = "#"
item["attr"] = item.get("attr", "") item["attr"] = item.get("attr", "")

View File

@ -4,11 +4,8 @@
# Code from http://code.activestate.com/recipes/457411/ # Code from http://code.activestate.com/recipes/457411/
from __future__ import print_function
from bisect import bisect_left, bisect_right from bisect import bisect_left, bisect_right
from six.moves import zip
class intervalmap(object): class intervalmap(object):
""" """

View File

@ -27,10 +27,7 @@
"""Calculs sur les notes et cache des resultats """Calculs sur les notes et cache des resultats
""" """
import inspect
import os
import pdb
import time
from operator import itemgetter from operator import itemgetter
from flask import g, url_for from flask import g, url_for
@ -40,12 +37,8 @@ import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
from app import log from app import log
from app.scodoc.sco_formulas import NoteVector from app.scodoc.sco_formulas import NoteVector
from app.scodoc.sco_exceptions import ( from app.scodoc.sco_exceptions import ScoValueError
AccessDenied,
NoteProcessError,
ScoException,
ScoValueError,
)
from app.scodoc.sco_formsemestre import ( from app.scodoc.sco_formsemestre import (
formsemestre_uecoef_list, formsemestre_uecoef_list,
formsemestre_uecoef_create, formsemestre_uecoef_create,
@ -109,15 +102,13 @@ def get_sem_ues_modimpls(formsemestre_id, modimpls=None):
(utilisé quand on ne peut pas construire nt et faire nt.get_ues()) (utilisé quand on ne peut pas construire nt et faire nt.get_ues())
""" """
if modimpls is None: if modimpls is None:
modimpls = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) modimpls = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
uedict = {} uedict = {}
for modimpl in modimpls: for modimpl in modimpls:
mod = sco_edit_module.do_module_list(args={"module_id": modimpl["module_id"]})[ mod = sco_edit_module.module_list(args={"module_id": modimpl["module_id"]})[0]
0
]
modimpl["module"] = mod modimpl["module"] = mod
if not mod["ue_id"] in uedict: if not mod["ue_id"] in uedict:
ue = sco_edit_ue.do_ue_list(args={"ue_id": mod["ue_id"]})[0] ue = sco_edit_ue.ue_list(args={"ue_id": mod["ue_id"]})[0]
uedict[ue["ue_id"]] = ue uedict[ue["ue_id"]] = ue
ues = list(uedict.values()) ues = list(uedict.values())
ues.sort(key=lambda u: u["numero"]) ues.sort(key=lambda u: u["numero"])
@ -219,26 +210,29 @@ class NotesTable(object):
valid_evals, valid_evals,
mods_att, mods_att,
self.expr_diagnostics, self.expr_diagnostics,
) = sco_compute_moy.do_formsemestre_moyennes(self, formsemestre_id) ) = sco_compute_moy.formsemestre_compute_modimpls_moyennes(
self, formsemestre_id
)
self._mods_att = mods_att # liste des modules avec des notes en attente self._mods_att = mods_att # liste des modules avec des notes en attente
self._matmoys = {} # moyennes par matieres self._matmoys = {} # moyennes par matieres
self._valid_evals = {} # { evaluation_id : eval } self._valid_evals = {} # { evaluation_id : eval }
for e in valid_evals: for e in valid_evals:
self._valid_evals[e["evaluation_id"]] = e # Liste des modules et UE self._valid_evals[e["evaluation_id"]] = e # Liste des modules et UE
uedict = {} # public member: { ue_id : ue } uedict = {} # public member: { ue_id : ue }
self.uedict = uedict self.uedict = uedict # les ues qui ont un modimpl dans ce semestre
for modimpl in self._modimpls: for modimpl in self._modimpls:
mod = modimpl["module"] # has been added here by do_formsemestre_moyennes # module has been added by formsemestre_compute_modimpls_moyennes
mod = modimpl["module"]
if not mod["ue_id"] in uedict: if not mod["ue_id"] in uedict:
ue = sco_edit_ue.do_ue_list(args={"ue_id": mod["ue_id"]})[0] ue = sco_edit_ue.ue_list(args={"ue_id": mod["ue_id"]})[0]
uedict[ue["ue_id"]] = ue uedict[ue["ue_id"]] = ue
else: else:
ue = uedict[mod["ue_id"]] ue = uedict[mod["ue_id"]]
modimpl["ue"] = ue # add ue dict to moduleimpl modimpl["ue"] = ue # add ue dict to moduleimpl
self._matmoys[mod["matiere_id"]] = {} self._matmoys[mod["matiere_id"]] = {}
mat = sco_edit_matiere.do_matiere_list( mat = sco_edit_matiere.matiere_list(args={"matiere_id": mod["matiere_id"]})[
args={"matiere_id": mod["matiere_id"]} 0
)[0] ]
modimpl["mat"] = mat # add matiere dict to moduleimpl modimpl["mat"] = mat # add matiere dict to moduleimpl
# calcul moyennes du module et stocke dans le module # calcul moyennes du module et stocke dans le module
# nb_inscrits, nb_notes, nb_abs, nb_neutre, moy, median, last_modif= # nb_inscrits, nb_notes, nb_abs, nb_neutre, moy, median, last_modif=
@ -1059,7 +1053,7 @@ class NotesTable(object):
"Warning: %s capitalized an UE %s which is not part of current sem %s" "Warning: %s capitalized an UE %s which is not part of current sem %s"
% (etudid, ue_id, self.formsemestre_id) % (etudid, ue_id, self.formsemestre_id)
) )
ue = sco_edit_ue.do_ue_list(args={"ue_id": ue_id})[0] ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
self.uedict[ue_id] = ue # record this UE self.uedict[ue_id] = ue # record this UE
if ue_id not in self._uecoef: if ue_id not in self._uecoef:
cl = formsemestre_uecoef_list( cl = formsemestre_uecoef_list(

View File

@ -96,7 +96,7 @@ def DBInsertDict(
convert_empty_to_nulls=1, convert_empty_to_nulls=1,
return_id=True, return_id=True,
ignore_conflicts=False, ignore_conflicts=False,
): ) -> int:
"""insert into table values in dict 'vals' """insert into table values in dict 'vals'
Return: id de l'object créé Return: id de l'object créé
""" """
@ -327,7 +327,7 @@ class EditableTable(object):
self.sql_default_values = None self.sql_default_values = None
self.insert_ignore_conflicts = insert_ignore_conflicts self.insert_ignore_conflicts = insert_ignore_conflicts
def create(self, cnx, args): def create(self, cnx, args) -> int:
"create object in table" "create object in table"
vals = dictfilter(args, self.dbfields, self.filter_nulls) vals = dictfilter(args, self.dbfields, self.filter_nulls)
if self.id_name in vals: if self.id_name in vals:

View File

@ -474,7 +474,7 @@ def _get_abs_description(a, cursor=None):
desc = a["description"] desc = a["description"]
if a["moduleimpl_id"] and a["moduleimpl_id"] != "NULL": if a["moduleimpl_id"] and a["moduleimpl_id"] != "NULL":
# Trouver le nom du module # Trouver le nom du module
Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( Mlist = sco_moduleimpl.moduleimpl_withmodule_list(
moduleimpl_id=a["moduleimpl_id"] moduleimpl_id=a["moduleimpl_id"]
) )
if Mlist: if Mlist:

View File

@ -115,7 +115,7 @@ def doSignaleAbsence(
J = "NON " J = "NON "
M = "" M = ""
if moduleimpl_id and moduleimpl_id != "NULL": if moduleimpl_id and moduleimpl_id != "NULL":
mod = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] mod = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
formsemestre_id = mod["formsemestre_id"] formsemestre_id = mod["formsemestre_id"]
nt = sco_cache.NotesTableCache.get(formsemestre_id) nt = sco_cache.NotesTableCache.get(formsemestre_id)
ues = nt.get_ues(etudid=etudid) ues = nt.get_ues(etudid=etudid)
@ -939,7 +939,7 @@ def _tables_abs_etud(
return "" return ""
ex = [] ex = []
for ev in a["evals"]: for ev in a["evals"]:
mod = sco_moduleimpl.do_moduleimpl_withmodule_list( mod = sco_moduleimpl.moduleimpl_withmodule_list(
moduleimpl_id=ev["moduleimpl_id"] moduleimpl_id=ev["moduleimpl_id"]
)[0] )[0]
if format == "html": if format == "html":
@ -957,7 +957,7 @@ def _tables_abs_etud(
def descr_abs(a): def descr_abs(a):
ex = [] ex = []
for ev in a.get("absent", []): for ev in a.get("absent", []):
mod = sco_moduleimpl.do_moduleimpl_withmodule_list( mod = sco_moduleimpl.moduleimpl_withmodule_list(
moduleimpl_id=ev["moduleimpl_id"] moduleimpl_id=ev["moduleimpl_id"]
)[0] )[0]
if format == "html": if format == "html":

View File

@ -203,7 +203,9 @@ class BaseArchiver(object):
def get_archive_description(self, archive_id): def get_archive_description(self, archive_id):
"""Return description of archive""" """Return description of archive"""
self.initialize() self.initialize()
return open(os.path.join(archive_id, "_description.txt")).read() with open(os.path.join(archive_id, "_description.txt")) as f:
descr = f.read()
return descr
def create_obj_archive(self, oid: int, description: str): def create_obj_archive(self, oid: int, description: str):
"""Creates a new archive for this object and returns its id.""" """Creates a new archive for this object and returns its id."""
@ -232,9 +234,8 @@ class BaseArchiver(object):
try: try:
scu.GSL.acquire() scu.GSL.acquire()
fname = os.path.join(archive_id, filename) fname = os.path.join(archive_id, filename)
f = open(fname, "wb") with open(fname, "wb") as f:
f.write(data) f.write(data)
f.close()
finally: finally:
scu.GSL.release() scu.GSL.release()
return filename return filename
@ -247,7 +248,9 @@ class BaseArchiver(object):
raise ValueError("invalid filename") raise ValueError("invalid filename")
fname = os.path.join(archive_id, filename) fname = os.path.join(archive_id, filename)
log("reading archive file %s" % fname) log("reading archive file %s" % fname)
return open(fname, "rb").read() with open(fname, "rb") as f:
data = f.read()
return data
def get_archived_file(self, oid, archive_name, filename): def get_archived_file(self, oid, archive_name, filename):
"""Recupere donnees du fichier indiqué et envoie au client""" """Recupere donnees du fichier indiqué et envoie au client"""

View File

@ -360,7 +360,7 @@ def formsemestre_bulletinetud_published_dict(
"decisions_ue" "decisions_ue"
]: # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee) ]: # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee)
for ue_id in decision["decisions_ue"].keys(): for ue_id in decision["decisions_ue"].keys():
ue = sco_edit_ue.do_ue_list({"ue_id": ue_id})[0] ue = sco_edit_ue.ue_list({"ue_id": ue_id})[0]
d["decision_ue"].append( d["decision_ue"].append(
dict( dict(
ue_id=ue["ue_id"], ue_id=ue["ue_id"],

View File

@ -385,7 +385,7 @@ def make_xml_formsemestre_bulletinetud(
"decisions_ue" "decisions_ue"
]: # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee) ]: # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee)
for ue_id in decision["decisions_ue"].keys(): for ue_id in decision["decisions_ue"].keys():
ue = sco_edit_ue.do_ue_list({"ue_id": ue_id})[0] ue = sco_edit_ue.ue_list({"ue_id": ue_id})[0]
doc.append( doc.append(
Element( Element(
"decision_ue", "decision_ue",

View File

@ -292,7 +292,7 @@ def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=Fa
class DefferedSemCacheManager: class DefferedSemCacheManager:
"""Experimental: pour effectuer des opérations indépendantes dans la """Contexte pour effectuer des opérations indépendantes dans la
même requete qui invalident le cache. Par exemple, quand on inscrit même requete qui invalident le cache. Par exemple, quand on inscrit
des étudiants un par un à un semestre, chaque inscription va invalider des étudiants un par un à un semestre, chaque inscription va invalider
le cache, et la suivante va le reconstruire... pour l'invalider juste après. le cache, et la suivante va le reconstruire... pour l'invalider juste après.

View File

@ -28,7 +28,6 @@
"""Semestres: Codes gestion parcours (constantes) """Semestres: Codes gestion parcours (constantes)
""" """
import collections import collections
from six.moves import range
NOTES_TOLERANCE = 0.00499999999999 # si note >= (BARRE-TOLERANCE), considere ok NOTES_TOLERANCE = 0.00499999999999 # si note >= (BARRE-TOLERANCE), considere ok
# (permet d'eviter d'afficher 10.00 sous barre alors que la moyenne vaut 9.999) # (permet d'eviter d'afficher 10.00 sous barre alors que la moyenne vaut 9.999)

View File

@ -27,10 +27,10 @@
"""Calcul des moyennes de module """Calcul des moyennes de module
""" """
import traceback
import pprint import pprint
import traceback
from flask import url_for, g
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
from app.scodoc.sco_utils import ( from app.scodoc.sco_utils import (
@ -40,7 +40,7 @@ from app.scodoc.sco_utils import (
EVALUATION_RATTRAPAGE, EVALUATION_RATTRAPAGE,
EVALUATION_SESSION2, EVALUATION_SESSION2,
) )
from app.scodoc.sco_exceptions import ScoException from app.scodoc.sco_exceptions import ScoValueError
from app import log from app import log
from app.scodoc import sco_abs from app.scodoc import sco_abs
from app.scodoc import sco_edit_module from app.scodoc import sco_edit_module
@ -65,7 +65,8 @@ def moduleimpl_has_expression(mod):
def formsemestre_expressions_use_abscounts(formsemestre_id): def formsemestre_expressions_use_abscounts(formsemestre_id):
"""True si les notes de ce semestre dépendent des compteurs d'absences. """True si les notes de ce semestre dépendent des compteurs d'absences.
Cela n'est normalement pas le cas, sauf si des formules utilisateur utilisent ces compteurs. Cela n'est normalement pas le cas, sauf si des formules utilisateur
utilisent ces compteurs.
""" """
# check presence of 'nbabs' in expressions # check presence of 'nbabs' in expressions
ab = "nb_abs" # chaine recherchée ab = "nb_abs" # chaine recherchée
@ -79,7 +80,7 @@ def formsemestre_expressions_use_abscounts(formsemestre_id):
if expr and expr[0] != "#" and ab in expr: if expr and expr[0] != "#" and ab in expr:
return True return True
# 2- moyennes de modules # 2- moyennes de modules
for mod in sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id): for mod in sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id):
if moduleimpl_has_expression(mod) and ab in mod["computation_expr"]: if moduleimpl_has_expression(mod) and ab in mod["computation_expr"]:
return True return True
return False return False
@ -128,7 +129,7 @@ def compute_user_formula(
coefs, coefs,
coefs_mask, coefs_mask,
formula, formula,
diag_info={}, # infos supplementaires a placer ds messages d'erreur diag_info=None, # infos supplementaires a placer ds messages d'erreur
use_abs=True, use_abs=True,
): ):
"""Calcul moyenne a partir des notes et coefs, en utilisant la formule utilisateur (une chaine). """Calcul moyenne a partir des notes et coefs, en utilisant la formule utilisateur (une chaine).
@ -164,9 +165,14 @@ def compute_user_formula(
if (user_moy > 20) or (user_moy < 0): if (user_moy > 20) or (user_moy < 0):
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
raise ScoException( raise ScoValueError(
"""valeur moyenne %s hors limite pour <a href="formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s">%s</a>""" f"""
% (user_moy, sem["formsemestre_id"], etudid, etud["nomprenom"]) Valeur moyenne {user_moy} hors limite pour
<a href="{url_for('notes.formsemestre_bulletinetud',
scodoc_dept=g.scodoc_dept,
formsemestre_id=sem["formsemestre_id"],
etudid=etudid
)}">{etud["nomprenom"]}</a>"""
) )
except: except:
log( log(
@ -183,7 +189,7 @@ def compute_user_formula(
return user_moy return user_moy
def do_moduleimpl_moyennes(nt, mod): def compute_moduleimpl_moyennes(nt, modimpl):
"""Retourne dict { etudid : note_moyenne } pour tous les etuds inscrits """Retourne dict { etudid : note_moyenne } pour tous les etuds inscrits
au moduleimpl mod, la liste des evaluations "valides" (toutes notes entrées au moduleimpl mod, la liste des evaluations "valides" (toutes notes entrées
ou en attente), et att (vrai s'il y a des notes en attente dans ce module). ou en attente), et att (vrai s'il y a des notes en attente dans ce module).
@ -193,13 +199,13 @@ def do_moduleimpl_moyennes(nt, mod):
S'il manque des notes et que le coef n'est pas nul, S'il manque des notes et que le coef n'est pas nul,
la moyenne n'est pas calculée: NA la moyenne n'est pas calculée: NA
Ne prend en compte que les evaluations toutes les notes sont entrées. Ne prend en compte que les evaluations toutes les notes sont entrées.
Le résultat est une note sur 20. Le résultat note_moyenne est une note sur 20.
""" """
diag_info = {} # message d'erreur formule diag_info = {} # message d'erreur formule
moduleimpl_id = mod["moduleimpl_id"] moduleimpl_id = modimpl["moduleimpl_id"]
is_malus = mod["module"]["module_type"] == scu.MODULE_MALUS is_malus = modimpl["module"]["module_type"] == scu.MODULE_MALUS
sem = sco_formsemestre.get_formsemestre(mod["formsemestre_id"]) sem = sco_formsemestre.get_formsemestre(modimpl["formsemestre_id"])
etudids = sco_moduleimpl.do_moduleimpl_listeetuds( etudids = sco_moduleimpl.moduleimpl_listeetuds(
moduleimpl_id moduleimpl_id
) # tous, y compris demissions ) # tous, y compris demissions
# Inscrits au semestre (pour traiter les demissions): # Inscrits au semestre (pour traiter les demissions):
@ -207,7 +213,7 @@ def do_moduleimpl_moyennes(nt, mod):
[ [
x["etudid"] x["etudid"]
for x in sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits( for x in sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits(
mod["formsemestre_id"] modimpl["formsemestre_id"]
) )
] ]
) )
@ -218,7 +224,7 @@ def do_moduleimpl_moyennes(nt, mod):
key=lambda x: (x["numero"], x["jour"], x["heure_debut"]) key=lambda x: (x["numero"], x["jour"], x["heure_debut"])
) # la plus ancienne en tête ) # la plus ancienne en tête
user_expr = moduleimpl_has_expression(mod) user_expr = moduleimpl_has_expression(modimpl)
attente = False attente = False
# recupere les notes de toutes les evaluations # recupere les notes de toutes les evaluations
eval_rattr = None eval_rattr = None
@ -268,7 +274,7 @@ def do_moduleimpl_moyennes(nt, mod):
] ]
# #
R = {} R = {}
formula = scu.unescape_html(mod["computation_expr"]) formula = scu.unescape_html(modimpl["computation_expr"])
formula_use_abs = "abs" in formula formula_use_abs = "abs" in formula
for etudid in insmod_set: # inscrits au semestre et au module for etudid in insmod_set: # inscrits au semestre et au module
@ -365,7 +371,7 @@ def do_moduleimpl_moyennes(nt, mod):
return R, valid_evals, attente, diag_info return R, valid_evals, attente, diag_info
def do_formsemestre_moyennes(nt, formsemestre_id): def formsemestre_compute_modimpls_moyennes(nt, formsemestre_id):
"""retourne dict { moduleimpl_id : { etudid, note_moyenne_dans_ce_module } }, """retourne dict { moduleimpl_id : { etudid, note_moyenne_dans_ce_module } },
la liste des moduleimpls, la liste des evaluations valides, la liste des moduleimpls, la liste des evaluations valides,
liste des moduleimpls avec notes en attente. liste des moduleimpls avec notes en attente.
@ -375,7 +381,7 @@ def do_formsemestre_moyennes(nt, formsemestre_id):
# args={"formsemestre_id": formsemestre_id} # args={"formsemestre_id": formsemestre_id}
# ) # )
# etudids = [x["etudid"] for x in inscr] # etudids = [x["etudid"] for x in inscr]
modimpls = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) modimpls = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
# recupere les moyennes des etudiants de tous les modules # recupere les moyennes des etudiants de tous les modules
D = {} D = {}
valid_evals = [] valid_evals = []
@ -383,15 +389,16 @@ def do_formsemestre_moyennes(nt, formsemestre_id):
mods_att = [] mods_att = []
expr_diags = [] expr_diags = []
for modimpl in modimpls: for modimpl in modimpls:
mod = sco_edit_module.do_module_list(args={"module_id": modimpl["module_id"]})[ mod = sco_edit_module.module_list(args={"module_id": modimpl["module_id"]})[0]
0
]
modimpl["module"] = mod # add module dict to moduleimpl (used by nt) modimpl["module"] = mod # add module dict to moduleimpl (used by nt)
moduleimpl_id = modimpl["moduleimpl_id"] moduleimpl_id = modimpl["moduleimpl_id"]
assert moduleimpl_id not in D assert moduleimpl_id not in D
D[moduleimpl_id], valid_evals_mod, attente, expr_diag = do_moduleimpl_moyennes( (
nt, modimpl D[moduleimpl_id],
) valid_evals_mod,
attente,
expr_diag,
) = compute_moduleimpl_moyennes(nt, modimpl)
valid_evals_per_mod[moduleimpl_id] = valid_evals_mod valid_evals_per_mod[moduleimpl_id] = valid_evals_mod
valid_evals += valid_evals_mod valid_evals += valid_evals_mod
if attente: if attente:

View File

@ -59,9 +59,7 @@ def formsemestre_table_estim_cost(
""" """
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
sco_formsemestre_status.fill_formsemestre(sem) sco_formsemestre_status.fill_formsemestre(sem)
Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
formsemestre_id=formsemestre_id
)
T = [] T = []
for M in Mlist: for M in Mlist:
Mod = M["module"] Mod = M["module"]

View File

@ -167,7 +167,8 @@ def _anonymize_db(ano_db_name):
def _get_scodoc_serial(): def _get_scodoc_serial():
try: try:
return int(open(os.path.join(scu.SCODOC_VERSION_DIR, "scodoc.sn")).read()) with open(os.path.join(scu.SCODOC_VERSION_DIR, "scodoc.sn")) as f:
return int(f.read())
except: except:
return 0 return 0

View File

@ -104,7 +104,7 @@ def do_formation_delete(oid):
raise ScoLockedFormError() raise ScoLockedFormError()
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
# delete all UE in this formation # delete all UE in this formation
ues = sco_edit_ue.do_ue_list({"formation_id": oid}) ues = sco_edit_ue.ue_list({"formation_id": oid})
for ue in ues: for ue in ues:
sco_edit_ue.do_ue_delete(ue["ue_id"], force=True) sco_edit_ue.do_ue_delete(ue["ue_id"], force=True)
@ -252,7 +252,7 @@ def formation_edit(formation_id=None, create=False):
do_formation_edit(tf[2]) do_formation_edit(tf[2])
return flask.redirect( return flask.redirect(
url_for( url_for(
"notes.ue_list", scodoc_dept=g.scodoc_dept, formation_id=formation_id "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation_id
) )
) )
@ -313,13 +313,13 @@ def invalidate_sems_in_formation(formation_id):
def module_move(module_id, after=0, redirect=1): def module_move(module_id, after=0, redirect=1):
"""Move before/after previous one (decrement/increment numero)""" """Move before/after previous one (decrement/increment numero)"""
module = sco_edit_module.do_module_list({"module_id": module_id})[0] module = sco_edit_module.module_list({"module_id": module_id})[0]
redirect = int(redirect) redirect = int(redirect)
after = int(after) # 0: deplace avant, 1 deplace apres after = int(after) # 0: deplace avant, 1 deplace apres
if after not in (0, 1): if after not in (0, 1):
raise ValueError('invalid value for "after"') raise ValueError('invalid value for "after"')
formation_id = module["formation_id"] formation_id = module["formation_id"]
others = sco_edit_module.do_module_list({"matiere_id": module["matiere_id"]}) others = sco_edit_module.module_list({"matiere_id": module["matiere_id"]})
# log('others=%s' % others) # log('others=%s' % others)
if len(others) > 1: if len(others) > 1:
idx = [p["module_id"] for p in others].index(module_id) idx = [p["module_id"] for p in others].index(module_id)
@ -343,21 +343,21 @@ def module_move(module_id, after=0, redirect=1):
if redirect: if redirect:
return flask.redirect( return flask.redirect(
url_for( url_for(
"notes.ue_list", scodoc_dept=g.scodoc_dept, formation_id=formation_id "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation_id
) )
) )
def ue_move(ue_id, after=0, redirect=1): def ue_move(ue_id, after=0, redirect=1):
"""Move UE before/after previous one (decrement/increment numero)""" """Move UE before/after previous one (decrement/increment numero)"""
o = sco_edit_ue.do_ue_list({"ue_id": ue_id})[0] o = sco_edit_ue.ue_list({"ue_id": ue_id})[0]
# log('ue_move %s (#%s) after=%s' % (ue_id, o['numero'], after)) # log('ue_move %s (#%s) after=%s' % (ue_id, o['numero'], after))
redirect = int(redirect) redirect = int(redirect)
after = int(after) # 0: deplace avant, 1 deplace apres after = int(after) # 0: deplace avant, 1 deplace apres
if after not in (0, 1): if after not in (0, 1):
raise ValueError('invalid value for "after"') raise ValueError('invalid value for "after"')
formation_id = o["formation_id"] formation_id = o["formation_id"]
others = sco_edit_ue.do_ue_list({"formation_id": formation_id}) others = sco_edit_ue.ue_list({"formation_id": formation_id})
if len(others) > 1: if len(others) > 1:
idx = [p["ue_id"] for p in others].index(ue_id) idx = [p["ue_id"] for p in others].index(ue_id)
neigh = None # object to swap with neigh = None # object to swap with
@ -378,7 +378,7 @@ def ue_move(ue_id, after=0, redirect=1):
if redirect: if redirect:
return flask.redirect( return flask.redirect(
url_for( url_for(
"notes.ue_list", "notes.ue_table",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
formation_id=o["formation_id"], formation_id=o["formation_id"],
) )

View File

@ -47,7 +47,7 @@ _matiereEditor = ndb.EditableTable(
) )
def do_matiere_list(*args, **kw): def matiere_list(*args, **kw):
"list matieres" "list matieres"
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
return _matiereEditor.list(cnx, *args, **kw) return _matiereEditor.list(cnx, *args, **kw)
@ -60,12 +60,12 @@ def do_matiere_edit(*args, **kw):
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
# check # check
mat = do_matiere_list({"matiere_id": args[0]["matiere_id"]})[0] mat = matiere_list({"matiere_id": args[0]["matiere_id"]})[0]
if matiere_is_locked(mat["matiere_id"]): if matiere_is_locked(mat["matiere_id"]):
raise ScoLockedFormError() raise ScoLockedFormError()
# edit # edit
_matiereEditor.edit(cnx, *args, **kw) _matiereEditor.edit(cnx, *args, **kw)
formation_id = sco_edit_ue.do_ue_list({"ue_id": mat["ue_id"]})[0]["formation_id"] formation_id = sco_edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]["formation_id"]
sco_edit_formation.invalidate_sems_in_formation(formation_id) sco_edit_formation.invalidate_sems_in_formation(formation_id)
@ -77,7 +77,7 @@ def do_matiere_create(args):
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
# check # check
ue = sco_edit_ue.do_ue_list({"ue_id": args["ue_id"]})[0] ue = sco_edit_ue.ue_list({"ue_id": args["ue_id"]})[0]
# create matiere # create matiere
r = _matiereEditor.create(cnx, args) r = _matiereEditor.create(cnx, args)
@ -96,7 +96,7 @@ def matiere_create(ue_id=None):
"""Creation d'une matiere""" """Creation d'une matiere"""
from app.scodoc import sco_edit_ue from app.scodoc import sco_edit_ue
UE = sco_edit_ue.do_ue_list(args={"ue_id": ue_id})[0] UE = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
H = [ H = [
html_sco_header.sco_header(page_title="Création d'une matière"), html_sco_header.sco_header(page_title="Création d'une matière"),
"""<h2>Création d'une matière dans l'UE %(titre)s (%(acronyme)s)</h2>""" % UE, """<h2>Création d'une matière dans l'UE %(titre)s (%(acronyme)s)</h2>""" % UE,
@ -134,7 +134,7 @@ associé.
) )
dest_url = url_for( dest_url = url_for(
"notes.ue_list", scodoc_dept=g.scodoc_dept, formation_id=UE["formation_id"] "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=UE["formation_id"]
) )
if tf[0] == 0: if tf[0] == 0:
@ -143,7 +143,7 @@ associé.
return flask.redirect(dest_url) return flask.redirect(dest_url)
else: else:
# check unicity # check unicity
mats = do_matiere_list(args={"ue_id": ue_id, "titre": tf[2]["titre"]}) mats = matiere_list(args={"ue_id": ue_id, "titre": tf[2]["titre"]})
if mats: if mats:
return ( return (
"\n".join(H) "\n".join(H)
@ -164,8 +164,8 @@ def do_matiere_delete(oid):
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
# check # check
mat = do_matiere_list({"matiere_id": oid})[0] mat = matiere_list({"matiere_id": oid})[0]
ue = sco_edit_ue.do_ue_list({"ue_id": mat["ue_id"]})[0] ue = sco_edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]
locked = matiere_is_locked(mat["matiere_id"]) locked = matiere_is_locked(mat["matiere_id"])
if locked: if locked:
log("do_matiere_delete: mat=%s" % mat) log("do_matiere_delete: mat=%s" % mat)
@ -174,7 +174,7 @@ def do_matiere_delete(oid):
raise ScoLockedFormError() raise ScoLockedFormError()
log("do_matiere_delete: matiere_id=%s" % oid) log("do_matiere_delete: matiere_id=%s" % oid)
# delete all modules in this matiere # delete all modules in this matiere
mods = sco_edit_module.do_module_list({"matiere_id": oid}) mods = sco_edit_module.module_list({"matiere_id": oid})
for mod in mods: for mod in mods:
sco_edit_module.do_module_delete(mod["module_id"]) sco_edit_module.do_module_delete(mod["module_id"])
_matiereEditor.delete(cnx, oid) _matiereEditor.delete(cnx, oid)
@ -193,8 +193,8 @@ def matiere_delete(matiere_id=None):
"""Delete an UE""" """Delete an UE"""
from app.scodoc import sco_edit_ue from app.scodoc import sco_edit_ue
M = do_matiere_list(args={"matiere_id": matiere_id})[0] M = matiere_list(args={"matiere_id": matiere_id})[0]
UE = sco_edit_ue.do_ue_list(args={"ue_id": M["ue_id"]})[0] UE = sco_edit_ue.ue_list(args={"ue_id": M["ue_id"]})[0]
H = [ H = [
html_sco_header.sco_header(page_title="Suppression d'une matière"), html_sco_header.sco_header(page_title="Suppression d'une matière"),
"<h2>Suppression de la matière %(titre)s" % M, "<h2>Suppression de la matière %(titre)s" % M,
@ -223,17 +223,17 @@ def matiere_edit(matiere_id=None):
from app.scodoc import sco_formations from app.scodoc import sco_formations
from app.scodoc import sco_edit_ue from app.scodoc import sco_edit_ue
F = do_matiere_list(args={"matiere_id": matiere_id}) F = matiere_list(args={"matiere_id": matiere_id})
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.do_ue_list(args={"ue_id": F["ue_id"]}) U = sco_edit_ue.ue_list(args={"ue_id": F["ue_id"]})
if not F: if not F:
raise ScoValueError("UE inexistante !") raise ScoValueError("UE inexistante !")
U = U[0] U = U[0]
Fo = sco_formations.formation_list(args={"formation_id": U["formation_id"]})[0] Fo = sco_formations.formation_list(args={"formation_id": U["formation_id"]})[0]
ues = sco_edit_ue.do_ue_list(args={"formation_id": U["formation_id"]}) ues = sco_edit_ue.ue_list(args={"formation_id": U["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 = [
@ -286,7 +286,7 @@ associé.
return flask.redirect(dest_url) return flask.redirect(dest_url)
else: else:
# check unicity # check unicity
mats = do_matiere_list(args={"ue_id": tf[2]["ue_id"], "titre": tf[2]["titre"]}) mats = matiere_list(args={"ue_id": tf[2]["ue_id"], "titre": tf[2]["titre"]})
if len(mats) > 1 or (len(mats) == 1 and mats[0]["matiere_id"] != matiere_id): if len(mats) > 1 or (len(mats) == 1 and mats[0]["matiere_id"] != matiere_id):
return ( return (
"\n".join(H) "\n".join(H)

View File

@ -94,7 +94,7 @@ _moduleEditor = ndb.EditableTable(
) )
def do_module_list(*args, **kw): def module_list(*args, **kw):
"list modules" "list modules"
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
return _moduleEditor.list(cnx, *args, **kw) return _moduleEditor.list(cnx, *args, **kw)
@ -126,8 +126,8 @@ def module_create(matiere_id=None):
if matiere_id is None: if matiere_id is None:
raise ScoValueError("invalid matiere !") raise ScoValueError("invalid matiere !")
M = sco_edit_matiere.do_matiere_list(args={"matiere_id": matiere_id})[0] M = sco_edit_matiere.matiere_list(args={"matiere_id": matiere_id})[0]
UE = sco_edit_ue.do_ue_list(args={"ue_id": M["ue_id"]})[0] UE = sco_edit_ue.ue_list(args={"ue_id": M["ue_id"]})[0]
Fo = sco_formations.formation_list(args={"formation_id": UE["formation_id"]})[0] Fo = sco_formations.formation_list(args={"formation_id": UE["formation_id"]})[0]
parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"]) parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"])
semestres_indices = list(range(1, parcours.NB_SEM + 1)) semestres_indices = list(range(1, parcours.NB_SEM + 1))
@ -138,7 +138,7 @@ def module_create(matiere_id=None):
_MODULE_HELP, _MODULE_HELP,
] ]
# cherche le numero adequat (pour placer le module en fin de liste) # cherche le numero adequat (pour placer le module en fin de liste)
Mods = do_module_list(args={"matiere_id": matiere_id}) Mods = module_list(args={"matiere_id": matiere_id})
if Mods: if Mods:
default_num = max([m["numero"] for m in Mods]) + 10 default_num = max([m["numero"] for m in Mods]) + 10
else: else:
@ -241,7 +241,7 @@ def module_create(matiere_id=None):
do_module_create(tf[2]) do_module_create(tf[2])
return flask.redirect( return flask.redirect(
url_for( url_for(
"notes.ue_list", "notes.ue_table",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
formation_id=UE["formation_id"], formation_id=UE["formation_id"],
) )
@ -252,18 +252,18 @@ def do_module_delete(oid):
"delete module" "delete module"
from app.scodoc import sco_formations from app.scodoc import sco_formations
mod = do_module_list({"module_id": oid})[0] mod = module_list({"module_id": oid})[0]
if module_is_locked(mod["module_id"]): if module_is_locked(mod["module_id"]):
raise ScoLockedFormError() raise ScoLockedFormError()
# S'il y a des moduleimpls, on ne peut pas detruire le module ! # S'il y a des moduleimpls, on ne peut pas detruire le module !
mods = sco_moduleimpl.do_moduleimpl_list(module_id=oid) mods = sco_moduleimpl.moduleimpl_list(module_id=oid)
if mods: if mods:
err_page = f"""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3> err_page = f"""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>
<p class="help">Il faut d'abord supprimer le semestre. Mais il est peut être préférable de <p class="help">Il faut d'abord supprimer le semestre. Mais il est peut être préférable de
laisser ce programme intact et d'en créer une nouvelle version pour la modifier. laisser ce programme intact et d'en créer une nouvelle version pour la modifier.
</p> </p>
<a href="{url_for('notes.ue_list', scodoc_dept=g.scodoc_dept, <a href="{url_for('notes.ue_table', scodoc_dept=g.scodoc_dept,
formation_id=mod["formation_id"])}">reprendre</a> formation_id=mod["formation_id"])}">reprendre</a>
""" """
raise ScoGenError(err_page) raise ScoGenError(err_page)
@ -285,7 +285,7 @@ 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 = do_module_list(args={"module_id": module_id}) Mods = module_list(args={"module_id": module_id})
if not Mods: if not Mods:
raise ScoValueError("Module inexistant !") raise ScoValueError("Module inexistant !")
Mod = Mods[0] Mod = Mods[0]
@ -317,7 +317,7 @@ def do_module_edit(val):
from app.scodoc import sco_edit_formation from app.scodoc import sco_edit_formation
# check # check
mod = do_module_list({"module_id": val["module_id"]})[0] mod = module_list({"module_id": val["module_id"]})[0]
if module_is_locked(mod["module_id"]): if module_is_locked(mod["module_id"]):
# formation verrouillée: empeche de modifier certains champs: # formation verrouillée: empeche de modifier certains champs:
protected_fields = ("coefficient", "ue_id", "matiere_id", "semestre_id") protected_fields = ("coefficient", "ue_id", "matiere_id", "semestre_id")
@ -332,7 +332,7 @@ def do_module_edit(val):
def check_module_code_unicity(code, field, formation_id, module_id=None): def check_module_code_unicity(code, field, formation_id, module_id=None):
"true si code module unique dans la formation" "true si code module unique dans la formation"
Mods = do_module_list(args={"code": code, "formation_id": formation_id}) Mods = module_list(args={"code": code, "formation_id": formation_id})
if module_id: # edition: supprime le module en cours if module_id: # edition: supprime le module en cours
Mods = [m for m in Mods if m["module_id"] != module_id] Mods = [m for m in Mods if m["module_id"] != module_id]
@ -346,7 +346,7 @@ def module_edit(module_id=None):
if not module_id: if not module_id:
raise ScoValueError("invalid module !") raise ScoValueError("invalid module !")
Mod = do_module_list(args={"module_id": module_id}) Mod = module_list(args={"module_id": module_id})
if not Mod: if not Mod:
raise ScoValueError("invalid module !") raise ScoValueError("invalid module !")
Mod = Mod[0] Mod = Mod[0]
@ -521,7 +521,7 @@ def edit_module_set_code_apogee(id=None, value=None):
value = value.strip("-_ \t") value = value.strip("-_ \t")
log("edit_module_set_code_apogee: module_id=%s code_apogee=%s" % (module_id, value)) log("edit_module_set_code_apogee: module_id=%s code_apogee=%s" % (module_id, value))
modules = do_module_list(args={"module_id": module_id}) modules = module_list(args={"module_id": module_id})
if not modules: if not modules:
return "module invalide" # should not occur return "module invalide" # should not occur
@ -531,7 +531,7 @@ def edit_module_set_code_apogee(id=None, value=None):
return value return value
def module_list(formation_id): def module_table(formation_id):
"""Liste des modules de la formation """Liste des modules de la formation
(XXX inutile ou a revoir) (XXX inutile ou a revoir)
""" """
@ -548,7 +548,7 @@ def module_list(formation_id):
] ]
editable = current_user.has_permission(Permission.ScoChangeFormation) editable = current_user.has_permission(Permission.ScoChangeFormation)
for Mod in do_module_list(args={"formation_id": formation_id}): for Mod in module_list(args={"formation_id": formation_id}):
H.append('<li class="notes_module_list">%s' % Mod) H.append('<li class="notes_module_list">%s' % Mod)
if editable: if editable:
H.append('<a href="module_edit?module_id=%(module_id)s">modifier</a>' % Mod) H.append('<a href="module_edit?module_id=%(module_id)s">modifier</a>' % Mod)
@ -580,7 +580,7 @@ def module_is_locked(module_id):
def module_count_moduleimpls(module_id): def module_count_moduleimpls(module_id):
"Number of moduleimpls using this module" "Number of moduleimpls using this module"
mods = sco_moduleimpl.do_moduleimpl_list(module_id=module_id) mods = sco_moduleimpl.moduleimpl_list(module_id=module_id)
return len(mods) return len(mods)
@ -588,14 +588,14 @@ 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.do_ue_list(args={"formation_id": formation_id}) ue_list = sco_edit_ue.ue_list(args={"formation_id": formation_id})
for ue in ue_list: for ue in ue_list:
# Un seul module de malus par UE: # Un seul module de malus par UE:
nb_mod_malus = len( nb_mod_malus = len(
[ [
mod mod
for mod in do_module_list(args={"ue_id": ue["ue_id"]}) for mod in module_list(args={"ue_id": ue["ue_id"]})
if mod["module_type"] == scu.MODULE_MALUS if mod["module_type"] == scu.MODULE_MALUS
] ]
) )
@ -610,7 +610,7 @@ def ue_add_malus_module(ue_id, titre=None, code=None):
"""Add a malus module in this ue""" """Add a malus module in this ue"""
from app.scodoc import sco_edit_ue from app.scodoc import sco_edit_ue
ue = sco_edit_ue.do_ue_list(args={"ue_id": ue_id})[0] ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
if titre is None: if titre is None:
titre = "" titre = ""
@ -629,7 +629,7 @@ def ue_add_malus_module(ue_id, titre=None, code=None):
) )
# Matiere pour placer le module malus # Matiere pour placer le module malus
Matlist = sco_edit_matiere.do_matiere_list(args={"ue_id": ue_id}) Matlist = sco_edit_matiere.matiere_list(args={"ue_id": ue_id})
numero = max([mat["numero"] for mat in Matlist]) + 10 numero = max([mat["numero"] for mat in Matlist]) + 10
matiere_id = sco_edit_matiere.do_matiere_create( matiere_id = sco_edit_matiere.do_matiere_create(
{"ue_id": ue_id, "titre": "Malus", "numero": numero} {"ue_id": ue_id, "titre": "Malus", "numero": numero}

View File

@ -85,7 +85,7 @@ _ueEditor = ndb.EditableTable(
) )
def do_ue_list(*args, **kw): def ue_list(*args, **kw):
"list UEs" "list UEs"
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
return _ueEditor.list(cnx, *args, **kw) return _ueEditor.list(cnx, *args, **kw)
@ -97,9 +97,7 @@ def do_ue_create(args):
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
# check duplicates # check duplicates
ues = do_ue_list( ues = ue_list({"formation_id": args["formation_id"], "acronyme": args["acronyme"]})
{"formation_id": args["formation_id"], "acronyme": args["acronyme"]}
)
if ues: if ues:
raise ScoValueError('Acronyme d\'UE "%s" déjà utilisé !' % args["acronyme"]) raise ScoValueError('Acronyme d\'UE "%s" déjà utilisé !' % args["acronyme"])
# create # create
@ -124,7 +122,7 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
log("do_ue_delete: ue_id=%s, delete_validations=%s" % (ue_id, delete_validations)) log("do_ue_delete: ue_id=%s, delete_validations=%s" % (ue_id, delete_validations))
# check # check
ue = do_ue_list({"ue_id": ue_id}) ue = ue_list({"ue_id": ue_id})
if not ue: if not ue:
raise ScoValueError("UE inexistante !") raise ScoValueError("UE inexistante !")
ue = ue[0] ue = ue[0]
@ -152,7 +150,7 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
) )
# delete all matiere in this UE # delete all matiere in this UE
mats = sco_edit_matiere.do_matiere_list({"ue_id": ue_id}) mats = sco_edit_matiere.matiere_list({"ue_id": ue_id})
for mat in mats: for mat in mats:
sco_edit_matiere.do_matiere_delete(mat["matiere_id"]) sco_edit_matiere.do_matiere_delete(mat["matiere_id"])
# delete uecoef and events # delete uecoef and events
@ -177,7 +175,7 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
if not force: if not force:
return flask.redirect( return flask.redirect(
url_for( url_for(
"notes.ue_list", "notes.ue_table",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
formation_id=ue["formation_id"], formation_id=ue["formation_id"],
) )
@ -197,7 +195,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None):
create = int(create) create = int(create)
if not create: if not create:
U = do_ue_list(args={"ue_id": ue_id}) U = ue_list(args={"ue_id": ue_id})
if not U: if not U:
raise ScoValueError("UE inexistante !") raise ScoValueError("UE inexistante !")
U = U[0] U = U[0]
@ -371,7 +369,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None):
do_ue_edit(tf[2]) do_ue_edit(tf[2])
return flask.redirect( return flask.redirect(
url_for( url_for(
"notes.ue_list", scodoc_dept=g.scodoc_dept, formation_id=formation_id "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation_id
) )
) )
@ -382,7 +380,7 @@ def _add_ue_semestre_id(ue_list):
qui les place à la fin de la liste. qui les place à la fin de la liste.
""" """
for ue in ue_list: for ue in ue_list:
Modlist = sco_edit_module.do_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"]
else: else:
@ -393,7 +391,7 @@ 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 = do_ue_list(args={"formation_id": formation_id}) ue_list = ue_list(args={"formation_id": formation_id})
if not ue_list: if not ue_list:
return 0 return 0
if semestre_id is None: if semestre_id is None:
@ -410,7 +408,7 @@ def next_ue_numero(formation_id, semestre_id=None):
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 = do_ue_list(args={"ue_id": ue_id}) ue = ue_list(args={"ue_id": ue_id})
if not ue: if not ue:
raise ScoValueError("UE inexistante !") raise ScoValueError("UE inexistante !")
ue = ue[0] ue = ue[0]
@ -426,9 +424,9 @@ def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False):
return do_ue_delete(ue_id, delete_validations=delete_validations) return do_ue_delete(ue_id, delete_validations=delete_validations)
def ue_list(formation_id=None, msg=""): def ue_table(formation_id=None, msg=""): # was ue_list
"""Liste des matières et modules d'une formation, avec liens pour """Liste des matières et modules d'une formation, avec liens pour
editer (si non verrouillée). éditer (si non verrouillée).
""" """
from app.scodoc import sco_formations from app.scodoc import sco_formations
from app.scodoc import sco_formsemestre_validation from app.scodoc import sco_formsemestre_validation
@ -440,7 +438,7 @@ def ue_list(formation_id=None, msg=""):
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 = do_ue_list(args={"formation_id": formation_id}) ue_list = ue_list(args={"formation_id": formation_id})
# tri par semestre et numero: # tri par semestre et numero:
_add_ue_semestre_id(ue_list) _add_ue_semestre_id(ue_list)
ue_list.sort(key=lambda u: (u["semestre_id"], u["numero"])) ue_list.sort(key=lambda u: (u["semestre_id"], u["numero"]))
@ -461,7 +459,7 @@ def ue_list(formation_id=None, msg=""):
else: else:
lockicon = "" lockicon = ""
arrow_up, arrow_down, arrow_none = sco_groups.getArrowIconsTags() arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags()
delete_icon = scu.icontag( delete_icon = scu.icontag(
"delete_small_img", title="Supprimer (module inutilisé)", alt="supprimer" "delete_small_img", title="Supprimer (module inutilisé)", alt="supprimer"
) )
@ -627,7 +625,7 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
H.append('<span class="locked">[verrouillé]</span>') H.append('<span class="locked">[verrouillé]</span>')
if not parcours.UE_IS_MODULE: if not parcours.UE_IS_MODULE:
H.append('<ul class="notes_matiere_list">') H.append('<ul class="notes_matiere_list">')
Matlist = sco_edit_matiere.do_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:
if not parcours.UE_IS_MODULE: if not parcours.UE_IS_MODULE:
H.append('<li class="notes_matiere_list">') H.append('<li class="notes_matiere_list">')
@ -648,7 +646,7 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
H.append("</a>") H.append("</a>")
H.append('<ul class="notes_module_list">') H.append('<ul class="notes_module_list">')
Modlist = sco_edit_module.do_module_list( Modlist = sco_edit_module.module_list(
args={"matiere_id": Mat["matiere_id"]} args={"matiere_id": Mat["matiere_id"]}
) )
im = 0 im = 0
@ -847,7 +845,7 @@ def ue_sharing_code(ue_code=None, ue_id=None, hide_ue_id=None):
ue_code = str(ue_code) ue_code = str(ue_code)
if ue_id: if ue_id:
ue = do_ue_list(args={"ue_id": ue_id})[0] ue = ue_list(args={"ue_id": ue_id})[0]
if not ue_code: if not ue_code:
ue_code = ue["ue_code"] ue_code = ue["ue_code"]
F = sco_formations.formation_list(args={"formation_id": ue["formation_id"]})[0] F = sco_formations.formation_list(args={"formation_id": ue["formation_id"]})[0]
@ -884,7 +882,7 @@ def ue_sharing_code(ue_code=None, ue_id=None, hide_ue_id=None):
for ue in ues: for ue in ues:
H.append( H.append(
f"""<li>{ue.acronyme} ({ue.titre}) dans <a class="stdlink" f"""<li>{ue.acronyme} ({ue.titre}) dans <a class="stdlink"
href="{url_for("notes.ue_list", scodoc_dept=g.scodoc_dept, formation_id=ue.formation.id)}" href="{url_for("notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=ue.formation.id)}"
>{ue.formation.acronyme} ({ue.formation.titre})</a>, version {ue.formation.version} >{ue.formation.acronyme} ({ue.formation.titre})</a>, version {ue.formation.version}
</li> </li>
""" """
@ -897,13 +895,13 @@ def do_ue_edit(args, bypass_lock=False, dont_invalidate_cache=False):
"edit an UE" "edit an UE"
# check # check
ue_id = args["ue_id"] ue_id = args["ue_id"]
ue = do_ue_list({"ue_id": ue_id})[0] ue = ue_list({"ue_id": ue_id})[0]
if (not bypass_lock) and ue_is_locked(ue["ue_id"]): if (not bypass_lock) and ue_is_locked(ue["ue_id"]):
raise ScoLockedFormError() raise ScoLockedFormError()
# check: acronyme unique dans cette formation # check: acronyme unique dans cette formation
if "acronyme" in args: if "acronyme" in args:
new_acro = args["acronyme"] new_acro = args["acronyme"]
ues = do_ue_list({"formation_id": ue["formation_id"], "acronyme": new_acro}) ues = ue_list({"formation_id": ue["formation_id"], "acronyme": new_acro})
if ues and ues[0]["ue_id"] != ue_id: if ues and ues[0]["ue_id"] != ue_id:
raise ScoValueError('Acronyme d\'UE "%s" déjà utilisé !' % args["acronyme"]) raise ScoValueError('Acronyme d\'UE "%s" déjà utilisé !' % args["acronyme"])
@ -926,7 +924,7 @@ def edit_ue_set_code_apogee(id=None, value=None):
value = value.strip("-_ \t") value = value.strip("-_ \t")
log("edit_ue_set_code_apogee: ue_id=%s code_apogee=%s" % (ue_id, value)) log("edit_ue_set_code_apogee: ue_id=%s code_apogee=%s" % (ue_id, value))
ues = do_ue_list(args={"ue_id": ue_id}) ues = ue_list(args={"ue_id": ue_id})
if not ues: if not ues:
return "ue invalide" return "ue invalide"
@ -966,11 +964,11 @@ 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 = do_ue_list(args={"formation_id": formation_id}) ue_list = ue_list(args={"formation_id": formation_id})
for UE in ue_list: for UE in ue_list:
Matlist = sco_edit_matiere.do_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.do_module_list( Modlist = sco_edit_module.module_list(
args={"matiere_id": Mat["matiere_id"]} args={"matiere_id": Mat["matiere_id"]}
) )
for Mod in Modlist: for Mod in Modlist:
@ -1049,5 +1047,5 @@ def ue_list_semestre_ids(ue):
Mais cela n'a pas toujours été le cas dans les programmes pédagogiques officiels, Mais cela n'a pas toujours été le cas dans les programmes pédagogiques officiels,
aussi ScoDoc laisse le choix. aussi ScoDoc laisse le choix.
""" """
Modlist = sco_edit_module.do_module_list(args={"ue_id": ue["ue_id"]}) Modlist = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]})
return sorted(list(set([mod["semestre_id"] for mod in Modlist]))) return sorted(list(set([mod["semestre_id"] for mod in Modlist])))

View File

@ -33,10 +33,10 @@ XXX incompatible avec les ics HyperPlanning Paris 13 (était pour GPU).
""" """
import six.moves.urllib.request, six.moves.urllib.error, six.moves.urllib.parse
import traceback
import icalendar import icalendar
import pprint import pprint
import traceback
import urllib
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app import log from app import log
@ -80,7 +80,7 @@ def formsemestre_load_ics(sem):
ics_data = "" ics_data = ""
else: else:
log("Loading edt from %s" % ics_url) log("Loading edt from %s" % ics_url)
f = six.moves.urllib.request.urlopen( f = urllib.request.urlopen(
ics_url, timeout=5 ics_url, timeout=5
) # 5s TODO: add config parameter, eg for slow networks ) # 5s TODO: add config parameter, eg for slow networks
ics_data = f.read() ics_data = f.read()

View File

@ -830,7 +830,7 @@ appreciations_edit = _appreciationsEditor.edit
def read_etablissements(): def read_etablissements():
filename = os.path.join(scu.SCO_TOOLS_DIR, scu.CONFIG.ETABL_FILENAME) filename = os.path.join(scu.SCO_TOOLS_DIR, scu.CONFIG.ETABL_FILENAME)
log("reading %s" % filename) log("reading %s" % filename)
f = open(filename) with open(filename) as f:
L = [x[:-1].split(";") for x in f] L = [x[:-1].split(";") for x in f]
E = {} E = {}
for l in L[1:]: for l in L[1:]:

View File

@ -179,7 +179,7 @@ def do_evaluation_list(args, sortkey=None):
def do_evaluation_list_in_formsemestre(formsemestre_id): def do_evaluation_list_in_formsemestre(formsemestre_id):
"list evaluations in this formsemestre" "list evaluations in this formsemestre"
mods = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) mods = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
evals = [] evals = []
for mod in mods: for mod in mods:
evals += do_evaluation_list(args={"moduleimpl_id": mod["moduleimpl_id"]}) evals += do_evaluation_list(args={"moduleimpl_id": mod["moduleimpl_id"]})
@ -213,7 +213,7 @@ def _check_evaluation_args(args):
jour = args.get("jour", None) jour = args.get("jour", None)
args["jour"] = jour args["jour"] = jour
if jour: if jour:
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
d, m, y = [int(x) for x in sem["date_debut"].split("/")] d, m, y = [int(x) for x in sem["date_debut"].split("/")]
date_debut = datetime.date(y, m, d) date_debut = datetime.date(y, m, d)
@ -301,8 +301,8 @@ def do_evaluation_create(
r = _evaluationEditor.create(cnx, args) r = _evaluationEditor.create(cnx, args)
# news # news
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
mod["moduleimpl_id"] = M["moduleimpl_id"] mod["moduleimpl_id"] = M["moduleimpl_id"]
mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
sco_news.add( sco_news.add(
@ -332,7 +332,7 @@ def do_evaluation_edit(args):
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
_evaluationEditor.edit(cnx, args) _evaluationEditor.edit(cnx, args)
# inval cache pour ce semestre # inval cache pour ce semestre
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
sco_cache.invalidate_formsemestre(formsemestre_id=M["formsemestre_id"]) sco_cache.invalidate_formsemestre(formsemestre_id=M["formsemestre_id"])
@ -357,10 +357,10 @@ def do_evaluation_delete(evaluation_id):
_evaluationEditor.delete(cnx, evaluation_id) _evaluationEditor.delete(cnx, evaluation_id)
# inval cache pour ce semestre # inval cache pour ce semestre
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
sco_cache.invalidate_formsemestre(formsemestre_id=M["formsemestre_id"]) sco_cache.invalidate_formsemestre(formsemestre_id=M["formsemestre_id"])
# news # news
mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
mod["moduleimpl_id"] = M["moduleimpl_id"] mod["moduleimpl_id"] = M["moduleimpl_id"]
mod["url"] = ( mod["url"] = (
scu.NotesURL() + "/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod scu.NotesURL() + "/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
@ -373,9 +373,6 @@ def do_evaluation_delete(evaluation_id):
) )
_DEE_TOT = 0
def do_evaluation_etat(evaluation_id, partition_id=None, select_first_partition=False): def do_evaluation_etat(evaluation_id, partition_id=None, select_first_partition=False):
"""donne infos sur l'etat du evaluation """donne infos sur l'etat du evaluation
{ nb_inscrits, nb_notes, nb_abs, nb_neutre, nb_att, { nb_inscrits, nb_notes, nb_abs, nb_neutre, nb_att,
@ -412,8 +409,8 @@ def do_evaluation_etat(evaluation_id, partition_id=None, select_first_partition=
last_modif = None last_modif = None
# ---- Liste des groupes complets et incomplets # ---- Liste des groupes complets et incomplets
E = do_evaluation_list(args={"evaluation_id": evaluation_id})[0] E = do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
is_malus = Mod["module_type"] == scu.MODULE_MALUS # True si module de malus is_malus = Mod["module_type"] == scu.MODULE_MALUS # True si module de malus
formsemestre_id = M["formsemestre_id"] formsemestre_id = M["formsemestre_id"]
# Si partition_id is None, prend 'all' ou bien la premiere: # Si partition_id is None, prend 'all' ou bien la premiere:
@ -735,7 +732,7 @@ def formsemestre_evaluations_cal(formsemestre_id):
if not e["jour"]: if not e["jour"]:
continue continue
day = e["jour"].strftime("%Y-%m-%d") day = e["jour"].strftime("%Y-%m-%d")
mod = sco_moduleimpl.do_moduleimpl_withmodule_list( mod = sco_moduleimpl.moduleimpl_withmodule_list(
moduleimpl_id=e["moduleimpl_id"] moduleimpl_id=e["moduleimpl_id"]
)[0] )[0]
txt = mod["module"]["code"] or mod["module"]["abbrev"] or "eval" txt = mod["module"]["code"] or mod["module"]["abbrev"] or "eval"
@ -812,7 +809,7 @@ def evaluation_date_first_completion(evaluation_id):
# (pour avoir l'etat et le groupe) et aussi les inscriptions # (pour avoir l'etat et le groupe) et aussi les inscriptions
# au module (pour gerer les modules optionnels correctement) # au module (pour gerer les modules optionnels correctement)
# E = do_evaluation_list(args={"evaluation_id": evaluation_id})[0] # E = do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
# M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] # M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
# formsemestre_id = M["formsemestre_id"] # formsemestre_id = M["formsemestre_id"]
# insem = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits( formsemestre_id) # insem = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits( formsemestre_id)
# insmod = sco_moduleimpl.do_moduleimpl_inscription_list(moduleimpl_id=E["moduleimpl_id"]) # insmod = sco_moduleimpl.do_moduleimpl_inscription_list(moduleimpl_id=E["moduleimpl_id"])
@ -854,8 +851,8 @@ def formsemestre_evaluations_delai_correction(formsemestre_id, format="html"):
evals = nt.get_sem_evaluation_etat_list() evals = nt.get_sem_evaluation_etat_list()
T = [] T = []
for e in evals: for e in evals:
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=e["moduleimpl_id"])[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=e["moduleimpl_id"])[0]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
if (e["evaluation_type"] != scu.EVALUATION_NORMALE) or ( if (e["evaluation_type"] != scu.EVALUATION_NORMALE) or (
Mod["module_type"] == scu.MODULE_MALUS Mod["module_type"] == scu.MODULE_MALUS
): ):
@ -1035,8 +1032,8 @@ def evaluation_describe(evaluation_id="", edit_in_place=True):
E = do_evaluation_list({"evaluation_id": evaluation_id})[0] E = do_evaluation_list({"evaluation_id": evaluation_id})[0]
moduleimpl_id = E["moduleimpl_id"] moduleimpl_id = E["moduleimpl_id"]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
formsemestre_id = M["formsemestre_id"] formsemestre_id = M["formsemestre_id"]
u = sco_users.user_info(M["responsable_id"]) u = sco_users.user_info(M["responsable_id"])
resp = u["prenomnom"] resp = u["prenomnom"]
@ -1065,7 +1062,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True):
] ]
if Mod["module_type"] == scu.MODULE_MALUS: if Mod["module_type"] == scu.MODULE_MALUS:
# Indique l'UE # Indique l'UE
ue = sco_edit_ue.do_ue_list(args={"ue_id": Mod["ue_id"]})[0] ue = sco_edit_ue.ue_list(args={"ue_id": Mod["ue_id"]})[0]
H.append("<p><b>UE : %(acronyme)s</b></p>" % ue) H.append("<p><b>UE : %(acronyme)s</b></p>" % ue)
# store min/max values used by JS client-side checks: # store min/max values used by JS client-side checks:
H.append( H.append(
@ -1115,7 +1112,7 @@ def evaluation_create_form(
the_eval = do_evaluation_list({"evaluation_id": evaluation_id})[0] the_eval = do_evaluation_list({"evaluation_id": evaluation_id})[0]
moduleimpl_id = the_eval["moduleimpl_id"] moduleimpl_id = the_eval["moduleimpl_id"]
# #
M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0]
is_malus = M["module"]["module_type"] == scu.MODULE_MALUS # True si module de malus is_malus = M["module"]["module_type"] == scu.MODULE_MALUS # True si module de malus
formsemestre_id = M["formsemestre_id"] formsemestre_id = M["formsemestre_id"]
min_note_max = scu.NOTES_PRECISION # le plus petit bareme possible min_note_max = scu.NOTES_PRECISION # le plus petit bareme possible
@ -1174,7 +1171,7 @@ def evaluation_create_form(
else: else:
min_note_max_str = "0" min_note_max_str = "0"
# #
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
# #
help = """<div class="help"><p class="help"> help = """<div class="help"><p class="help">
Le coefficient d'une évaluation n'est utilisé que pour pondérer les évaluations au sein d'un module. Le coefficient d'une évaluation n'est utilisé que pour pondérer les évaluations au sein d'un module.

View File

@ -285,10 +285,25 @@ class ScoExcelSheet:
def make_cell(self, value: any = None, style=None, comment=None): def make_cell(self, value: any = None, style=None, comment=None):
"""Construit une cellule. """Construit une cellule.
value -- contenu de la cellule (texte ou numérique) value -- contenu de la cellule (texte, numérique, booléen ou date)
style -- style par défaut (dictionnaire cf. excel_make_style) de la feuille si non spécifié style -- style par défaut (dictionnaire cf. excel_make_style) de la feuille si non spécifié
""" """
cell = WriteOnlyCell(self.ws, value or "") # adapatation des valeurs si nécessaire
if value is None:
value = ""
elif value is True:
value = 1
elif value is False:
value = 0
elif isinstance(value, datetime.datetime):
value = value.replace(
tzinfo=None
) # make date naive (cf https://openpyxl.readthedocs.io/en/latest/datetime.html#timezones)
# création de la cellule
cell = WriteOnlyCell(self.ws, value)
# recopie des styles
if style is None: if style is None:
style = self.default_style style = self.default_style
if "font" in style: if "font" in style:
@ -310,7 +325,8 @@ class ScoExcelSheet:
lines = comment.splitlines() lines = comment.splitlines()
cell.comment.width = 7 * max([len(line) for line in lines]) cell.comment.width = 7 * max([len(line) for line in lines])
cell.comment.height = 20 * len(lines) cell.comment.height = 20 * len(lines)
# test datatype at the end so that datetime format may be overwritten
# test datatype to overwrite datetime format
if isinstance(value, datetime.date): if isinstance(value, datetime.date):
cell.data_type = "d" cell.data_type = "d"
cell.number_format = FORMAT_DATE_DDMMYY cell.number_format = FORMAT_DATE_DDMMYY
@ -318,6 +334,7 @@ class ScoExcelSheet:
cell.data_type = "n" cell.data_type = "n"
else: else:
cell.data_type = "s" cell.data_type = "s"
return cell return cell
def make_row(self, values: list, style=None, comments=None): def make_row(self, values: list, style=None, comments=None):

View File

@ -98,7 +98,7 @@ def formation_export(formation_id, export_ids=False, export_tags=True, format=No
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.do_ue_list({"formation_id": formation_id}) ues = sco_edit_ue.ue_list({"formation_id": formation_id})
F["ue"] = ues F["ue"] = ues
for ue in ues: for ue in ues:
ue_id = ue["ue_id"] ue_id = ue["ue_id"]
@ -107,14 +107,14 @@ def formation_export(formation_id, export_ids=False, export_tags=True, format=No
del ue["formation_id"] del ue["formation_id"]
if ue["ects"] is None: if ue["ects"] is None:
del ue["ects"] del ue["ects"]
mats = sco_edit_matiere.do_matiere_list({"ue_id": ue_id}) mats = sco_edit_matiere.matiere_list({"ue_id": ue_id})
ue["matiere"] = mats ue["matiere"] = mats
for mat in mats: for mat in mats:
matiere_id = mat["matiere_id"] matiere_id = mat["matiere_id"]
if not export_ids: if not export_ids:
del mat["matiere_id"] del mat["matiere_id"]
del mat["ue_id"] del mat["ue_id"]
mods = sco_edit_module.do_module_list({"matiere_id": matiere_id}) mods = sco_edit_module.module_list({"matiere_id": matiere_id})
mat["module"] = mods mat["module"] = mods
for mod in mods: for mod in mods:
if export_tags: if export_tags:
@ -130,7 +130,9 @@ def formation_export(formation_id, export_ids=False, export_tags=True, format=No
if mod["ects"] is None: if mod["ects"] is None:
del mod["ects"] del mod["ects"]
return scu.sendResult(F, name="formation", format=format, force_outer_xml_tag=False, attached=True) return scu.sendResult(
F, name="formation", format=format, force_outer_xml_tag=False, attached=True
)
def formation_import_xml(doc: str, import_tags=True): def formation_import_xml(doc: str, import_tags=True):
@ -364,7 +366,7 @@ def formation_create_new_version(formation_id, redirect=True):
if redirect: if redirect:
return flask.redirect( return flask.redirect(
url_for( url_for(
"notes.ue_list", "notes.ue_table",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
formation_id=new_id, formation_id=new_id,
msg="Nouvelle version !", msg="Nouvelle version !",

View File

@ -246,7 +246,7 @@ def do_formsemestre_create(args, silent=False):
default=True, default=True,
redirect=0, redirect=0,
) )
_group_id = sco_groups.createGroup(partition_id, default=True) _group_id = sco_groups.create_group(partition_id, default=True)
# news # news
if "titre" not in args: if "titre" not in args:

View File

@ -51,6 +51,7 @@ from app.scodoc import sco_etud
from app.scodoc import sco_evaluations from app.scodoc import sco_evaluations
from app.scodoc import sco_formations from app.scodoc import sco_formations
from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups_copy
from app.scodoc import sco_modalites from app.scodoc import sco_modalites
from app.scodoc import sco_moduleimpl from app.scodoc import sco_moduleimpl
from app.scodoc import sco_parcours_dut from app.scodoc import sco_parcours_dut
@ -58,7 +59,6 @@ from app.scodoc import sco_permissions_check
from app.scodoc import sco_portal_apogee from app.scodoc import sco_portal_apogee
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_users from app.scodoc import sco_users
import six
def _default_sem_title(F): def _default_sem_title(F):
@ -171,7 +171,7 @@ def do_formsemestre_createwithmodules(edit=False):
initvalues = sem initvalues = sem
semestre_id = initvalues["semestre_id"] semestre_id = initvalues["semestre_id"]
# add associated modules to tf-checked: # add associated modules to tf-checked:
ams = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) ams = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
sem_module_ids = set([x["module_id"] for x in ams]) sem_module_ids = set([x["module_id"] for x in ams])
initvalues["tf-checked"] = ["MI" + str(x["module_id"]) for x in ams] initvalues["tf-checked"] = ["MI" + str(x["module_id"]) for x in ams]
for x in ams: for x in ams:
@ -205,11 +205,11 @@ def do_formsemestre_createwithmodules(edit=False):
# on pourrait faire un simple module_list( ) # on pourrait faire un simple module_list( )
# mais si on veut l'ordre du PPN (groupe par UE et matieres) il faut: # mais si on veut l'ordre du PPN (groupe par UE et matieres) il faut:
mods = [] # liste de dicts mods = [] # liste de dicts
uelist = sco_edit_ue.do_ue_list({"formation_id": formation_id}) uelist = sco_edit_ue.ue_list({"formation_id": formation_id})
for ue in uelist: for ue in uelist:
matlist = sco_edit_matiere.do_matiere_list({"ue_id": ue["ue_id"]}) matlist = sco_edit_matiere.matiere_list({"ue_id": ue["ue_id"]})
for mat in matlist: for mat in matlist:
modsmat = sco_edit_module.do_module_list({"matiere_id": mat["matiere_id"]}) modsmat = sco_edit_module.module_list({"matiere_id": mat["matiere_id"]})
# XXX debug checks # XXX debug checks
for m in modsmat: for m in modsmat:
if m["ue_id"] != ue["ue_id"]: if m["ue_id"] != ue["ue_id"]:
@ -751,7 +751,7 @@ def do_formsemestre_createwithmodules(edit=False):
# (retire le "MI" du début du nom de champs) # (retire le "MI" du début du nom de champs)
checkedmods = [int(x[2:]) for x in tf[2]["tf-checked"]] checkedmods = [int(x[2:]) for x in tf[2]["tf-checked"]]
sco_formsemestre.do_formsemestre_edit(tf[2]) sco_formsemestre.do_formsemestre_edit(tf[2])
ams = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) ams = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
existingmods = [x["module_id"] for x in ams] existingmods = [x["module_id"] for x in ams]
mods_tocreate = [x for x in checkedmods if not x in existingmods] mods_tocreate = [x for x in checkedmods if not x in existingmods]
# modules a existants a modifier # modules a existants a modifier
@ -767,7 +767,7 @@ def do_formsemestre_createwithmodules(edit=False):
"responsable_id": tf[2]["MI" + str(module_id)], "responsable_id": tf[2]["MI" + str(module_id)],
} }
moduleimpl_id = sco_moduleimpl.do_moduleimpl_create(modargs) moduleimpl_id = sco_moduleimpl.do_moduleimpl_create(modargs)
mod = sco_edit_module.do_module_list({"module_id": module_id})[0] mod = sco_edit_module.module_list({"module_id": module_id})[0]
msg += ["création de %s (%s)" % (mod["code"], mod["titre"])] msg += ["création de %s (%s)" % (mod["code"], mod["titre"])]
# INSCRIPTIONS DES ETUDIANTS # INSCRIPTIONS DES ETUDIANTS
log( log(
@ -801,7 +801,7 @@ def do_formsemestre_createwithmodules(edit=False):
ok, diag = formsemestre_delete_moduleimpls(formsemestre_id, mods_todelete) ok, diag = formsemestre_delete_moduleimpls(formsemestre_id, mods_todelete)
msg += diag msg += diag
for module_id in mods_toedit: for module_id in mods_toedit:
moduleimpl_id = sco_moduleimpl.do_moduleimpl_list( moduleimpl_id = sco_moduleimpl.moduleimpl_list(
formsemestre_id=formsemestre_id, module_id=module_id formsemestre_id=formsemestre_id, module_id=module_id
)[0]["moduleimpl_id"] )[0]["moduleimpl_id"]
modargs = { modargs = {
@ -813,7 +813,7 @@ def do_formsemestre_createwithmodules(edit=False):
sco_moduleimpl.do_moduleimpl_edit( sco_moduleimpl.do_moduleimpl_edit(
modargs, formsemestre_id=formsemestre_id modargs, formsemestre_id=formsemestre_id
) )
mod = sco_edit_module.do_module_list({"module_id": module_id})[0] mod = sco_edit_module.module_list({"module_id": module_id})[0]
if msg: if msg:
msg_html = ( msg_html = (
@ -846,10 +846,10 @@ def formsemestre_delete_moduleimpls(formsemestre_id, module_ids_to_del):
msg = [] msg = []
for module_id in module_ids_to_del: for module_id in module_ids_to_del:
# get id # get id
moduleimpl_id = sco_moduleimpl.do_moduleimpl_list( moduleimpl_id = sco_moduleimpl.moduleimpl_list(
formsemestre_id=formsemestre_id, module_id=module_id formsemestre_id=formsemestre_id, module_id=module_id
)[0]["moduleimpl_id"] )[0]["moduleimpl_id"]
mod = sco_edit_module.do_module_list({"module_id": module_id})[0] mod = sco_edit_module.module_list({"module_id": module_id})[0]
# Evaluations dans ce module ? # Evaluations dans ce module ?
evals = sco_evaluations.do_evaluation_list({"moduleimpl_id": moduleimpl_id}) evals = sco_evaluations.do_evaluation_list({"moduleimpl_id": moduleimpl_id})
if evals: if evals:
@ -1015,7 +1015,7 @@ def do_formsemestre_clone(
formsemestre_id = sco_formsemestre.do_formsemestre_create(args) formsemestre_id = sco_formsemestre.do_formsemestre_create(args)
log("created formsemestre %s" % formsemestre_id) log("created formsemestre %s" % formsemestre_id)
# 2- create moduleimpls # 2- create moduleimpls
mods_orig = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=orig_formsemestre_id) mods_orig = sco_moduleimpl.moduleimpl_list(formsemestre_id=orig_formsemestre_id)
for mod_orig in mods_orig: for mod_orig in mods_orig:
args = mod_orig.copy() args = mod_orig.copy()
args["formsemestre_id"] = formsemestre_id args["formsemestre_id"] = formsemestre_id
@ -1074,32 +1074,11 @@ def do_formsemestre_clone(
args["formsemestre_id"] = formsemestre_id args["formsemestre_id"] = formsemestre_id
_ = sco_compute_moy.formsemestre_ue_computation_expr_create(cnx, args) _ = sco_compute_moy.formsemestre_ue_computation_expr_create(cnx, args)
# 5- Copy partitions # 5- Copy partitions and groups
if clone_partitions: if clone_partitions:
listgroups = [] sco_groups_copy.clone_partitions_and_groups(
listnamegroups = [] orig_formsemestre_id, formsemestre_id
# Création des partitions:
for part in sco_groups.get_partitions_list(orig_formsemestre_id):
if part["partition_name"] != None:
partname = part["partition_name"]
new_partition_id = sco_groups.partition_create(
formsemestre_id,
partition_name=partname,
redirect=0,
) )
for g in sco_groups.get_partition_groups(part):
if g["group_name"] != None:
listnamegroups.append(g["group_name"])
listgroups.append([new_partition_id, listnamegroups])
listnamegroups = []
# Création des groupes dans les nouvelles partitions:
for newpart in sco_groups.get_partitions_list(formsemestre_id):
for g in listgroups:
if newpart["partition_id"] == g[0]:
part_id = g[0]
for group_name in g[1]:
_ = sco_groups.createGroup(part_id, group_name=group_name)
return formsemestre_id return formsemestre_id
@ -1212,7 +1191,7 @@ def _reassociate_moduleimpls(cnx, formsemestre_id, ues_old2new, modules_old2new)
et met à jour les décisions de jury (validations d'UE). et met à jour les décisions de jury (validations d'UE).
""" """
# re-associate moduleimpls to new modules: # re-associate moduleimpls to new modules:
modimpls = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) modimpls = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
for mod in modimpls: for mod in modimpls:
mod["module_id"] = modules_old2new[mod["module_id"]] mod["module_id"] = modules_old2new[mod["module_id"]]
sco_moduleimpl.do_moduleimpl_edit(mod, formsemestre_id=formsemestre_id) sco_moduleimpl.do_moduleimpl_edit(mod, formsemestre_id=formsemestre_id)
@ -1329,7 +1308,7 @@ def do_formsemestre_delete(formsemestre_id):
sco_cache.EvaluationCache.invalidate_sem(formsemestre_id) sco_cache.EvaluationCache.invalidate_sem(formsemestre_id)
# --- Destruction des modules de ce semestre # --- Destruction des modules de ce semestre
mods = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) mods = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
for mod in mods: for mod in mods:
# evaluations # evaluations
evals = sco_evaluations.do_evaluation_list( evals = sco_evaluations.do_evaluation_list(

View File

@ -439,7 +439,7 @@ 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.do_ue_list({"formation_id": sem["formation_id"]}) ue_list = sco_edit_ue.ue_list({"formation_id": sem["formation_id"]})
for ue in ue_list: for ue in ue_list:
# add coefficient # add coefficient
uecoef = sco_formsemestre.formsemestre_uecoef_list( uecoef = sco_formsemestre.formsemestre_uecoef_list(

View File

@ -237,7 +237,7 @@ def do_formsemestre_inscription_with_modules(
gdone[group_id] = 1 gdone[group_id] = 1
# inscription a tous les modules de ce semestre # inscription a tous les modules de ce semestre
modimpls = sco_moduleimpl.do_moduleimpl_withmodule_list( modimpls = sco_moduleimpl.moduleimpl_withmodule_list(
formsemestre_id=formsemestre_id formsemestre_id=formsemestre_id
) )
for mod in modimpls: for mod in modimpls:
@ -448,7 +448,7 @@ def formsemestre_inscription_option(etudid, formsemestre_id):
] ]
# Cherche les moduleimpls et les inscriptions # Cherche les moduleimpls et les inscriptions
mods = sco_moduleimpl.do_moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) mods = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
inscr = sco_moduleimpl.do_moduleimpl_inscription_list(etudid=etudid) inscr = sco_moduleimpl.do_moduleimpl_inscription_list(etudid=etudid)
# Formulaire # Formulaire
modimpls_by_ue_ids = scu.DictDefault(defaultvalue=[]) # ue_id : [ moduleimpl_id ] modimpls_by_ue_ids = scu.DictDefault(defaultvalue=[]) # ue_id : [ moduleimpl_id ]
@ -680,7 +680,7 @@ def do_moduleimpl_incription_options(
# inscriptions # inscriptions
for moduleimpl_id in a_inscrire: for moduleimpl_id in a_inscrire:
# verifie que ce module existe bien # verifie que ce module existe bien
mods = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id) mods = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)
if len(mods) != 1: if len(mods) != 1:
raise ScoValueError( raise ScoValueError(
"inscription: invalid moduleimpl_id: %s" % moduleimpl_id "inscription: invalid moduleimpl_id: %s" % moduleimpl_id
@ -693,7 +693,7 @@ def do_moduleimpl_incription_options(
# desinscriptions # desinscriptions
for moduleimpl_id in a_desinscrire: for moduleimpl_id in a_desinscrire:
# verifie que ce module existe bien # verifie que ce module existe bien
mods = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id) mods = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)
if len(mods) != 1: if len(mods) != 1:
raise ScoValueError( raise ScoValueError(
"desinscription: invalid moduleimpl_id: %s" % moduleimpl_id "desinscription: invalid moduleimpl_id: %s" % moduleimpl_id

View File

@ -141,7 +141,7 @@ def formsemestre_status_menubar(sem):
}, },
{ {
"title": "Voir la formation %(acronyme)s (v%(version)s)" % F, "title": "Voir la formation %(acronyme)s (v%(version)s)" % F,
"endpoint": "notes.ue_list", "endpoint": "notes.ue_table",
"args": {"formation_id": sem["formation_id"]}, "args": {"formation_id": sem["formation_id"]},
"enabled": True, "enabled": True,
"helpmsg": "Tableau de bord du semestre", "helpmsg": "Tableau de bord du semestre",
@ -453,7 +453,7 @@ def retreive_formsemestre_from_request() -> int:
if "formsemestre_id" in args: if "formsemestre_id" in args:
formsemestre_id = args["formsemestre_id"] formsemestre_id = args["formsemestre_id"]
elif "moduleimpl_id" in args and args["moduleimpl_id"]: elif "moduleimpl_id" in args and args["moduleimpl_id"]:
modimpl = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=args["moduleimpl_id"]) modimpl = sco_moduleimpl.moduleimpl_list(moduleimpl_id=args["moduleimpl_id"])
if not modimpl: if not modimpl:
return None # suppressed ? return None # suppressed ?
modimpl = modimpl[0] modimpl = modimpl[0]
@ -463,7 +463,7 @@ def retreive_formsemestre_from_request() -> int:
if not E: if not E:
return None # evaluation suppressed ? return None # evaluation suppressed ?
E = E[0] E = E[0]
modimpl = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] modimpl = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
formsemestre_id = modimpl["formsemestre_id"] formsemestre_id = modimpl["formsemestre_id"]
elif "group_id" in args: elif "group_id" in args:
group = sco_groups.get_group(args["group_id"]) group = sco_groups.get_group(args["group_id"])
@ -593,9 +593,7 @@ def formsemestre_description_table(formsemestre_id, with_evals=False):
use_ue_coefs = sco_preferences.get_preference("use_ue_coefs", formsemestre_id) use_ue_coefs = sco_preferences.get_preference("use_ue_coefs", formsemestre_id)
F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0] F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0]
parcours = sco_codes_parcours.get_parcours_from_code(F["type_parcours"]) parcours = sco_codes_parcours.get_parcours_from_code(F["type_parcours"])
Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
formsemestre_id=formsemestre_id
)
R = [] R = []
sum_coef = 0 sum_coef = 0
@ -885,7 +883,7 @@ def html_expr_diagnostic(diagnostics):
last_id, last_msg = None, None last_id, last_msg = None, None
for diag in diagnostics: for diag in diagnostics:
if "moduleimpl_id" in diag: if "moduleimpl_id" in diag:
mod = sco_moduleimpl.do_moduleimpl_withmodule_list( mod = sco_moduleimpl.moduleimpl_withmodule_list(
moduleimpl_id=diag["moduleimpl_id"] moduleimpl_id=diag["moduleimpl_id"]
)[0] )[0]
H.append( H.append(
@ -898,7 +896,7 @@ def html_expr_diagnostic(diagnostics):
) )
else: else:
if diag["ue_id"] != last_id or diag["msg"] != last_msg: if diag["ue_id"] != last_id or diag["msg"] != last_msg:
ue = sco_edit_ue.do_ue_list({"ue_id": diag["ue_id"]})[0] ue = sco_edit_ue.ue_list({"ue_id": diag["ue_id"]})[0]
H.append( H.append(
'<li>UE "%s": %s</li>' '<li>UE "%s": %s</li>'
% (ue["acronyme"] or ue["titre"] or "?", diag["msg"]) % (ue["acronyme"] or ue["titre"] or "?", diag["msg"])
@ -928,7 +926,7 @@ def formsemestre_status_head(formsemestre_id=None, page_title=None):
), ),
f"""<table> f"""<table>
<tr><td class="fichetitre2">Formation: </td><td> <tr><td class="fichetitre2">Formation: </td><td>
<a href="{url_for('notes.ue_list', scodoc_dept=g.scodoc_dept, formation_id=F['formation_id'])}" <a href="{url_for('notes.ue_table', scodoc_dept=g.scodoc_dept, formation_id=F['formation_id'])}"
class="discretelink" title="Formation {F['acronyme']}, v{F['version']}">{F['titre']}</a>""", class="discretelink" title="Formation {F['acronyme']}, v{F['version']}">{F['titre']}</a>""",
] ]
if sem["semestre_id"] >= 0: if sem["semestre_id"] >= 0:
@ -982,9 +980,7 @@ def formsemestre_status(formsemestre_id=None):
# porté du DTML # porté du DTML
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True) sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True)
Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
formsemestre_id=formsemestre_id
)
# inscrits = sco_formsemestre_inscriptions.do_formsemestre_inscription_list( # inscrits = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
# args={"formsemestre_id": formsemestre_id} # args={"formsemestre_id": formsemestre_id}
# ) # )

View File

@ -27,7 +27,7 @@
"""Semestres: validation semestre et UE dans parcours """Semestres: validation semestre et UE dans parcours
""" """
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error, time, datetime import time
import flask import flask
from flask import url_for, g, request from flask import url_for, g, request
@ -494,7 +494,7 @@ def formsemestre_recap_parcours_table(
with_links=False, with_links=False,
with_all_columns=True, with_all_columns=True,
a_url="", a_url="",
sem_info={}, sem_info=None,
show_details=False, show_details=False,
): ):
"""Tableau HTML recap parcours """Tableau HTML recap parcours
@ -502,6 +502,7 @@ def formsemestre_recap_parcours_table(
sem_info = { formsemestre_id : txt } permet d'ajouter des informations associées à chaque semestre sem_info = { formsemestre_id : txt } permet d'ajouter des informations associées à chaque semestre
with_all_columns: si faux, pas de colonne "assiduité". with_all_columns: si faux, pas de colonne "assiduité".
""" """
sem_info = sem_info or {}
H = [] H = []
linktmpl = '<span onclick="toggle_vis(this);" class="toggle_sem sem_%%s">%s</span>' linktmpl = '<span onclick="toggle_vis(this);" class="toggle_sem sem_%%s">%s</span>'
minuslink = linktmpl % scu.icontag("minus_img", border="0", alt="-") minuslink = linktmpl % scu.icontag("minus_img", border="0", alt="-")
@ -1021,7 +1022,7 @@ def formsemestre_validate_previous_ue(formsemestre_id, etudid):
] ]
# Toutes les UE de cette formation sont présentées (même celles des autres semestres) # Toutes les UE de cette formation sont présentées (même celles des autres semestres)
ues = sco_edit_ue.do_ue_list({"formation_id": Fo["formation_id"]}) ues = sco_edit_ue.ue_list({"formation_id": Fo["formation_id"]})
ue_names = ["Choisir..."] + ["%(acronyme)s %(titre)s" % ue for ue in ues] ue_names = ["Choisir..."] + ["%(acronyme)s %(titre)s" % ue for ue in ues]
ue_ids = [""] + [ue["ue_id"] for ue in ues] ue_ids = [""] + [ue["ue_id"] for ue in ues]
tf = TrivialFormulator( tf = TrivialFormulator(
@ -1233,7 +1234,7 @@ def check_formation_ues(formation_id):
définition du programme: cette fonction retourne un bout de HTML définition du programme: cette fonction retourne un bout de HTML
à afficher pour prévenir l'utilisateur, ou '' si tout est ok. à afficher pour prévenir l'utilisateur, ou '' si tout est ok.
""" """
ues = sco_edit_ue.do_ue_list({"formation_id": formation_id}) ues = sco_edit_ue.ue_list({"formation_id": formation_id})
ue_multiples = {} # { ue_id : [ liste des formsemestre ] } ue_multiples = {} # { ue_id : [ liste des formsemestre ] }
for ue in ues: for ue in ues:
# formsemestres utilisant cette ue ? # formsemestres utilisant cette ue ?

View File

@ -47,7 +47,7 @@ from flask import url_for, make_response
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
from app import log from app import log, cache
from app.scodoc.scolog import logdb from app.scodoc.scolog import logdb
from app.scodoc import html_sco_header from app.scodoc import html_sco_header
from app.scodoc import sco_codes_parcours from app.scodoc import sco_codes_parcours
@ -59,20 +59,6 @@ from app.scodoc.sco_exceptions import ScoException, AccessDenied, ScoValueError
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
from app.scodoc.TrivialFormulator import TrivialFormulator from app.scodoc.TrivialFormulator import TrivialFormulator
import six
def checkGroupName(
groupName,
): # XXX unused: now allow any string as a group or partition name
"Raises exception if not a valid group name"
if groupName and (
not re.match(r"^\w+$", groupName)
or (scu.simplesqlquote(groupName) != groupName)
):
log("!!! invalid group name: " + groupName)
raise ValueError("invalid group name: " + groupName)
partitionEditor = ndb.EditableTable( partitionEditor = ndb.EditableTable(
"partition", "partition",
@ -219,7 +205,7 @@ def get_default_group(formsemestre_id, fix_if_missing=False):
partition_id = partition_create( partition_id = partition_create(
formsemestre_id, default=True, redirect=False formsemestre_id, default=True, redirect=False
) )
group_id = createGroup(partition_id, default=True) group_id = create_group(partition_id, default=True)
return group_id return group_id
# debug check # debug check
if len(r) != 1: if len(r) != 1:
@ -452,6 +438,7 @@ def etud_add_group_infos(etud, sem, sep=" "):
return etud return etud
@cache.memoize(timeout=50) # seconds
def get_etud_groups_in_partition(partition_id): def get_etud_groups_in_partition(partition_id):
"""Returns { etudid : group }, with all students in this partition""" """Returns { etudid : group }, with all students in this partition"""
infos = ndb.SimpleDictFetch( infos = ndb.SimpleDictFetch(
@ -724,7 +711,7 @@ def setGroups(
# Supprime les groupes indiqués comme supprimés: # Supprime les groupes indiqués comme supprimés:
for group_id in groupsToDelete: for group_id in groupsToDelete:
suppressGroup(group_id, partition_id=partition_id) delete_group(group_id, partition_id=partition_id)
# Crée les nouveaux groupes # Crée les nouveaux groupes
for line in groupsToCreate.split("\n"): # for each group_name (one per line) for line in groupsToCreate.split("\n"): # for each group_name (one per line)
@ -732,11 +719,7 @@ def setGroups(
group_name = fs[0].strip() group_name = fs[0].strip()
if not group_name: if not group_name:
continue continue
# ajax arguments are encoded in utf-8: group_id = create_group(partition_id, group_name)
# group_name = six.text_type(group_name, "utf-8").encode(
# scu.SCO_ENCODING
# ) # #py3 #sco8
group_id = createGroup(partition_id, group_name)
# Place dans ce groupe les etudiants indiqués: # Place dans ce groupe les etudiants indiqués:
for etudid in fs[1:-1]: for etudid in fs[1:-1]:
change_etud_group_in_partition(etudid, group_id, partition) change_etud_group_in_partition(etudid, group_id, partition)
@ -749,7 +732,7 @@ def setGroups(
return response return response
def createGroup(partition_id, group_name="", default=False): def create_group(partition_id, group_name="", default=False) -> int:
"""Create a new group in this partition""" """Create a new group in this partition"""
partition = get_partition(partition_id) partition = get_partition(partition_id)
formsemestre_id = partition["formsemestre_id"] formsemestre_id = partition["formsemestre_id"]
@ -769,12 +752,12 @@ def createGroup(partition_id, group_name="", default=False):
group_id = groupEditor.create( group_id = groupEditor.create(
cnx, {"partition_id": partition_id, "group_name": group_name} cnx, {"partition_id": partition_id, "group_name": group_name}
) )
log("createGroup: created group_id=%s" % group_id) log("create_group: created group_id=%s" % group_id)
# #
return group_id return group_id
def suppressGroup(group_id, partition_id=None): def delete_group(group_id, partition_id=None):
"""form suppression d'un groupe. """form suppression d'un groupe.
(ne desinscrit pas les etudiants, change juste leur (ne desinscrit pas les etudiants, change juste leur
affectation aux groupes) affectation aux groupes)
@ -791,7 +774,7 @@ def suppressGroup(group_id, partition_id=None):
if not sco_permissions_check.can_change_groups(partition["formsemestre_id"]): if not sco_permissions_check.can_change_groups(partition["formsemestre_id"]):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
log( log(
"suppressGroup: group_id=%s group_name=%s partition_name=%s" "delete_group: group_id=%s group_name=%s partition_name=%s"
% (group_id, group["group_name"], partition["partition_name"]) % (group_id, group["group_name"], partition["partition_name"])
) )
group_delete(group) group_delete(group)
@ -808,7 +791,7 @@ def partition_create(
if not sco_permissions_check.can_change_groups(formsemestre_id): if not sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
if partition_name: if partition_name:
partition_name = partition_name.strip() partition_name = str(partition_name).strip()
if default: if default:
partition_name = None partition_name = None
if not partition_name and not default: if not partition_name and not default:
@ -840,7 +823,7 @@ def partition_create(
return partition_id return partition_id
def getArrowIconsTags(): def get_arrow_icons_tags():
"""returns html tags for arrows""" """returns html tags for arrows"""
# #
arrow_up = scu.icontag("arrow_up", title="remonter") arrow_up = scu.icontag("arrow_up", title="remonter")
@ -856,7 +839,7 @@ def editPartitionForm(formsemestre_id=None):
if not sco_permissions_check.can_change_groups(formsemestre_id): if not sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
partitions = get_partitions_list(formsemestre_id) partitions = get_partitions_list(formsemestre_id)
arrow_up, arrow_down, arrow_none = getArrowIconsTags() arrow_up, arrow_down, arrow_none = get_arrow_icons_tags()
suppricon = scu.icontag( suppricon = scu.icontag(
"delete_small_img", border="0", alt="supprimer", title="Supprimer" "delete_small_img", border="0", alt="supprimer", title="Supprimer"
) )
@ -1123,7 +1106,7 @@ def partition_rename(partition_id):
def partition_set_name(partition_id, partition_name, redirect=1): def partition_set_name(partition_id, partition_name, redirect=1):
"""Set partition name""" """Set partition name"""
partition_name = partition_name.strip() partition_name = str(partition_name).strip()
if not partition_name: if not partition_name:
raise ValueError("partition name must be non empty") raise ValueError("partition name must be non empty")
partition = get_partition(partition_id) partition = get_partition(partition_id)
@ -1159,7 +1142,7 @@ def partition_set_name(partition_id, partition_name, redirect=1):
) )
def group_set_name(group_id, group_name, redirect=1): def group_set_name(group_id, group_name, redirect=True):
"""Set group name""" """Set group name"""
if group_name: if group_name:
group_name = group_name.strip() group_name = group_name.strip()
@ -1229,7 +1212,7 @@ def group_rename(group_id):
) )
else: else:
# form submission # form submission
return group_set_name(group_id, tf[2]["group_name"], redirect=1) return group_set_name(group_id, tf[2]["group_name"])
def groups_auto_repartition(partition_id=None): def groups_auto_repartition(partition_id=None):
@ -1304,7 +1287,7 @@ def groups_auto_repartition(partition_id=None):
# except: # except:
# H.append('<p class="warning">Nom de groupe invalide: %s</p>'%group_name) # H.append('<p class="warning">Nom de groupe invalide: %s</p>'%group_name)
# return '\n'.join(H) + tf[1] + html_sco_header.sco_footer() # return '\n'.join(H) + tf[1] + html_sco_header.sco_footer()
group_ids.append(createGroup(partition_id, group_name)) group_ids.append(create_group(partition_id, group_name))
# #
nt = sco_cache.NotesTableCache.get(formsemestre_id) # > identdict nt = sco_cache.NotesTableCache.get(formsemestre_id) # > identdict
identdict = nt.identdict identdict = nt.identdict
@ -1364,6 +1347,7 @@ def create_etapes_partition(formsemestre_id, partition_name="apo_etapes"):
""" """
from app.scodoc import sco_formsemestre_inscriptions from app.scodoc import sco_formsemestre_inscriptions
partition_name = str(partition_name)
log("create_etapes_partition(%s)" % formsemestre_id) log("create_etapes_partition(%s)" % formsemestre_id)
ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_list( ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
args={"formsemestre_id": formsemestre_id} args={"formsemestre_id": formsemestre_id}
@ -1388,7 +1372,7 @@ def create_etapes_partition(formsemestre_id, partition_name="apo_etapes"):
groups_by_names = {g["group_name"]: g for g in groups} groups_by_names = {g["group_name"]: g for g in groups}
for etape in etapes: for etape in etapes:
if not (etape in groups_by_names): if not (etape in groups_by_names):
gid = createGroup(pid, etape) gid = create_group(pid, etape)
g = get_group(gid) g = get_group(gid)
groups_by_names[etape] = g groups_by_names[etape] = g
# Place les etudiants dans les groupes # Place les etudiants dans les groupes
@ -1409,6 +1393,7 @@ def do_evaluation_listeetuds_groups(
Si include_dems, compte aussi les etudiants démissionnaires Si include_dems, compte aussi les etudiants démissionnaires
(sinon, par défaut, seulement les 'I') (sinon, par défaut, seulement les 'I')
""" """
# nb: pour notes_table / do_evaluation_etat, getallstudents est vrai et include_dems faux
fromtables = [ fromtables = [
"notes_moduleimpl_inscription Im", "notes_moduleimpl_inscription Im",
"notes_formsemestre_inscription Isem", "notes_formsemestre_inscription Isem",
@ -1444,10 +1429,9 @@ def do_evaluation_listeetuds_groups(
req += " and Isem.etat='I'" req += " and Isem.etat='I'"
req += r req += r
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor = cnx.cursor()
cursor.execute(req, {"evaluation_id": evaluation_id}) cursor.execute(req, {"evaluation_id": evaluation_id})
res = cursor.fetchall() return [x[0] for x in cursor]
return [x[0] for x in res]
def do_evaluation_listegroupes(evaluation_id, include_default=False): def do_evaluation_listegroupes(evaluation_id, include_default=False):
@ -1461,7 +1445,7 @@ def do_evaluation_listegroupes(evaluation_id, include_default=False):
else: else:
c = " AND p.partition_name is not NULL" c = " AND p.partition_name is not NULL"
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor = cnx.cursor()
cursor.execute( cursor.execute(
"""SELECT DISTINCT gd.id AS group_id """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,
@ -1475,8 +1459,7 @@ def do_evaluation_listegroupes(evaluation_id, include_default=False):
+ c, + c,
{"evaluation_id": evaluation_id}, {"evaluation_id": evaluation_id},
) )
res = cursor.fetchall() group_ids = [x[0] for x in cursor]
group_ids = [x[0] for x in res]
return listgroups(group_ids) return listgroups(group_ids)

View File

@ -0,0 +1,66 @@
from app import db
from app.scodoc import sco_groups
import app.scodoc.notesdb as ndb
def clone_partitions_and_groups(
orig_formsemestre_id: int, formsemestre_id: int, inscrit_etuds=False
):
"""Crée dans le semestre formsemestre_id les mêmes partitions et groupes que ceux
de orig_formsemestre_id.
Si inscrit_etuds, inscrit les mêmes étudiants (rarement souhaité).
"""
list_groups_per_part = []
list_groups = []
groups_old2new = {} # old group_id : new_group_id
# Création des partitions:
for part in sco_groups.get_partitions_list(orig_formsemestre_id):
if part["partition_name"] is not None:
partname = part["partition_name"]
new_partition_id = sco_groups.partition_create(
formsemestre_id,
partition_name=partname,
numero=part["numero"],
redirect=False,
)
for group in sco_groups.get_partition_groups(part):
if group["group_name"] != None:
list_groups.append(group)
list_groups_per_part.append([new_partition_id, list_groups])
list_groups = []
# Création des groupes dans les nouvelles partitions:
for newpart in sco_groups.get_partitions_list(formsemestre_id):
for (new_partition_id, list_groups) in list_groups_per_part:
if newpart["partition_id"] == new_partition_id:
for group in list_groups:
new_group_id = sco_groups.create_group(
new_partition_id, group_name=group["group_name"]
)
groups_old2new[group["group_id"]] = new_group_id
#
if inscrit_etuds:
cnx = ndb.GetDBConnexion()
cursor = cnx.cursor()
for old_group_id, new_group_id in groups_old2new.items():
cursor.execute(
"""
WITH etuds AS (
SELECT gm.etudid
FROM group_membership gm, notes_formsemestre_inscription ins
WHERE ins.etudid = gm.etudid
AND ins.formsemestre_id = %(orig_formsemestre_id)s
AND gm.group_id=%(old_group_id)s
)
INSERT INTO group_membership (etudid, group_id)
SELECT *, %(new_group_id)s FROM etuds
ON CONFLICT DO NOTHING
""",
{
"orig_formsemestre_id": orig_formsemestre_id,
"old_group_id": old_group_id,
"new_group_id": new_group_id,
},
)
cnx.commit()

View File

@ -42,7 +42,7 @@ def affect_groups(partition_id):
partition = sco_groups.get_partition(partition_id) partition = sco_groups.get_partition(partition_id)
formsemestre_id = partition["formsemestre_id"] formsemestre_id = partition["formsemestre_id"]
if not sco_groups.sco_permissions_check.can_change_groups(formsemestre_id): if not sco_groups.sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("vous n'avez pas la permission d'effectuer cette opération") raise AccessDenied("vous n'avez pas la permission de modifier les groupes")
return render_template( return render_template(
"scolar/affect_groups.html", "scolar/affect_groups.html",
sco_header=html_sco_header.sco_header( sco_header=html_sco_header.sco_header(

View File

@ -304,7 +304,7 @@ class DisplayedGroupsInfos(object):
else: else:
group_ids = [int(g) for g in group_ids] group_ids = [int(g) for g in group_ids]
if not formsemestre_id and moduleimpl_id: if not formsemestre_id and moduleimpl_id:
mods = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id) mods = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)
if len(mods) != 1: if len(mods) != 1:
raise ValueError("invalid moduleimpl_id") raise ValueError("invalid moduleimpl_id")
formsemestre_id = mods[0]["formsemestre_id"] formsemestre_id = mods[0]["formsemestre_id"]
@ -777,13 +777,7 @@ def groups_table(
m["parcours"] = Se.get_parcours_descr() m["parcours"] = Se.get_parcours_descr()
m["codeparcours"], _ = sco_report.get_codeparcoursetud(etud) m["codeparcours"], _ = sco_report.get_codeparcoursetud(etud)
def dicttakestr(d, keys): L = [[m.get(k, "") for k in keys] for m in groups_infos.members]
r = []
for k in keys:
r.append(str(d.get(k, "")))
return r
L = [dicttakestr(m, keys) for m in groups_infos.members]
title = "etudiants_%s" % groups_infos.groups_filename title = "etudiants_%s" % groups_infos.groups_filename
xls = sco_excel.excel_simple_table(titles=titles, lines=L, sheet_name=title) xls = sco_excel.excel_simple_table(titles=titles, lines=L, sheet_name=title)
filename = title filename = title

View File

@ -27,27 +27,23 @@
"""Import d'utilisateurs via fichier Excel """Import d'utilisateurs via fichier Excel
""" """
import random, time import random
import re import time
from email.mime.multipart import MIMEMultipart from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText from flask import g, url_for
from email.header import Header from flask_login import current_user
from app import db, Departement from app import db
from app import email
from app.auth.models import User, UserRole
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app import log from app import log
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError, ScoException from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
from app.scodoc import sco_excel from app.scodoc import sco_excel
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_users from app.scodoc import sco_users
from flask import g
from flask_login import current_user
from app.auth.models import User, UserRole
from app import email
TITLES = ("user_name", "nom", "prenom", "email", "roles", "dept") TITLES = ("user_name", "nom", "prenom", "email", "roles", "dept")
COMMENTS = ( COMMENTS = (
@ -86,11 +82,11 @@ def generate_excel_sample():
) )
def import_excel_file(datafile): def import_excel_file(datafile, force=""):
""" """
Import scodoc users from Excel file. Import scodoc users from Excel file.
This method: This method:
* checks that the current_user has the ability to do so (at the moment only a SuperAdmin). He may thereoff import users with any well formed role into any deprtment (or all) * checks that the current_user has the ability to do so (at the moment only a SuperAdmin). He may thereoff import users with any well formed role into any department (or all)
* Once the check is done ans successfull, build the list of users (does not check the data) * Once the check is done ans successfull, build the list of users (does not check the data)
* call :func:`import_users` to actually do the job * call :func:`import_users` to actually do the job
history: scodoc7 with no SuperAdmin every Admin_XXX could import users. history: scodoc7 with no SuperAdmin every Admin_XXX could import users.
@ -98,7 +94,6 @@ def import_excel_file(datafile):
:return: same as import users :return: same as import users
""" """
# Check current user privilege # Check current user privilege
auth_dept = current_user.dept
auth_name = str(current_user) auth_name = str(current_user)
if not current_user.is_administrator(): if not current_user.is_administrator():
raise AccessDenied("invalid user (%s) must be SuperAdmin" % auth_name) raise AccessDenied("invalid user (%s) must be SuperAdmin" % auth_name)
@ -127,7 +122,8 @@ def import_excel_file(datafile):
del cols[tit] del cols[tit]
if cols or unknown: if cols or unknown:
raise ScoValueError( raise ScoValueError(
"colonnes incorrectes (on attend %d, et non %d) <br/> (colonnes manquantes: %s, colonnes invalides: %s)" """colonnes incorrectes (on attend %d, et non %d) <br/>
(colonnes manquantes: %s, colonnes invalides: %s)"""
% (len(TITLES), len(fs), list(cols.keys()), unknown) % (len(TITLES), len(fs), list(cols.keys()), unknown)
) )
# ok, same titles... : build the list of dictionaries # ok, same titles... : build the list of dictionaries
@ -138,10 +134,10 @@ def import_excel_file(datafile):
d[fs[i]] = line[i] d[fs[i]] = line[i]
users.append(d) users.append(d)
return import_users(users) return import_users(users=users, force=force)
def import_users(users): def import_users(users, force=""):
""" """
Import users from a list of users_descriptors. Import users from a list of users_descriptors.
@ -182,19 +178,17 @@ def import_users(users):
line = line + 1 line = line + 1
user_ok, msg = sco_users.check_modif_user( user_ok, msg = sco_users.check_modif_user(
0, 0,
ignore_optionals=False, enforce_optionals=not force,
user_name=u["user_name"], user_name=u["user_name"],
nom=u["nom"], nom=u["nom"],
prenom=u["prenom"], prenom=u["prenom"],
email=u["email"], email=u["email"],
roles=u["roles"].split(","), roles=[r for r in u["roles"].split(",") if r],
dept=u["dept"], dept=u["dept"],
) )
if not user_ok: if not user_ok:
append_msg("identifiant '%s' %s" % (u["user_name"], msg)) append_msg("identifiant '%s' %s" % (u["user_name"], msg))
# raise ScoValueError(
# "données invalides pour %s: %s" % (u["user_name"], msg)
# )
u["passwd"] = generate_password() u["passwd"] = generate_password()
# #
# check identifiant # check identifiant
@ -224,7 +218,7 @@ def import_users(users):
import_ok = False import_ok = False
except ScoValueError as value_error: except ScoValueError as value_error:
log("import_users: exception: abort create %s" % str(created.keys())) log("import_users: exception: abort create %s" % str(created.keys()))
raise ScoValueError(msg) # re-raise exception raise ScoValueError(msg) from value_error
if import_ok: if import_ok:
for u in created.values(): for u in created.values():
# Création de l'utilisateur (via SQLAlchemy) # Création de l'utilisateur (via SQLAlchemy)
@ -244,7 +238,7 @@ def import_users(users):
ALPHABET = r"""ABCDEFGHIJKLMNPQRSTUVWXYZ123456789123456789AEIOU""" ALPHABET = r"""ABCDEFGHIJKLMNPQRSTUVWXYZ123456789123456789AEIOU"""
PASSLEN = 6 PASSLEN = 8
RNG = random.Random(time.time()) RNG = random.Random(time.time())
@ -259,23 +253,18 @@ def generate_password():
return "".join(RNG.sample(l, PASSLEN)) return "".join(RNG.sample(l, PASSLEN))
def mail_password(u, context=None, reset=False): def mail_password(user: dict, reset=False) -> None:
"Send password by email" "Send password by email"
if not u["email"]: if not user["email"]:
return return
u[ user["url"] = url_for("scodoc.index", _external=True)
"url"
] = (
scu.ScoURL()
) # TODO set auth page URL ? (shared by all departments) ../auth/login
txt = ( txt = (
""" """
Bonjour %(prenom)s %(nom)s, Bonjour %(prenom)s %(nom)s,
""" """
% u % user
) )
if reset: if reset:
txt += ( txt += (
@ -288,7 +277,7 @@ Votre nom d'utilisateur est %(user_name)s
Vous devrez changer ce mot de passe lors de votre première connexion Vous devrez changer ce mot de passe lors de votre première connexion
sur %(url)s sur %(url)s
""" """
% u % user
) )
else: else:
txt += ( txt += (
@ -300,16 +289,15 @@ Votre mot de passe est: %(passwd)s
Le logiciel est accessible sur: %(url)s Le logiciel est accessible sur: %(url)s
Vous êtes invité à changer ce mot de passe au plus vite (cliquez sur Vous êtes invité à changer ce mot de passe au plus vite (cliquez sur votre nom en haut à gauche de la page d'accueil).
votre nom en haut à gauche de la page d'accueil).
""" """
% u % user
) )
txt += ( txt += (
""" """
_______
ScoDoc est un logiciel libre développé à l'Université Paris 13 par Emmanuel Viennet. ScoDoc est un logiciel libre développé par Emmanuel Viennet et l'association ScoDoc.
Pour plus d'informations sur ce logiciel, voir %s Pour plus d'informations sur ce logiciel, voir %s
""" """
@ -321,4 +309,4 @@ Pour plus d'informations sur ce logiciel, voir %s
else: else:
subject = "Votre accès ScoDoc" subject = "Votre accès ScoDoc"
sender = sco_preferences.get_preference("email_from_addr") sender = sco_preferences.get_preference("email_from_addr")
email.send_email(subject, sender, [u["email"]], txt) email.send_email(subject, sender, [user["email"]], txt)

View File

@ -228,8 +228,8 @@ def _make_table_notes(
return "<p>Aucune évaluation !</p>" return "<p>Aucune évaluation !</p>"
E = evals[0] E = evals[0]
moduleimpl_id = E["moduleimpl_id"] moduleimpl_id = E["moduleimpl_id"]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
# (debug) check that all evals are in same module: # (debug) check that all evals are in same module:
for e in evals: for e in evals:
@ -872,9 +872,7 @@ def formsemestre_check_absences_html(formsemestre_id):
</p>""", </p>""",
] ]
# Modules, dans l'ordre # Modules, dans l'ordre
Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
formsemestre_id=formsemestre_id
)
for M in Mlist: for M in Mlist:
evals = sco_evaluations.do_evaluation_list( evals = sco_evaluations.do_evaluation_list(
{"moduleimpl_id": M["moduleimpl_id"]} {"moduleimpl_id": M["moduleimpl_id"]}

View File

@ -100,7 +100,7 @@ def do_moduleimpl_delete(oid, formsemestre_id=None):
) # > moduleimpl_delete ) # > moduleimpl_delete
def do_moduleimpl_list(moduleimpl_id=None, formsemestre_id=None, module_id=None): def moduleimpl_list(moduleimpl_id=None, formsemestre_id=None, module_id=None):
"list moduleimpls" "list moduleimpls"
args = locals() args = locals()
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
@ -122,10 +122,11 @@ def do_moduleimpl_edit(args, formsemestre_id=None, cnx=None):
) # > modif moduleimpl ) # > modif moduleimpl
def do_moduleimpl_withmodule_list( def moduleimpl_withmodule_list(
moduleimpl_id=None, formsemestre_id=None, module_id=None moduleimpl_id=None, formsemestre_id=None, module_id=None
): ):
"""Liste les moduleimpls et ajoute dans chacun le module correspondant """Liste les moduleimpls et ajoute dans chacun
l'UE, la matière et le module auxquels ils appartiennent.
Tri la liste par semestre/UE/numero_matiere/numero_module. Tri la liste par semestre/UE/numero_matiere/numero_module.
Attention: Cette fonction fait partie de l'API ScoDoc 7 et est publiée. Attention: Cette fonction fait partie de l'API ScoDoc 7 et est publiée.
@ -134,22 +135,33 @@ def do_moduleimpl_withmodule_list(
from app.scodoc import sco_edit_matiere from app.scodoc import sco_edit_matiere
from app.scodoc import sco_edit_module from app.scodoc import sco_edit_module
args = locals() modimpls = moduleimpl_list(
modimpls = do_moduleimpl_list(
**{ **{
"moduleimpl_id": moduleimpl_id, "moduleimpl_id": moduleimpl_id,
"formsemestre_id": formsemestre_id, "formsemestre_id": formsemestre_id,
"module_id": module_id, "module_id": module_id,
} }
) )
for mo in modimpls: ues = {}
mo["module"] = sco_edit_module.do_module_list( matieres = {}
args={"module_id": mo["module_id"]} modules = {}
for mi in modimpls:
module_id = mi["module_id"]
if not mi["module_id"] in modules:
modules[module_id] = sco_edit_module.module_list(
args={"module_id": module_id}
)[0] )[0]
mo["ue"] = sco_edit_ue.do_ue_list(args={"ue_id": mo["module"]["ue_id"]})[0] mi["module"] = modules[module_id]
mo["matiere"] = sco_edit_matiere.do_matiere_list( ue_id = mi["module"]["ue_id"]
args={"matiere_id": mo["module"]["matiere_id"]} if not ue_id in ues:
ues[ue_id] = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
mi["ue"] = ues[ue_id]
matiere_id = mi["module"]["matiere_id"]
if not matiere_id in matieres:
matieres[matiere_id] = sco_edit_matiere.matiere_list(
args={"matiere_id": matiere_id}
)[0] )[0]
mi["matiere"] = matieres[matiere_id]
# tri par semestre/UE/numero_matiere/numero_module # tri par semestre/UE/numero_matiere/numero_module
modimpls.sort( modimpls.sort(
@ -173,7 +185,7 @@ def do_moduleimpl_inscription_list(moduleimpl_id=None, etudid=None):
return _moduleimpl_inscriptionEditor.list(cnx, args) return _moduleimpl_inscriptionEditor.list(cnx, args)
def do_moduleimpl_listeetuds(moduleimpl_id): def moduleimpl_listeetuds(moduleimpl_id):
"retourne liste des etudids inscrits a ce module" "retourne liste des etudids inscrits a ce module"
req = """SELECT DISTINCT Im.etudid req = """SELECT DISTINCT Im.etudid
FROM notes_moduleimpl_inscription Im, FROM notes_moduleimpl_inscription Im,
@ -306,7 +318,7 @@ def can_change_module_resp(moduleimpl_id):
"""Check if current user can modify module resp. (raise exception if not). """Check if current user can modify module resp. (raise exception if not).
= Admin, et dir des etud. (si option l'y autorise) = Admin, et dir des etud. (si option l'y autorise)
""" """
M = do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] M = moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0]
# -- check lock # -- check lock
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
if not sem["etat"]: if not sem["etat"]:
@ -322,7 +334,7 @@ def can_change_module_resp(moduleimpl_id):
def can_change_ens(moduleimpl_id, raise_exc=True): def can_change_ens(moduleimpl_id, raise_exc=True):
"check if current user can modify ens list (raise exception if not)" "check if current user can modify ens list (raise exception if not)"
M = do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] M = moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0]
# -- check lock # -- check lock
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
if not sem["etat"]: if not sem["etat"]:

View File

@ -63,9 +63,9 @@ def moduleimpl_inscriptions_edit(moduleimpl_id, etuds=[], submitted=False):
* Si pas les droits: idem en readonly * Si pas les droits: idem en readonly
""" """
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
formsemestre_id = M["formsemestre_id"] formsemestre_id = M["formsemestre_id"]
mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
# -- check lock # -- check lock
if not sem["etat"]: if not sem["etat"]:
@ -263,9 +263,7 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
can_change = authuser.has_permission(Permission.ScoEtudInscrit) and sem["etat"] can_change = authuser.has_permission(Permission.ScoEtudInscrit) and sem["etat"]
# Liste des modules # Liste des modules
Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
formsemestre_id=formsemestre_id
)
# Decrit les inscriptions aux modules: # Decrit les inscriptions aux modules:
commons = [] # modules communs a tous les etuds du semestre commons = [] # modules communs a tous les etuds du semestre
options = [] # modules ou seuls quelques etudiants sont inscrits options = [] # modules ou seuls quelques etudiants sont inscrits
@ -341,7 +339,7 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
UECaps = get_etuds_with_capitalized_ue(formsemestre_id) UECaps = get_etuds_with_capitalized_ue(formsemestre_id)
if UECaps: if UECaps:
H.append('<h3>Etudiants avec UEs capitalisées:</h3><ul class="ue_inscr_list">') H.append('<h3>Etudiants avec UEs capitalisées:</h3><ul class="ue_inscr_list">')
ues = [sco_edit_ue.do_ue_list({"ue_id": ue_id})[0] for ue_id in UECaps.keys()] ues = [sco_edit_ue.ue_list({"ue_id": ue_id})[0] for ue_id in UECaps.keys()]
ues.sort(key=lambda u: u["numero"]) ues.sort(key=lambda u: u["numero"])
for ue in ues: for ue in ues:
H.append( H.append(

View File

@ -58,7 +58,7 @@ from app.scodoc import sco_users
def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0): def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0):
"Menu avec actions sur une evaluation" "Menu avec actions sur une evaluation"
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
modimpl = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] modimpl = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
group_id = sco_groups.get_default_group(modimpl["formsemestre_id"]) group_id = sco_groups.get_default_group(modimpl["formsemestre_id"])
@ -156,9 +156,9 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0):
def moduleimpl_status(moduleimpl_id=None, partition_id=None): def moduleimpl_status(moduleimpl_id=None, partition_id=None):
"""Tableau de bord module (liste des evaluations etc)""" """Tableau de bord module (liste des evaluations etc)"""
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
formsemestre_id = M["formsemestre_id"] formsemestre_id = M["formsemestre_id"]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0] F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0]
ModInscrits = sco_moduleimpl.do_moduleimpl_inscription_list( ModInscrits = sco_moduleimpl.do_moduleimpl_inscription_list(
@ -176,7 +176,7 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
current_user, moduleimpl_id, allow_ens=sem["ens_can_edit_eval"] current_user, moduleimpl_id, allow_ens=sem["ens_can_edit_eval"]
) )
caneditnotes = sco_permissions_check.can_edit_notes(current_user, moduleimpl_id) caneditnotes = sco_permissions_check.can_edit_notes(current_user, moduleimpl_id)
arrow_up, arrow_down, arrow_none = sco_groups.getArrowIconsTags() arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags()
# #
module_resp = User.query.get(M["responsable_id"]) module_resp = User.query.get(M["responsable_id"])
H = [ H = [

View File

@ -174,7 +174,7 @@ def _get_formsemestre_infos_from_news(n):
elif n["type"] == NEWS_NOTE: elif n["type"] == NEWS_NOTE:
moduleimpl_id = n["object"] moduleimpl_id = n["object"]
if n["object"]: if n["object"]:
mods = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id) mods = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)
if not mods: if not mods:
return {} # module does not exists anymore return {} # module does not exists anymore
return {} # pas d'indication du module return {} # pas d'indication du module

View File

@ -350,7 +350,6 @@ def pdf_basic_page(
# Gestion du lock pdf # Gestion du lock pdf
import threading, time, six.moves.queue, six.moves._thread
class PDFLock(object): class PDFLock(object):

View File

@ -26,7 +26,7 @@ def can_edit_notes(authuser, moduleimpl_id, allow_ens=True):
from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre
from app.scodoc import sco_parcours_dut from app.scodoc import sco_parcours_dut
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
if not sem["etat"]: if not sem["etat"]:
return False # semestre verrouillé return False # semestre verrouillé
@ -64,7 +64,7 @@ def can_edit_evaluation(moduleimpl_id=None):
# acces pour resp. moduleimpl et resp. form semestre (dir etud) # acces pour resp. moduleimpl et resp. form semestre (dir etud)
if moduleimpl_id is None: if moduleimpl_id is None:
raise ValueError("no moduleimpl specified") # bug raise ValueError("no moduleimpl specified") # bug
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
if ( if (

View File

@ -244,10 +244,10 @@ class PlacementRunner:
# gr_title = sco_groups.listgroups_abbrev(d['groups']) # gr_title = sco_groups.listgroups_abbrev(d['groups'])
self.current_user = current_user self.current_user = current_user
self.moduleimpl_id = self.eval_data["moduleimpl_id"] self.moduleimpl_id = self.eval_data["moduleimpl_id"]
self.moduleimpl_data = sco_moduleimpl.do_moduleimpl_list( self.moduleimpl_data = sco_moduleimpl.moduleimpl_list(
moduleimpl_id=self.moduleimpl_id moduleimpl_id=self.moduleimpl_id
)[0] )[0]
self.module_data = sco_edit_module.do_module_list( self.module_data = sco_edit_module.module_list(
args={"module_id": self.moduleimpl_data["module_id"]} args={"module_id": self.moduleimpl_data["module_id"]}
)[0] )[0]
self.sem = sco_formsemestre.get_formsemestre( self.sem = sco_formsemestre.get_formsemestre(

View File

@ -39,7 +39,6 @@ import app.scodoc.sco_utils as scu
from app import log from app import log
from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
import six
SCO_CACHE_ETAPE_FILENAME = os.path.join(scu.SCO_TMP_DIR, "last_etapes.xml") SCO_CACHE_ETAPE_FILENAME = os.path.join(scu.SCO_TMP_DIR, "last_etapes.xml")
@ -386,7 +385,8 @@ def get_etapes_apogee():
# cache le resultat (utile si le portail repond de façon intermitente) # cache le resultat (utile si le portail repond de façon intermitente)
if infos: if infos:
log("get_etapes_apogee: caching result") log("get_etapes_apogee: caching result")
open(SCO_CACHE_ETAPE_FILENAME, "w").write(doc) with open(SCO_CACHE_ETAPE_FILENAME, "w") as f:
f.write(doc)
except: except:
log("invalid XML response from getEtapes Web Service\n%s" % etapes_url) log("invalid XML response from getEtapes Web Service\n%s" % etapes_url)
# Avons nous la copie d'une réponse récente ? # Avons nous la copie d'une réponse récente ?

View File

@ -92,7 +92,7 @@ def _descr_decisions_ues(nt, etudid, decisions_ue, decision_sem):
and sco_codes_parcours.code_semestre_validant(decision_sem["code"]) and sco_codes_parcours.code_semestre_validant(decision_sem["code"])
) )
): ):
ue = sco_edit_ue.do_ue_list(args={"ue_id": ue_id})[0] ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
uelist.append(ue) uelist.append(ue)
except: except:
log("descr_decisions_ues: ue_id=%s decisions_ue=%s" % (ue_id, decisions_ue)) log("descr_decisions_ues: ue_id=%s decisions_ue=%s" % (ue_id, decisions_ue))

View File

@ -31,7 +31,6 @@
""" """
import os import os
import tempfile import tempfile
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
import re import re
import time import time
import datetime import datetime
@ -1399,7 +1398,8 @@ def graph_parcours(
# Genere graphe # Genere graphe
_, path = tempfile.mkstemp(".gr") _, path = tempfile.mkstemp(".gr")
g.write(path=path, format=format) g.write(path=path, format=format)
data = open(path, "rb").read() with open(path, "rb") as f:
data = f.read()
log("dot generated %d bytes in %s format" % (len(data), format)) log("dot generated %d bytes in %s format" % (len(data), format))
if not data: if not data:
log("graph.to_string=%s" % g.to_string()) log("graph.to_string=%s" % g.to_string())

View File

@ -45,6 +45,7 @@ from app.scodoc.sco_exceptions import (
AccessDenied, AccessDenied,
InvalidNoteValue, InvalidNoteValue,
NoteProcessError, NoteProcessError,
ScoGenError,
ScoValueError, ScoValueError,
) )
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@ -72,13 +73,16 @@ def convert_note_from_string(
note_max, note_max,
note_min=scu.NOTES_MIN, note_min=scu.NOTES_MIN,
etudid=None, etudid=None,
absents=[], absents=None,
tosuppress=[], tosuppress=None,
invalids=[], invalids=None,
): ):
"""converti une valeur (chaine saisie) vers une note numérique (float) """converti une valeur (chaine saisie) vers une note numérique (float)
Les listes absents, tosuppress et invalids sont modifiées Les listes absents, tosuppress et invalids sont modifiées
""" """
absents = absents or []
tosuppress = tosuppress or []
invalids = invalids or []
invalid = False invalid = False
note_value = None note_value = None
note = note.replace(",", ".") note = note.replace(",", ".")
@ -173,13 +177,10 @@ def do_evaluation_upload_xls():
evaluation_id = int(vals["evaluation_id"]) evaluation_id = int(vals["evaluation_id"])
comment = vals["comment"] comment = vals["comment"]
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[ M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[0]
0
]
# Check access # Check access
# (admin, respformation, and responsable_id) # (admin, respformation, and responsable_id)
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]): if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]):
# XXX imaginer un redirect + msg erreur
raise AccessDenied("Modification des notes impossible pour %s" % authuser) raise AccessDenied("Modification des notes impossible pour %s" % authuser)
# #
diag, lines = sco_excel.excel_file_to_list(vals["notefile"]) diag, lines = sco_excel.excel_file_to_list(vals["notefile"])
@ -250,8 +251,8 @@ def do_evaluation_upload_xls():
) )
# news # news
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
mod["moduleimpl_id"] = M["moduleimpl_id"] mod["moduleimpl_id"] = M["moduleimpl_id"]
mod["url"] = url_for( mod["url"] = url_for(
"notes.moduleimpl_status", "notes.moduleimpl_status",
@ -289,9 +290,7 @@ def do_evaluation_upload_xls():
def do_evaluation_set_missing(evaluation_id, value, dialog_confirmed=False): def do_evaluation_set_missing(evaluation_id, value, dialog_confirmed=False):
"""Initialisation des notes manquantes""" """Initialisation des notes manquantes"""
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[ M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[0]
0
]
# Check access # Check access
# (admin, respformation, and responsable_id) # (admin, respformation, and responsable_id)
if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]): if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]):
@ -337,8 +336,8 @@ def do_evaluation_set_missing(evaluation_id, value, dialog_confirmed=False):
comment = "Initialisation notes manquantes" comment = "Initialisation notes manquantes"
nb_changed, _, _ = _notes_add(current_user, evaluation_id, L, comment) nb_changed, _, _ = _notes_add(current_user, evaluation_id, L, comment)
# news # news
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
mod["moduleimpl_id"] = M["moduleimpl_id"] mod["moduleimpl_id"] = M["moduleimpl_id"]
mod["url"] = url_for( mod["url"] = url_for(
"notes.moduleimpl_status", "notes.moduleimpl_status",
@ -426,8 +425,8 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
% E["moduleimpl_id"] % E["moduleimpl_id"]
] ]
# news # news
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
mod["moduleimpl_id"] = M["moduleimpl_id"] mod["moduleimpl_id"] = M["moduleimpl_id"]
mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
sco_news.add( sco_news.add(
@ -473,7 +472,7 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
nb_changed = 0 nb_changed = 0
nb_suppress = 0 nb_suppress = 0
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
existing_decisions = ( existing_decisions = (
[] []
) # etudids pour lesquels il y a une decision de jury et que la note change ) # etudids pour lesquels il y a une decision de jury et que la note change
@ -573,12 +572,13 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
except: except:
log("*** exception in _notes_add") log("*** exception in _notes_add")
if do_it: if do_it:
cnx.rollback() # abort
# inval cache # inval cache
sco_cache.invalidate_formsemestre( sco_cache.invalidate_formsemestre(
formsemestre_id=M["formsemestre_id"] formsemestre_id=M["formsemestre_id"]
) # > modif notes (exception) ) # > modif notes (exception)
cnx.rollback() # abort sco_cache.EvaluationCache.delete(evaluation_id)
raise # re-raise exception raise ScoGenError("Erreur enregistrement note: merci de ré-essayer")
if do_it: if do_it:
cnx.commit() cnx.commit()
sco_cache.invalidate_formsemestre( sco_cache.invalidate_formsemestre(
@ -588,13 +588,13 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
return nb_changed, nb_suppress, existing_decisions return nb_changed, nb_suppress, existing_decisions
def saisie_notes_tableur(evaluation_id, group_ids=[]): def saisie_notes_tableur(evaluation_id, group_ids=()):
"""Saisie des notes via un fichier Excel""" """Saisie des notes via un fichier Excel"""
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id}) evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
if not evals: if not evals:
raise ScoValueError("invalid evaluation_id") raise ScoValueError("invalid evaluation_id")
E = evals[0] E = evals[0]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
formsemestre_id = M["formsemestre_id"] formsemestre_id = M["formsemestre_id"]
if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]): if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]):
return ( return (
@ -765,9 +765,9 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]):
if not evals: if not evals:
raise ScoValueError("invalid evaluation_id") raise ScoValueError("invalid evaluation_id")
E = evals[0] E = evals[0]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
formsemestre_id = M["formsemestre_id"] formsemestre_id = M["formsemestre_id"]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
mod_responsable = sco_users.user_info(M["responsable_id"]) mod_responsable = sco_users.user_info(M["responsable_id"])
if E["jour"]: if E["jour"]:
@ -845,7 +845,7 @@ def has_existing_decision(M, E, etudid):
return True return True
dec_ues = nt.get_etud_decision_ues(etudid) dec_ues = nt.get_etud_decision_ues(etudid)
if dec_ues: if dec_ues:
mod = sco_edit_module.do_module_list({"module_id": M["module_id"]})[0] mod = sco_edit_module.module_list({"module_id": M["module_id"]})[0]
ue_id = mod["ue_id"] ue_id = mod["ue_id"]
if ue_id in dec_ues: if ue_id in dec_ues:
return True # decision pour l'UE a laquelle appartient cette evaluation return True # decision pour l'UE a laquelle appartient cette evaluation
@ -864,9 +864,7 @@ def saisie_notes(evaluation_id, group_ids=[]):
if not evals: if not evals:
raise ScoValueError("invalid evaluation_id") raise ScoValueError("invalid evaluation_id")
E = evals[0] E = evals[0]
M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[ M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[0]
0
]
formsemestre_id = M["formsemestre_id"] formsemestre_id = M["formsemestre_id"]
# Check access # Check access
# (admin, respformation, and responsable_id) # (admin, respformation, and responsable_id)
@ -1226,8 +1224,8 @@ def save_note(etudid=None, evaluation_id=None, value=None, comment=""):
% (evaluation_id, etudid, authuser, value) % (evaluation_id, etudid, authuser, value)
) )
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
Mod["url"] = url_for( Mod["url"] = url_for(
"notes.moduleimpl_status", "notes.moduleimpl_status",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,

View File

@ -242,7 +242,7 @@ def module_tag_set(module_id="", taglist=None):
taglist = [t.strip() for t in taglist] taglist = [t.strip() for t in taglist]
# log("module_tag_set: module_id=%s taglist=%s" % (module_id, taglist)) # log("module_tag_set: module_id=%s taglist=%s" % (module_id, taglist))
# Sanity check: # Sanity check:
Mod = sco_edit_module.do_module_list(args={"module_id": module_id}) Mod = sco_edit_module.module_list(args={"module_id": module_id})
if not Mod: if not Mod:
raise ScoValueError("invalid module !") raise ScoValueError("invalid module !")

View File

@ -174,9 +174,7 @@ def external_ue_inscrit_et_note(moduleimpl_id, formsemestre_id, notes_etuds):
def get_existing_external_ue(formation_id): def get_existing_external_ue(formation_id):
"la liste de toutes les UE externes définies dans cette formation" "la liste de toutes les UE externes définies dans cette formation"
return sco_edit_ue.do_ue_list( return sco_edit_ue.ue_list(args={"formation_id": formation_id, "is_external": True})
args={"formation_id": formation_id, "is_external": True}
)
def get_external_moduleimpl_id(formsemestre_id, ue_id): def get_external_moduleimpl_id(formsemestre_id, ue_id):

View File

@ -149,7 +149,7 @@ def list_operations(evaluation_id):
def evaluation_list_operations(evaluation_id): def evaluation_list_operations(evaluation_id):
"""Page listing operations on evaluation""" """Page listing operations on evaluation"""
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
Ops = list_operations(evaluation_id) Ops = list_operations(evaluation_id)

View File

@ -32,9 +32,9 @@
import re import re
from flask import url_for, g, request from flask import url_for, g, request
from flask.templating import render_template
from flask_login import current_user from flask_login import current_user
import cracklib # pylint: disable=import-error
from app import db, Departement from app import db, Departement
@ -56,23 +56,6 @@ from app.scodoc.sco_exceptions import (
) )
def is_valid_password(cleartxt):
"""Check password.
returns True if OK.
"""
if (
hasattr(scu.CONFIG, "MIN_PASSWORD_LENGTH")
and scu.CONFIG.MIN_PASSWORD_LENGTH > 0
and len(cleartxt) < scu.CONFIG.MIN_PASSWORD_LENGTH
):
return False # invalid: too short
try:
_ = cracklib.FascistCheck(cleartxt)
return True
except ValueError:
return False
# --------------- # ---------------
# --------------- # ---------------
@ -289,105 +272,9 @@ def user_info(user_name_or_id=None, user=None):
return info return info
def user_info_page(user_name=None):
"""Display page of info about given user.
If user_name not specified, user current_user
"""
from app.scodoc.sco_permissions_check import can_handle_passwd
# peut on divulguer ces infos ?
if not can_handle_passwd(current_user, allow_admindepts=True):
raise AccessDenied("Vous n'avez pas la permission de voir cette page")
dept = g.scodoc_dept
if not user_name:
user = current_user
else:
user = User.query.filter_by(user_name=user_name).first()
if not user:
raise ScoValueError("invalid user_name")
H = [
html_sco_header.sco_header(
page_title="Utilisateur %s" % user.user_name,
)
]
F = html_sco_header.sco_footer()
H.append("<h2>Utilisateur: %s" % user.user_name)
info = user.to_dict()
if info:
H.append(" (%(status_txt)s)" % info)
H.append("</h2>")
if not info:
H.append(
"<p>L' utilisateur '%s' n'est pas défini dans ce module.</p>" % user_name
)
if user.has_permission(Permission.ScoEditAllNotes, dept):
H.append("<p>(il peut modifier toutes les notes de %s)</p>" % dept)
if user.has_permission(Permission.ScoEditAllEvals, dept):
H.append("<p>(il peut modifier toutes les évaluations de %s)</p>" % dept)
if user.has_permission(Permission.ScoImplement, dept):
H.append("<p>(il peut creer des formations en %s)</p>" % dept)
else:
H.append(
"""<p>
<b>Login :</b> %(user_name)s<br/>
<b>Nom :</b> %(nom)s<br/>
<b>Prénom :</b> %(prenom)s<br/>
<b>Mail :</b> %(email)s<br/>
<b>Roles :</b> %(roles_string)s<br/>
<b>Dept :</b> %(dept)s<br/>
<b>Dernière modif mot de passe:</b> %(date_modif_passwd)s<br/>
<b>Date d'expiration:</b> %(date_expiration)s
<p><ul>
<li><a class="stdlink" href="form_change_password?user_name=%(user_name)s">changer le mot de passe</a></li>"""
% info
)
if current_user.has_permission(Permission.ScoUsersAdmin, dept):
H.append(
f"""
<li><a class="stdlink" href="{url_for('users.create_user_form', scodoc_dept=g.scodoc_dept,
user_name=user.user_name, edit=1)}">modifier ce compte</a>
</li>
<li><a class="stdlink" href="{url_for('users.toggle_active_user', scodoc_dept=g.scodoc_dept,
user_name=user.user_name)
}">{"désactiver" if user.active else "activer"} ce compte</a>
</li>
"""
% info
)
H.append("</ul>")
if current_user.user_name == user_name:
H.append(
'<p><b>Se déconnecter: <a class="stdlink" href="%s">logout</a></b></p>'
% url_for("auth.logout")
)
# Liste des permissions
H.append(
'<div class="permissions"><p>Permissions de cet utilisateur dans le département %s:</p><ul>'
% dept
)
for p in Permission.description:
perm = getattr(Permission, p)
if user.has_permission(perm, dept):
b = "oui"
else:
b = "non"
H.append("<li>%s : %s</li>" % (Permission.description[p], b))
H.append("</ul></div>")
if current_user.has_permission(Permission.ScoUsersAdmin, dept):
H.append(
'<p><a class="stdlink" href="%s">Liste de tous les utilisateurs</a></p>'
% url_for("users.index_html", scodoc_dept=g.scodoc_dept)
)
return "\n".join(H) + F
def check_modif_user( def check_modif_user(
edit, edit,
ignore_optionals=False, enforce_optionals=False,
user_name="", user_name="",
nom="", nom="",
prenom="", prenom="",
@ -400,9 +287,9 @@ def check_modif_user(
returns (ok, msg) returns (ok, msg)
- ok : si vrai, peut continuer avec ces parametres - ok : si vrai, peut continuer avec ces parametres
(si ok est faux, l'utilisateur peut quand même forcer la creation) (si ok est faux, l'utilisateur peut quand même forcer la creation)
- msg: message warning a presenter l'utilisateur - msg: message warning à presenter à l'utilisateur
""" """
MSG_OPT = """Attention: %s (vous pouvez forcer l'opération en cochant "<em>Ignorer les avertissements</em>" en bas de page)""" MSG_OPT = """<br/>Attention: (vous pouvez forcer l'opération en cochant "<em>Ignorer les avertissements</em>" en bas de page)"""
# ce login existe ? # ce login existe ?
user = _user_list(user_name) user = _user_list(user_name)
if edit and not user: # safety net, le user_name ne devrait pas changer if edit and not user: # safety net, le user_name ne devrait pas changer
@ -417,11 +304,11 @@ def check_modif_user(
"identifiant '%s' invalide (pas d'accents ni de caractères spéciaux)" "identifiant '%s' invalide (pas d'accents ni de caractères spéciaux)"
% user_name, % user_name,
) )
if ignore_optionals and len(user_name) > 64: if enforce_optionals and len(user_name) > 64:
return False, "identifiant '%s' trop long (64 caractères)" % user_name return False, "identifiant '%s' trop long (64 caractères)" % user_name
if ignore_optionals and len(nom) > 64: if enforce_optionals and len(nom) > 64:
return False, "nom '%s' trop long (64 caractères)" % nom + MSG_OPT return False, "nom '%s' trop long (64 caractères)" % nom + MSG_OPT
if ignore_optionals and len(prenom) > 64: if enforce_optionals and len(prenom) > 64:
return False, "prenom '%s' trop long (64 caractères)" % prenom + MSG_OPT return False, "prenom '%s' trop long (64 caractères)" % prenom + MSG_OPT
# check that tha same user_name has not already been described in this import # check that tha same user_name has not already been described in this import
if not email: if not email:
@ -432,13 +319,22 @@ def check_modif_user(
return False, "l'adresse mail semble incorrecte" return False, "l'adresse mail semble incorrecte"
# check département # check département
if ( if (
ignore_optionals enforce_optionals
and dept != "" and dept != ""
and Departement.query.filter_by(acronym=dept).first() is None and Departement.query.filter_by(acronym=dept).first() is None
): ):
return False, "département '%s' inexistant" % u["dept"] + MSG_OPT return False, "département '%s' inexistant" % dept + MSG_OPT
if ignore_optionals and not roles: if enforce_optionals and not roles:
return False, "aucun rôle sélectionné, êtes vous sûr ?" + MSG_OPT return False, "aucun rôle sélectionné, êtes vous sûr ?" + MSG_OPT
# Unicité du mail
users_with_this_mail = User.query.filter_by(email=email).all()
if edit: # modification
if email != user["email"] and len(users_with_this_mail) > 0:
return False, "un autre utilisateur existe déjà avec cette adresse mail"
else: # création utilisateur
if len(users_with_this_mail) > 0:
return False, "un autre utilisateur existe déjà avec cette adresse mail"
# ok # ok
# Des noms/prénoms semblables existent ? # Des noms/prénoms semblables existent ?
nom = nom.lower().strip() nom = nom.lower().strip()
@ -450,7 +346,7 @@ def check_modif_user(
minmatch = 1 minmatch = 1
else: else:
minmatch = 0 minmatch = 0
if len(similar_users) > minmatch: if enforce_optionals and len(similar_users) > minmatch:
return ( return (
False, False,
"des utilisateurs proches existent: " "des utilisateurs proches existent: "

View File

@ -290,8 +290,7 @@ SCO_DEV_MAIL = "emmanuel.viennet@gmail.com" # SVP ne pas changer
# Adresse pour l'envoi des dumps (pour assistance technnique): # Adresse pour l'envoi des dumps (pour assistance technnique):
# ne pas changer (ou vous perdez le support) # ne pas changer (ou vous perdez le support)
SCO_DUMP_UP_URL = "https://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/upload-dump" SCO_DUMP_UP_URL = "https://scodoc.org/scodoc-installmgr/upload-dump"
# SCO_DUMP_UP_URL = "http://192.168.56.1:5000/upload_dump"
CSV_FIELDSEP = ";" CSV_FIELDSEP = ";"
CSV_LINESEP = "\n" CSV_LINESEP = "\n"

View File

@ -0,0 +1,30 @@
function refresh() {
if ($("input[name='welcome:list']").is(":checked")) {
$("input[name='reset_password:list']").closest("tr").css("display", "table-row")
if ($("input[name='reset_password:list']").is(":checked")) {
$("#tf_password").closest('tr').css("display", "none");
$("#tf_password2").closest('tr').css("display", "none");
} else {
// Le mot de passe doit être saisi
$("#tf_password").closest('tr').css("display", "table-row");
$("#tf_password2").closest('tr').css("display", "table-row");
}
} else {
// Le mot de passe doit être saisi
$("input[name='reset_password:list']").closest("tr").css("display", "none")
$("#tf_password").closest('tr').css("display", "table-row");
$("#tf_password2").closest('tr').css("display", "table-row");
}
}
$(function () {
$("input[name='welcome:list']").click(function () {
refresh();
})
$("input[name='reset_password:list']").click(function () {
refresh();
})
refresh();
})

View File

@ -0,0 +1,42 @@
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% macro render_field(field) %}
<tr style="">
<td class="wtf-field">{{ field.label }}</td>
<td class="wtf-field">{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</td>
</tr>
{% endmacro %}
{% block app_content %}
<h1>Modification du compte ScoDoc <tt>{{form.user_name.data}}</tt></h1>
<div class="help">
<p>Identifiez-vous avez votre mot de passe actuel</p>
<p>Vous pouvez changer le mot de passe et/ou l'adresse email.</p>
<p>Les champs vides ne seront pas changés.</p>
</div>
<form method=post>
{{ form.user_name }}
{{ form.csrf_token }}
<table class="tf"><tbody>
{{ render_field(form.old_password, size=14,
style="padding:1px; margin-left: 1em; margin-top: 4px;") }}
{{ render_field(form.new_password, size=14,
style="padding:1px; margin-left: 1em; margin-top: 12px;") }}
{{ render_field(form.bis_password, size=14,
style="padding:1px; margin-left: 1em; margin-top: 4px;") }}
{{ render_field(form.email, size=40,
style="padding:1px; margin-top: 12px;margin-bottom: 16px; margin-left: 1em;") }}
</tbody></table>
<input type="submit" value="Valider">
<input type="submit" name="cancel" value="Annuler" style="margin-left: 1em;>
</form>
{% endblock %}

View File

@ -2,6 +2,11 @@
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}
{% if message %}
<div class="alert alert-danger" role="alert">{{ message }}</div>
{% endif %}
<h1>Connexion</h1> <h1>Connexion</h1>
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">

View File

@ -2,8 +2,13 @@
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}
<h1>Reset Your Password</h1> <h1>Changez votre mot de passe ScoDoc</h1>
<div class="row">
<div class="row" style="margin-top: 30px;">
<div class="col-md-4">Votre identifiant: <b>{{user.user_name}}</b></div>
</div>
<div class="row" style="margin-top: 30px;">
<div class="col-md-4"> <div class="col-md-4">
{{ wtf.quick_form(form) }} {{ wtf.quick_form(form) }}
</div> </div>

View File

@ -2,7 +2,7 @@
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}
<h1>Reset Password</h1> <h1>Demande d'un nouveau mot de passe</h1>
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
{{ wtf.quick_form(form) }} {{ wtf.quick_form(form) }}

View File

@ -0,0 +1,67 @@
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h2>Utilisateur: {{user.user_name}} ({{'actif' if user.active else 'fermé'}})</h2>
<p>
<b>Login :</b> {{user.user_name}}<br/>
<b>Nom :</b> {{user.nom or ""}}<br/>
<b>Prénom :</b> {{user.prenom or ""}}<br/>
<b>Mail :</b> {{user.email}}<br/>
<b>Roles :</b> {{user.get_roles_string()}}<br/>
<b>Dept :</b> {{user.dept or ""}}<br/>
<b>Dernière modif mot de passe:</b>
{{user.date_modif_passwd.isoformat() if user.date_modif_passwd else ""}}<br/>
<b>Date d'expiration:</b>
{{user.date_expiration.isoformat() if user.date_expiration else "(sans limite)"}}
<p>
<ul>
<li><a class="stdlink" href="{{
url_for( 'users.form_change_password',
scodoc_dept=g.scodoc_dept, user_name=user.user_name)
}}">modifier le mot de passe ou l'adresse mail</a>
</li>
{% if current_user.has_permission(Permission.ScoUsersAdmin, dept) %}
<li><a class="stdlink" href="{{
url_for('users.create_user_form', scodoc_dept=g.scodoc_dept,
user_name=user.user_name, edit=1)
}}">modifier ce compte</a>
</li>
<li><a class="stdlink" href="{{
url_for('users.toggle_active_user', scodoc_dept=g.scodoc_dept,
user_name=user.user_name)
}}">{{"désactiver" if user.active else "activer"}} ce compte</a>
</li>
{% endif %}
</ul>
{% if current_user.id == user.id %}
<p><b>Se déconnecter:
<a class="stdlink" href="{{url_for('auth.logout')}}">logout</a>
</b></p>
{% endif %}
{# Liste des permissions #}
<div class="permissions">
<p>Permissions de cet utilisateur dans le département {dept}:</p>
<ul>
{% for p in Permission.description %}
<li>{{Permission.description[p]}} :
{{
"oui" if user.has_permission(Permission.get_by_name(p), dept) else "non"
}}
</li>
{% endfor %}
</ul>
</div>
{% if current_user.has_permission(Permission.ScoUsersAdmin, dept) %}
<p><a class="stdlink" href="
{{url_for('users.index_html', scodoc_dept=g.scodoc_dept)}}
">Liste de tous les utilisateurs</a></p>
{% endif %}
{% endblock %}

View File

@ -22,12 +22,19 @@
</button> </button>
<a class="navbar-brand" href="{{ url_for('scodoc.index') }}">ScoDoc</a> <a class="navbar-brand" href="{{ url_for('scodoc.index') }}">ScoDoc</a>
</div> </div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
{% if current_user.is_administrator() %}
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li><a href="{{ url_for('scodoc.configuration') }}">configuration</a></li> {% if current_user.is_administrator() %}
</ul> <li><a href="{{ url_for('scodoc.configuration') }}">Configuration</a></li>
{% endif %} {% endif %}
{% if g.scodoc_dept %}
<li><a href="{{
url_for('scolar.index_html', scodoc_dept=g.scodoc_dept)
}}">Dept. {{ g.scodoc_dept }}</a></li>
{% endif %}
</ul>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
{% if current_user.is_anonymous %} {% if current_user.is_anonymous %}
<li><a href="{{ url_for('auth.login') }}">connexion</a></li> <li><a href="{{ url_for('auth.login') }}">connexion</a></li>

View File

@ -5,7 +5,7 @@
cliquez sur ce lien cliquez sur ce lien
</a>. </a>.
</p> </p>
<p>Vous pouvez aussi copier ce lien dans votre navigateur Web::</p> <p>Vous pouvez aussi copier ce lien dans votre navigateur Web:</p>
<p>{{ url_for('auth.reset_password', token=token, _external=True) }}</p> <p>{{ url_for('auth.reset_password', token=token, _external=True) }}</p>
<p>Si vous n'avez pas demandé à réinitialiser votre mot de passe sur <p>Si vous n'avez pas demandé à réinitialiser votre mot de passe sur

View File

@ -0,0 +1,18 @@
<p>Bienvenue {{ user.prenom }} {{ user.nom }},</p>
<p>
Votre accès à ScoDoc vient d'être validé.
</p>
<p>
Votre identifiant de connexion est: <b>{{ user.user_name }}</b>
</p>
{% if token %}
<p>Pour initialiser votre mot de passe ScoDoc,
<a href="{{ url_for('auth.reset_password', token=token, _external=True) }}">
cliquez sur ce lien
</a>.
</p>
<p>Vous pouvez aussi copier ce lien dans votre navigateur Web:</p>
<p>{{ url_for('auth.reset_password', token=token, _external=True) }}</p>
{% endif %}
<p>A bientôt !</p>

View File

@ -0,0 +1,11 @@
Bienvenue {{ user.prenom }} {{ user.nom }},
Votre accès à ScoDoc vient d'être validé.
Votre identifiant de connexion est: {{ user.user_name }}
{% if token %}
Pour initialiser votre mot de passe ScoDoc, suivre le lien:
{{ url_for('auth.reset_password', token=token, _external=True) }}
{% endif %}
<p>A bientôt !</p>

View File

@ -27,7 +27,7 @@ def start_scodoc_request():
"""Affecte toutes les requêtes, de tous les blueprints""" """Affecte toutes les requêtes, de tous les blueprints"""
# current_app.logger.info(f"start_scodoc_request") # current_app.logger.info(f"start_scodoc_request")
ndb.open_db_connection() ndb.open_db_connection()
if current_user.is_authenticated: if current_user and current_user.is_authenticated:
current_user.last_seen = datetime.datetime.utcnow() current_user.last_seen = datetime.datetime.utcnow()
db.session.commit() db.session.commit()
# caches locaux (durée de vie=la requête en cours) # caches locaux (durée de vie=la requête en cours)

View File

@ -35,6 +35,7 @@ Note: Code très ancien, porté de Zope/DTML, peu utilisable
import time import time
import urllib
from flask import request from flask import request
from flask_login import current_user from flask_login import current_user
@ -166,14 +167,14 @@ def index_html(etud_nom=None, limit=50, offset="", format="html"):
if offset: if offset:
webparams["offset"] = max((offset or 0) - limit, 0) webparams["offset"] = max((offset or 0) - limit, 0)
prev_lnk = '<a class="stdlink" href="%s">précédentes</a>' % ( prev_lnk = '<a class="stdlink" href="%s">précédentes</a>' % (
request.base_url + "?" + six.moves.urllib.parse.urlencode(webparams) request.base_url + "?" + urllib.parse.urlencode(webparams)
) )
else: else:
prev_lnk = "" prev_lnk = ""
if len(entreprises) >= limit: if len(entreprises) >= limit:
webparams["offset"] = (offset or 0) + limit webparams["offset"] = (offset or 0) + limit
next_lnk = '<a class="stdlink" href="%s">suivantes</a>' % ( next_lnk = '<a class="stdlink" href="%s">suivantes</a>' % (
request.base_url + "?" + six.moves.urllib.parse.urlencode(webparams) request.base_url + "?" + urllib.parse.urlencode(webparams)
) )
else: else:
next_lnk = "" next_lnk = ""

View File

@ -160,7 +160,7 @@ def sco_publish(route, function, permission, methods=["GET"]):
# <p class="help">Il faut d'abord supprimer le semestre. Mais il est peut être préférable de # <p class="help">Il faut d'abord supprimer le semestre. Mais il est peut être préférable de
# laisser ce programme intact et d'en créer une nouvelle version pour la modifier. # laisser ce programme intact et d'en créer une nouvelle version pour la modifier.
# </p> # </p>
# <a href="url_for('notes.ue_list', scodoc-dept=g.scodoc_dept, formation_id='XXX')">reprendre</a> # <a href="url_for('notes.ue_table', scodoc-dept=g.scodoc_dept, formation_id='XXX')">reprendre</a>
# """ # """
# raise ScoGenError(err_page) # raise ScoGenError(err_page)
# # raise ScoGenError("une erreur banale") # # raise ScoGenError("une erreur banale")
@ -334,7 +334,7 @@ sco_publish(
Permission.ScoChangeFormation, Permission.ScoChangeFormation,
methods=["GET", "POST"], methods=["GET", "POST"],
) )
sco_publish("/ue_list", sco_edit_ue.ue_list, Permission.ScoView) sco_publish("/ue_list", sco_edit_ue.ue_table, Permission.ScoView)
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",
@ -396,7 +396,7 @@ sco_publish(
sco_edit_module.edit_module_set_code_apogee, sco_edit_module.edit_module_set_code_apogee,
Permission.ScoChangeFormation, Permission.ScoChangeFormation,
) )
sco_publish("/module_list", sco_edit_module.module_list, Permission.ScoView) sco_publish("/module_list", sco_edit_module.module_table, Permission.ScoView)
sco_publish("/module_tag_search", sco_tag_module.module_tag_search, Permission.ScoView) sco_publish("/module_tag_search", sco_tag_module.module_tag_search, Permission.ScoView)
sco_publish( sco_publish(
"/module_tag_set", "/module_tag_set",
@ -548,8 +548,8 @@ sco_publish(
) )
sco_publish( sco_publish(
"/do_ue_list", "/ue_list",
sco_edit_ue.do_ue_list, sco_edit_ue.ue_list,
Permission.ScoView, Permission.ScoView,
) )
@ -1059,11 +1059,11 @@ def edit_moduleimpl_expr(moduleimpl_id):
@scodoc7func @scodoc7func
def view_module_abs(moduleimpl_id, format="html"): def view_module_abs(moduleimpl_id, format="html"):
"""Visualisation des absences a un module""" """Visualisation des absences a un module"""
M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0]
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
debut_sem = ndb.DateDMYtoISO(sem["date_debut"]) debut_sem = ndb.DateDMYtoISO(sem["date_debut"])
fin_sem = ndb.DateDMYtoISO(sem["date_fin"]) fin_sem = ndb.DateDMYtoISO(sem["date_fin"])
list_insc = sco_moduleimpl.do_moduleimpl_listeetuds(moduleimpl_id) list_insc = sco_moduleimpl.moduleimpl_listeetuds(moduleimpl_id)
T = [] T = []
for etudid in list_insc: for etudid in list_insc:
@ -1142,7 +1142,7 @@ def edit_ue_expr(formsemestre_id, ue_id):
raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération") raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération")
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
# #
ue = sco_edit_ue.do_ue_list({"ue_id": ue_id})[0] ue = sco_edit_ue.ue_list({"ue_id": ue_id})[0]
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
"Modification règle de calcul de l'UE %s (%s)" "Modification règle de calcul de l'UE %s (%s)"
@ -1213,7 +1213,7 @@ def formsemestre_enseignants_list(formsemestre_id, format="html"):
""" """
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
# resp. de modules: # resp. de modules:
mods = sco_moduleimpl.do_moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) mods = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
sem_ens = {} sem_ens = {}
for mod in mods: for mod in mods:
if not mod["responsable_id"] in sem_ens: if not mod["responsable_id"] in sem_ens:
@ -1527,8 +1527,8 @@ def evaluation_delete(evaluation_id):
if not El: if not El:
raise ValueError("Evalution inexistante ! (%s)" % evaluation_id) raise ValueError("Evalution inexistante ! (%s)" % evaluation_id)
E = El[0] E = El[0]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
tit = "Suppression de l'évaluation %(description)s (%(jour)s)" % E tit = "Suppression de l'évaluation %(description)s (%(jour)s)" % E
etat = sco_evaluations.do_evaluation_etat(evaluation_id) etat = sco_evaluations.do_evaluation_etat(evaluation_id)
H = [ H = [
@ -2424,14 +2424,14 @@ def check_sem_integrity(formsemestre_id, fix=False):
""" """
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
modimpls = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) modimpls = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
bad_ue = [] bad_ue = []
bad_sem = [] bad_sem = []
formations_set = set() # les formations mentionnées dans les UE et modules formations_set = set() # les formations mentionnées dans les UE et modules
for modimpl in modimpls: for modimpl in modimpls:
mod = sco_edit_module.do_module_list({"module_id": modimpl["module_id"]})[0] mod = sco_edit_module.module_list({"module_id": modimpl["module_id"]})[0]
formations_set.add(mod["formation_id"]) formations_set.add(mod["formation_id"])
ue = sco_edit_ue.do_ue_list({"ue_id": mod["ue_id"]})[0] ue = sco_edit_ue.ue_list({"ue_id": mod["ue_id"]})[0]
formations_set.add(ue["formation_id"]) formations_set.add(ue["formation_id"])
if ue["formation_id"] != mod["formation_id"]: if ue["formation_id"] != mod["formation_id"]:
modimpl["mod"] = mod modimpl["mod"] = mod
@ -2490,12 +2490,12 @@ def check_sem_integrity(formsemestre_id, fix=False):
def check_form_integrity(formation_id, fix=False): def check_form_integrity(formation_id, fix=False):
"debug" "debug"
log("check_form_integrity: formation_id=%s fix=%s" % (formation_id, fix)) log("check_form_integrity: formation_id=%s fix=%s" % (formation_id, fix))
ues = sco_edit_ue.do_ue_list(args={"formation_id": formation_id}) ues = sco_edit_ue.ue_list(args={"formation_id": formation_id})
bad = [] bad = []
for ue in ues: for ue in ues:
mats = sco_edit_matiere.do_matiere_list(args={"ue_id": ue["ue_id"]}) mats = sco_edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]})
for mat in mats: for mat in mats:
mods = sco_edit_module.do_module_list({"matiere_id": mat["matiere_id"]}) mods = sco_edit_module.module_list({"matiere_id": mat["matiere_id"]})
for mod in mods: for mod in mods:
if mod["ue_id"] != ue["ue_id"]: if mod["ue_id"] != ue["ue_id"]:
if fix: if fix:
@ -2534,9 +2534,7 @@ def check_formsemestre_integrity(formsemestre_id):
# de formations # de formations
diag = [] diag = []
Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
formsemestre_id=formsemestre_id
)
for mod in Mlist: for mod in Mlist:
if mod["module"]["ue_id"] != mod["matiere"]["ue_id"]: if mod["module"]["ue_id"] != mod["matiere"]["ue_id"]:
diag.append( diag.append(
@ -2593,14 +2591,14 @@ def check_integrity_all():
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Support for legacy ScoDoc 7 API # Support for legacy ScoDoc 7 API
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@bp.route("/do_moduleimpl_list") @bp.route("/moduleimpl_list")
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@scodoc7func @scodoc7func
def do_moduleimpl_list( def moduleimpl_list(
moduleimpl_id=None, formsemestre_id=None, module_id=None, format="json" moduleimpl_id=None, formsemestre_id=None, module_id=None, format="json"
): ):
data = sco_moduleimpl.do_moduleimpl_list( data = sco_moduleimpl.moduleimpl_list(
moduleimpl_id=moduleimpl_id, moduleimpl_id=moduleimpl_id,
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
module_id=module_id, module_id=module_id,
@ -2608,15 +2606,16 @@ def do_moduleimpl_list(
return scu.sendResult(data, format=format) return scu.sendResult(data, format=format)
@bp.route("/do_moduleimpl_withmodule_list") @bp.route("/do_moduleimpl_withmodule_list") # ancien nom
@bp.route("/moduleimpl_withmodule_list")
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@scodoc7func @scodoc7func
def do_moduleimpl_withmodule_list( def moduleimpl_withmodule_list(
moduleimpl_id=None, formsemestre_id=None, module_id=None moduleimpl_id=None, formsemestre_id=None, module_id=None
): ):
"""API ScoDoc 7""" """API ScoDoc 7"""
data = sco_moduleimpl.do_moduleimpl_withmodule_list( data = sco_moduleimpl.moduleimpl_withmodule_list(
moduleimpl_id=moduleimpl_id, moduleimpl_id=moduleimpl_id,
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
module_id=module_id, module_id=module_id,

View File

@ -669,9 +669,18 @@ sco_publish(
sco_publish("/setGroups", sco_groups.setGroups, Permission.ScoView) sco_publish("/setGroups", sco_groups.setGroups, Permission.ScoView)
sco_publish("/createGroup", sco_groups.createGroup, Permission.ScoView) sco_publish("/create_group", sco_groups.create_group, Permission.ScoView)
@bp.route("/suppressGroup") # backward compat (ScoDoc7 API)
@bp.route("/delete_group")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func
def delete_group(group_id, partition_id):
sco_groups.delete_group(group_id=group_id, partition_id=partition_id)
return "", 204
sco_publish("/suppressGroup", sco_groups.suppressGroup, Permission.ScoView)
sco_publish( sco_publish(
"/group_set_name", "/group_set_name",

View File

@ -38,10 +38,12 @@ import re
from xml.etree import ElementTree from xml.etree import ElementTree
import flask import flask
from flask import g, url_for, request from flask import g, url_for, request, current_app, flash
from flask import redirect, render_template from flask import redirect, render_template
from flask_login import current_user from flask_login import current_user
from wtforms import HiddenField, PasswordField, StringField, SubmitField
from wtforms.validators import DataRequired, Email, ValidationError, EqualTo
from app import db from app import db
from app.auth.forms import DeactivateUserForm from app.auth.forms import DeactivateUserForm
@ -49,6 +51,8 @@ from app.auth.models import Permission
from app.auth.models import User from app.auth.models import User
from app.auth.models import Role from app.auth.models import Role
from app.auth.models import UserRole from app.auth.models import UserRole
from app.auth.models import is_valid_password
from app.email import send_email
from app.models import Departement from app.models import Departement
from app.decorators import ( from app.decorators import (
@ -63,9 +67,53 @@ from app.scodoc import sco_utils as scu
from app.scodoc import sco_xml from app.scodoc import sco_xml
from app import log from app import log
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
from app.scodoc.sco_import_users import generate_password
from app.scodoc.sco_permissions_check import can_handle_passwd from app.scodoc.sco_permissions_check import can_handle_passwd
from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
from app.views import users_bp as bp from app.views import users_bp as bp
from flask_wtf import FlaskForm
_ = lambda x: x # sans babel
_l = _
class ChangePasswordForm(FlaskForm):
user_name = HiddenField()
old_password = PasswordField(_l("Identifiez-vous"))
new_password = PasswordField(_l("Nouveau mot de passe"))
bis_password = PasswordField(
_l("Répéter"),
validators=[
EqualTo(
"new_password",
message="Les deux saisies sont " "différentes, recommencez",
),
],
)
email = StringField(
_l("Email"),
validators=[
DataRequired(),
Email(message="adresse email invalide, recommencez"),
],
)
submit = SubmitField()
cancel = SubmitField("Annuler")
def validate_email(self, email):
user = User.query.filter_by(email=email.data.strip()).first()
if user is not None and self.user_name.data != user.user_name:
raise ValidationError(
_("Cette adresse e-mail est déjà attribuée à un autre compte")
)
def validate_new_password(self, new_password):
if new_password.data != "" and not is_valid_password(new_password.data):
raise ValidationError(f"Mot de passe trop simple, recommencez")
def validate_old_password(self, old_password):
if not current_user.check_password(old_password.data):
raise ValidationError("Mot de passe actuel incorrect, ré-essayez")
@bp.route("/") @bp.route("/")
@ -100,7 +148,12 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
initvalues = {} initvalues = {}
edit = int(edit) edit = int(edit)
all_roles = int(all_roles) all_roles = int(all_roles)
H = [html_sco_header.sco_header(bodyOnLoad="init_tf_form('')")] H = [
html_sco_header.sco_header(
bodyOnLoad="init_tf_form('')",
javascripts=["js/user_form.js"],
)
]
F = html_sco_header.sco_footer() F = html_sco_header.sco_footer()
if edit: if edit:
if not user_name: if not user_name:
@ -214,9 +267,11 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
"title": "Pseudo (login)", "title": "Pseudo (login)",
"size": 20, "size": 20,
"allow_null": False, "allow_null": False,
"explanation": "nom utilisé pour la connexion. Doit être unique parmi tous les utilisateurs.", "explanation": "nom utilisé pour la connexion. Doit être unique parmi tous les utilisateurs. "
"Lettres ou chiffres uniquement.",
}, },
), ),
("formsemestre_id", {"input_type": "hidden"}),
( (
"password", "password",
{ {
@ -257,6 +312,32 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
}, },
) )
] ]
if not edit: # options création utilisateur
descr += [
(
"welcome",
{
"title": "Message d'accueil",
"input_type": "checkbox",
"explanation": "Envoie un mail d'accueil à l'utilisateur.",
"labels": ("",),
"allowed_values": ("1",),
"default": "1",
},
),
(
"reset_password",
{
"title": "",
"input_type": "checkbox",
"explanation": "indiquer par mail de changer le mot de passe initial",
"labels": ("",),
"allowed_values": ("1",),
"default": "1",
# "attributes": ["style='margin-left:20pt'"],
},
),
]
if not auth_dept: if not auth_dept:
# si auth n'a pas de departement (admin global) # si auth n'a pas de departement (admin global)
@ -380,14 +461,14 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
if err: if err:
H.append(tf_error_message("""Erreur: %s""" % err)) H.append(tf_error_message("""Erreur: %s""" % err))
return "\n".join(H) + "\n" + tf[1] + F return "\n".join(H) + "\n" + tf[1] + F
ok, msg = sco_users.check_modif_user( ok, msg = sco_users.check_modif_user(
edit, edit,
ignore_optionals=force, enforce_optionals=not force,
user_name=user_name, user_name=user_name,
nom=vals["nom"], nom=vals["nom"],
prenom=vals["prenom"], prenom=vals["prenom"],
email=vals["email"], email=vals["email"],
dept=vals.get("dept", auth_dept),
roles=vals["roles"], roles=vals["roles"],
) )
if not ok: if not ok:
@ -400,6 +481,9 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
vals["date_expiration"] = datetime.datetime.strptime( vals["date_expiration"] = datetime.datetime.strptime(
vals["date_expiration"], "%d/%m/%Y" vals["date_expiration"], "%d/%m/%Y"
) )
if vals["date_expiration"] < datetime.datetime.now():
H.append(tf_error_message("date expiration passée"))
return "\n".join(H) + "\n" + tf[1] + F
else: else:
vals["date_expiration"] = None vals["date_expiration"] = None
except ValueError: except ValueError:
@ -444,14 +528,29 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
"identifiant invalide (pas d'accents ni de caractères spéciaux)" "identifiant invalide (pas d'accents ni de caractères spéciaux)"
) )
return "\n".join(H) + msg + "\n" + tf[1] + F return "\n".join(H) + msg + "\n" + tf[1] + F
# Traitement initial (mode) : 3 cas
# A: envoi de welcome + procedure de reset
# B: envoi de welcome seulement (mot de passe saisie dans le formulaire)
# C: Aucun envoi (mot de passe saisi dans le formulaire)
if vals["welcome:list"] == "1":
if vals["reset_password:list"] == "1":
mode = "A"
else:
mode = "B"
else:
mode = "C"
# check passwords # check passwords
if mode == "A":
vals["password"] = generate_password()
else:
if vals["password"]: if vals["password"]:
if vals["password"] != vals["password2"]: if vals["password"] != vals["password2"]:
msg = tf_error_message( msg = tf_error_message(
"""Les deux mots de passes ne correspondent pas !""" """Les deux mots de passes ne correspondent pas !"""
) )
return "\n".join(H) + msg + "\n" + tf[1] + F return "\n".join(H) + msg + "\n" + tf[1] + F
if not sco_users.is_valid_password(vals["password"]): if not is_valid_password(vals["password"]):
msg = tf_error_message( msg = tf_error_message(
"""Mot de passe trop simple, recommencez !""" """Mot de passe trop simple, recommencez !"""
) )
@ -467,6 +566,22 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
u.from_dict(vals, new_user=True) u.from_dict(vals, new_user=True)
db.session.add(u) db.session.add(u)
db.session.commit() db.session.commit()
# envoi éventuel d'un message
if mode == "A" or mode == "B":
if mode == "A":
token = u.get_reset_password_token()
else:
token = None
send_email(
"[ScoDoc] Création de votre compte",
sender=current_app.config["ADMINS"][0],
recipients=[u.email],
text_body=render_template("email/welcome.txt", user=u, token=token),
html_body=render_template(
"email/welcome.html", user=u, token=token
),
)
return flask.redirect( return flask.redirect(
url_for( url_for(
"users.user_info_page", "users.user_info_page",
@ -531,6 +646,16 @@ def import_users_form():
"xlsfile", "xlsfile",
{"title": "Fichier Excel:", "input_type": "file", "size": 40}, {"title": "Fichier Excel:", "input_type": "file", "size": 40},
), ),
(
"force",
{
"title": "Ignorer les avertissements",
"input_type": "checkbox",
"explanation": "passer outre les avertissements (homonymes, etc)",
"labels": ("",),
"allowed_values": ("1",),
},
),
("formsemestre_id", {"input_type": "hidden"}), ("formsemestre_id", {"input_type": "hidden"}),
), ),
submitlabel="Télécharger", submitlabel="Télécharger",
@ -541,14 +666,16 @@ def import_users_form():
return flask.redirect(url_for("scolar.index_html", docodc_dept=g.scodoc_dept)) return flask.redirect(url_for("scolar.index_html", docodc_dept=g.scodoc_dept))
else: else:
# IMPORT # IMPORT
ok, diag, nb_created = sco_import_users.import_excel_file(tf[2]["xlsfile"]) ok, diag, nb_created = sco_import_users.import_excel_file(
tf[2]["xlsfile"], tf[2]["force"]
)
H = [html_sco_header.sco_header(page_title="Import utilisateurs")] H = [html_sco_header.sco_header(page_title="Import utilisateurs")]
H.append("<ul>") H.append("<ul>")
for d in diag: for d in diag:
H.append("<li>%s</li>" % d) H.append("<li>%s</li>" % d)
H.append("</ul>") H.append("</ul>")
if ok: if ok:
dest = url_for("users.index_html", scodoc_dept=g.scodoc_dept) dest = url_for("users.index_html", scodoc_dept=g.scodoc_dept, all_depts=1)
H.append("<p>Ok, Import terminé (%s utilisateurs créés)!</p>" % nb_created) H.append("<p>Ok, Import terminé (%s utilisateurs créés)!</p>" % nb_created)
H.append('<p><a class="stdlink" href="%s">Continuer</a></p>' % dest) H.append('<p><a class="stdlink" href="%s">Continuer</a></p>' % dest)
else: else:
@ -562,8 +689,31 @@ def import_users_form():
@scodoc @scodoc
@permission_required(Permission.ScoUsersView) @permission_required(Permission.ScoUsersView)
@scodoc7func @scodoc7func
def user_info_page(user_name): def user_info_page(user_name=None):
return sco_users.user_info_page(user_name=user_name) """Display page of info about given user.
If user_name not specified, user current_user
"""
from app.scodoc.sco_permissions_check import can_handle_passwd
# peut on divulguer ces infos ?
if not can_handle_passwd(current_user, allow_admindepts=True):
raise AccessDenied("Vous n'avez pas la permission de voir cette page")
dept = g.scodoc_dept
if not user_name:
user = current_user
else:
user = User.query.filter_by(user_name=user_name).first()
if not user:
raise ScoValueError("invalid user_name")
return render_template(
"auth/user_info_page.html",
user=user,
title=f"Utilisateur {user.user_name}",
Permission=Permission,
dept=dept,
)
@bp.route("/get_user_list_xml") @bp.route("/get_user_list_xml")
@ -594,7 +744,7 @@ def get_user_list_xml(dept=None, start="", limit=25):
return scu.send_file(data, mime=scu.XML_MIMETYPE) return scu.send_file(data, mime=scu.XML_MIMETYPE)
@bp.route("/form_change_password") @bp.route("/form_change_password", methods=["GET", "POST"])
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@scodoc7func @scodoc7func
@ -603,36 +753,42 @@ def form_change_password(user_name=None):
Un utilisateur peut toujours changer son propre mot de passe. Un utilisateur peut toujours changer son propre mot de passe.
""" """
if not user_name: if not user_name:
u = current_user user = current_user
else: else:
u = User.query.filter_by(user_name=user_name).first() user = User.query.filter_by(user_name=user_name).first()
H = [html_sco_header.sco_header(user_check=False)]
F = html_sco_header.sco_footer()
# check access # check access
if not can_handle_passwd(u): if not can_handle_passwd(user):
return ( return "\n".join(
"\n".join(H) [
+ "<p>Vous n'avez pas la permission de changer ce mot de passe</p>" html_sco_header.sco_header(user_check=False),
+ F "<p>Vous n'avez pas la permission de changer ce mot de passe</p>",
html_sco_header.sco_footer(),
]
) )
# form = ChangePasswordForm(user_name=user.user_name, email=user.email)
H.append( destination = url_for(
"""<h2>Changement du mot de passe de <font color="red">%(nomplogin)s</font></h2> "users.user_info_page",
<p> scodoc_dept=g.scodoc_dept,
<form action="change_password" method="post"><table> user_name=user_name,
<tr><td>Nouveau mot de passe:</td><td><input type="password" size="14" name="password"/></td></tr> )
<tr><td>Confirmation: </td><td><input type="password" size="14" name="password2" /></td></tr> if request.method == "POST" and form.cancel.data: # cancel button clicked
</table> return redirect(destination)
<input type="hidden" value="%(user_name)s" name="user_name"> if form.validate_on_submit():
<input type="submit" value="Changer"> messages = []
</p> if form.new_password.data != "": # change password
<p class="help">Note: en ScoDoc 9, les utilisateurs peuvent changer eux-même leur mot de passe user.set_password(form.new_password.data)
en indiquant l'adresse mail associée à leur compte. messages.append("Mot de passe modifié")
</p> if form.email.data.strip() != user.email: # change email
""" user.email = form.email.data.strip()
% {"nomplogin": u.get_nomplogin(), "user_name": user_name} messages.append("Adresse email modifiée")
db.session.commit()
flash("\n".join(messages))
return redirect(destination)
return render_template(
"auth/change_password.html", form=form, title="Modification compte ScoDoc"
) )
return "\n".join(H) + F
@bp.route("/change_password", methods=["POST"]) @bp.route("/change_password", methods=["POST"])
@ -660,7 +816,7 @@ def change_password(user_name, password, password2):
% user_name % user_name
) )
else: else:
if not sco_users.is_valid_password(password): if not is_valid_password(password):
H.append( H.append(
"""<p><b>ce mot de passe n\'est pas assez compliqué !</b><br/>(oui, il faut un mot de passe vraiment compliqué !)</p> """<p><b>ce mot de passe n\'est pas assez compliqué !</b><br/>(oui, il faut un mot de passe vraiment compliqué !)</p>
<p><a href="form_change_password?user_name=%s" class="stdlink">Recommencer</a></p> <p><a href="form_change_password?user_name=%s" class="stdlink">Recommencer</a></p>

13
bench.py Normal file
View File

@ -0,0 +1,13 @@
"""Script pour faciliter le lancement de benchmarks
"""
from tests.bench.notes_table import bench_notes_table
BENCH_DEPT = "RT"
BENCH_FORMSEMESTRE_IDS = (
149, # RT S1 2020-21
145, # RT S2 2021
119, # RT S1 2029
)
bench_notes_table(BENCH_DEPT, BENCH_FORMSEMESTRE_IDS)

View File

@ -1,57 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Change un identifiant d'enseignant (pour corriger une erreur, typiquement un doublon)
(à lancer en tant qu'utilisateur postgres)
Emmanuel Viennet, 2007 - 2014
"""
from __future__ import print_function
import pdb, os, sys
import psycopg2
from six.moves import input
if len(sys.argv) != 4:
print("Usage: %s database ancien_utilisateur nouvel_utilisateur" % sys.argv[0])
print("Exemple: change_enseignant.py SCOGEII toto tata")
sys.exit(1)
dbname = sys.argv[1]
OLD_ID = sys.argv[2]
NEW_ID = sys.argv[3]
DBCNXSTRING = "dbname=%s" % dbname
# Confirmation
ans = input(
"Remplacer le l'utilisateur %s par %s dans toute la base du departement %s ?"
% (OLD_ID, NEW_ID, dbname)
).strip()
if not ans or ans[0].lower() not in "oOyY":
print("annulation")
sys.exit(-1)
cnx = psycopg2.connect(DBCNXSTRING)
cursor = cnx.cursor()
req = "update %s set %s=%%(new_id)s where %s=%%(old_id)s"
args = {"old_id": OLD_ID, "new_id": NEW_ID}
tables_attr = {
"notes_formsemestre": "responsable_id",
"entreprise_contact": "enseignant",
"admissions": "rapporteur",
"notes_moduleimpl": "responsable_id",
"notes_modules_enseignants": "ens_id",
"notes_notes": "uid",
"notes_notes_log": "uid",
"notes_appreciations": "author",
}
for (table, attr) in tables_attr.items():
cursor.execute(req % (table, attr, attr), args)
print("table %s: %s" % (table, cursor.statusmessage))
cnx.commit()

View File

@ -78,7 +78,7 @@ pp(sem)
# semdescr = GET(s, f"Notes/formsemestre_description?formsemestre_id={sem['formsemestre_id']}&with_evals=0&format=json" ) # semdescr = GET(s, f"Notes/formsemestre_description?formsemestre_id={sem['formsemestre_id']}&with_evals=0&format=json" )
# ---- Liste les modules et prend le premier # ---- Liste les modules et prend le premier
mods = GET(s, f"/Notes/do_moduleimpl_list?formsemestre_id={sem['formsemestre_id']}") mods = GET(s, f"/Notes/moduleimpl_list?formsemestre_id={sem['formsemestre_id']}")
print(f"{len(mods)} modules dans le semestre {sem['titre']}") print(f"{len(mods)} modules dans le semestre {sem['titre']}")
mod = mods[0] mod = mods[0]

View File

@ -58,7 +58,6 @@ redis==3.5.3
reportlab==3.6.1 reportlab==3.6.1
requests==2.26.0 requests==2.26.0
rq==1.9.0 rq==1.9.0
six==1.16.0
SQLAlchemy==1.4.22 SQLAlchemy==1.4.22
toml==0.10.2 toml==0.10.2
urllib3==1.26.6 urllib3==1.26.6

View File

@ -0,0 +1,50 @@
# Simple benchmark
# mesure temps execution NotesTable
import time
from flask import g
from flask_login import login_user
from config import RunningConfig as BenchConfig
import app
from app import db, create_app
from app import clear_scodoc_cache
from app.auth.models import get_super_admin
from app.scodoc import notesdb as ndb
from app.scodoc import notes_table
def setup_generator(dept: str):
# Setup
apptest = create_app(BenchConfig)
# Run tests:
with apptest.test_client() as client:
with apptest.app_context():
with apptest.test_request_context():
# Clear application cache:
print("clearing cache...")
clear_scodoc_cache()
# initialize scodoc "g":
g.stored_get_formsemestre = {}
# Loge l'utilisateur super-admin
admin_user = get_super_admin()
login_user(admin_user)
app.set_sco_dept(dept) # set db connection
yield client
ndb.close_db_connection()
# Teardown:
db.session.commit()
db.session.remove()
def bench_notes_table(dept: str, formsemestre_ids: list[int]) -> float:
for client in setup_generator(dept):
tot_time = 0.0
for formsemestre_id in formsemestre_ids:
print(f"building sem {formsemestre_id}...")
t0 = time.time()
nt = notes_table.NotesTable(formsemestre_id)
tot_time += time.time() - t0
print(f"Total time: {tot_time}")
return tot_time

View File

@ -52,7 +52,7 @@ def run_scenario1():
] ]
# --- Implémentation des modules # --- Implémentation des modules
modules = sco_edit_module.do_module_list({"formation_id": formation_id}) modules = sco_edit_module.module_list({"formation_id": formation_id})
mods_imp = [] mods_imp = []
for mod in modules: for mod in modules:
mi = G.create_moduleimpl( mi = G.create_moduleimpl(

View File

@ -170,7 +170,7 @@ class ScoFake(object):
if numero is None: if numero is None:
numero = sco_edit_ue.next_ue_numero(formation_id, 0) numero = sco_edit_ue.next_ue_numero(formation_id, 0)
oid = sco_edit_ue.do_ue_create(locals()) oid = sco_edit_ue.do_ue_create(locals())
oids = sco_edit_ue.do_ue_list(args={"ue_id": oid}) oids = sco_edit_ue.ue_list(args={"ue_id": oid})
if not oids: if not oids:
raise ScoValueError("ue not created !") raise ScoValueError("ue not created !")
return oids[0] return oids[0]
@ -178,7 +178,7 @@ class ScoFake(object):
@logging_meth @logging_meth
def create_matiere(self, ue_id=None, titre=None, numero=None): def create_matiere(self, ue_id=None, titre=None, numero=None):
oid = sco_edit_matiere.do_matiere_create(locals()) oid = sco_edit_matiere.do_matiere_create(locals())
oids = sco_edit_matiere.do_matiere_list(args={"matiere_id": oid}) oids = sco_edit_matiere.matiere_list(args={"matiere_id": oid})
if not oids: if not oids:
raise ScoValueError("matiere not created !") raise ScoValueError("matiere not created !")
return oids[0] return oids[0]
@ -203,7 +203,7 @@ class ScoFake(object):
module_type=None, module_type=None,
): ):
oid = sco_edit_module.do_module_create(locals()) oid = sco_edit_module.do_module_create(locals())
oids = sco_edit_module.do_module_list(args={"module_id": oid}) oids = sco_edit_module.module_list(args={"module_id": oid})
if not oids: if not oids:
raise ScoValueError("module not created ! (oid=%s)" % oid) raise ScoValueError("module not created ! (oid=%s)" % oid)
return oids[0] return oids[0]
@ -251,7 +251,7 @@ class ScoFake(object):
if not responsable_id: if not responsable_id:
responsable_id = self.default_user.id responsable_id = self.default_user.id
oid = sco_moduleimpl.do_moduleimpl_create(locals()) oid = sco_moduleimpl.do_moduleimpl_create(locals())
oids = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=oid) # API inconsistency oids = sco_moduleimpl.moduleimpl_list(moduleimpl_id=oid) # API inconsistency
if not oids: if not oids:
raise ScoValueError("moduleimpl not created !") raise ScoValueError("moduleimpl not created !")
return oids[0] return oids[0]

View File

@ -119,7 +119,7 @@ def test_abs_basic(test_client):
- sco_abs.get_abs_count(etudid, sem) - sco_abs.get_abs_count(etudid, sem)
- ListeAbsEtud - ListeAbsEtud
- partition_create - partition_create
- createGroup - create_group
- set_group - set_group
- EtatAbsenceGr - EtatAbsenceGr
- AddBilletAbsence - AddBilletAbsence
@ -268,7 +268,7 @@ def test_abs_basic(test_client):
partition_name="Eleve", partition_name="Eleve",
) )
li1 = sco_groups.get_partitions_list(sem["formsemestre_id"]) li1 = sco_groups.get_partitions_list(sem["formsemestre_id"])
_ = sco_groups.createGroup(li1[0]["partition_id"], "Groupe 1") _ = sco_groups.create_group(li1[0]["partition_id"], "Groupe 1")
# --- Affectation des élèves dans des groupes # --- Affectation des élèves dans des groupes

View File

@ -30,14 +30,14 @@
# - formation_list # - formation_list
# - formation_export # - formation_export
# - formsemestre_list # - formsemestre_list
# - do_moduleimpl_list # - moduleimpl_list
# - do_module_impl_with_module_list # - do_module_impl_with_module_list
# - do_formsemestre_delete # - do_formsemestre_delete
# - do_module_list # - module_list
# - do_module_delete # - do_module_delete
# - do_matiere_list # - matiere_list
# - do_matiere_delete # - do_matiere_delete
# - do_ue_list # - ue_list
# - do_ue_delete # - do_ue_delete
# - do_formation_delete # - do_formation_delete
@ -231,28 +231,24 @@ def test_formations(test_client):
# --- Liste des modules # --- Liste des modules
lim_sem1 = sco_moduleimpl.do_moduleimpl_list( lim_sem1 = sco_moduleimpl.moduleimpl_list(formsemestre_id=sem1["formsemestre_id"])
formsemestre_id=sem1["formsemestre_id"]
)
assert len(lim_sem1) == 2 assert len(lim_sem1) == 2
assert mod["module_id"] in (lim_sem1[0]["module_id"], lim_sem1[1]["module_id"]) assert mod["module_id"] in (lim_sem1[0]["module_id"], lim_sem1[1]["module_id"])
assert mod2["module_id"] in (lim_sem1[0]["module_id"], lim_sem1[1]["module_id"]) assert mod2["module_id"] in (lim_sem1[0]["module_id"], lim_sem1[1]["module_id"])
lim_modid = sco_moduleimpl.do_moduleimpl_list(module_id=mod["module_id"]) lim_modid = sco_moduleimpl.moduleimpl_list(module_id=mod["module_id"])
assert len(lim_modid) == 1 assert len(lim_modid) == 1
lim_modimpl_id = sco_moduleimpl.do_moduleimpl_list( lim_modimpl_id = sco_moduleimpl.moduleimpl_list(moduleimpl_id=mi["moduleimpl_id"])
moduleimpl_id=mi["moduleimpl_id"]
)
# print(lim_modimpl_id) # print(lim_modimpl_id)
# ---- Test de do_moduleimpl_withmodule_list # ---- Test de moduleimpl_withmodule_list
assert lim_modid == lim_modimpl_id # doit etre le meme resultat assert lim_modid == lim_modimpl_id # doit etre le meme resultat
liimp_sem1 = sco_moduleimpl.do_moduleimpl_withmodule_list( liimp_sem1 = sco_moduleimpl.moduleimpl_withmodule_list(
formsemestre_id=sem1["formsemestre_id"] formsemestre_id=sem1["formsemestre_id"]
) )
@ -262,16 +258,14 @@ def test_formations(test_client):
liimp_sem1[0]["module_id"], liimp_sem1[0]["module_id"],
liimp_sem1[1]["module_id"], liimp_sem1[1]["module_id"],
) )
liimp_sem2 = sco_moduleimpl.do_moduleimpl_withmodule_list( liimp_sem2 = sco_moduleimpl.moduleimpl_withmodule_list(
formsemestre_id=sem2["formsemestre_id"] formsemestre_id=sem2["formsemestre_id"]
) )
assert modt["module_id"] == liimp_sem2[0]["module_id"] assert modt["module_id"] == liimp_sem2[0]["module_id"]
liimp_modid = sco_moduleimpl.do_moduleimpl_withmodule_list( liimp_modid = sco_moduleimpl.moduleimpl_withmodule_list(module_id=mod["module_id"])
module_id=mod["module_id"]
)
assert len(liimp_modid) == 1 assert len(liimp_modid) == 1
liimp_modimplid = sco_moduleimpl.do_moduleimpl_withmodule_list( liimp_modimplid = sco_moduleimpl.moduleimpl_withmodule_list(
moduleimpl_id=mi["moduleimpl_id"] moduleimpl_id=mi["moduleimpl_id"]
) )
@ -296,31 +290,29 @@ def test_formations(test_client):
# RIEN NE SE PASSE AVEC CES FONCTIONS # RIEN NE SE PASSE AVEC CES FONCTIONS
li_module = sco_edit_module.do_module_list() li_module = sco_edit_module.module_list()
assert len(li_module) == 4 assert len(li_module) == 4
sco_edit_module.do_module_delete(oid=modt["module_id"]) # on supprime le semestre sco_edit_module.do_module_delete(oid=modt["module_id"]) # on supprime le semestre
# sco_formsemestre_edit.formsemestre_delete_moduleimpls( formsemestre_id=sem2["formsemestre_id"], module_ids_to_del=[modt["module_id"]]) # sco_formsemestre_edit.formsemestre_delete_moduleimpls( formsemestre_id=sem2["formsemestre_id"], module_ids_to_del=[modt["module_id"]])
# deuxieme methode de supression d'un module # deuxieme methode de supression d'un module
li_module2 = sco_edit_module.do_module_list() li_module2 = sco_edit_module.module_list()
assert len(li_module2) == 3 # verification de la suppression du module assert len(li_module2) == 3 # verification de la suppression du module
lim_sem2 = sco_moduleimpl.do_moduleimpl_list( lim_sem2 = sco_moduleimpl.moduleimpl_list(formsemestre_id=sem2["formsemestre_id"])
formsemestre_id=sem2["formsemestre_id"]
)
assert len(lim_sem2) == 0 # deuxieme vérification si le module s'est bien sup assert len(lim_sem2) == 0 # deuxieme vérification si le module s'est bien sup
li_mat = sco_edit_matiere.do_matiere_list() li_mat = sco_edit_matiere.matiere_list()
assert len(li_mat) == 4 assert len(li_mat) == 4
sco_edit_matiere.do_matiere_delete(oid=matt["matiere_id"]) # on supprime la matiere sco_edit_matiere.do_matiere_delete(oid=matt["matiere_id"]) # on supprime la matiere
li_mat2 = sco_edit_matiere.do_matiere_list() li_mat2 = sco_edit_matiere.matiere_list()
assert len(li_mat2) == 3 # verification de la suppression de la matiere assert len(li_mat2) == 3 # verification de la suppression de la matiere
li_ue = sco_edit_ue.do_ue_list() li_ue = sco_edit_ue.ue_list()
assert len(li_ue) == 4 assert len(li_ue) == 4
sco_edit_ue.ue_delete(ue_id=uet["ue_id"], dialog_confirmed=True) sco_edit_ue.ue_delete(ue_id=uet["ue_id"], dialog_confirmed=True)
li_ue2 = sco_edit_ue.do_ue_list() li_ue2 = sco_edit_ue.ue_list()
assert len(li_ue2) == 3 # verification de la suppression de l'UE assert len(li_ue2) == 3 # verification de la suppression de l'UE
# --- Suppression d'une formation # --- Suppression d'une formation
@ -363,7 +355,7 @@ def test_import_formation(test_client):
) )
] ]
# et les modules # et les modules
modules = sco_edit_module.do_module_list({"formation_id": formation_id}) modules = sco_edit_module.module_list({"formation_id": formation_id})
for mod in modules: for mod in modules:
mi = G.create_moduleimpl( mi = G.create_moduleimpl(
module_id=mod["module_id"], module_id=mod["module_id"],

View File

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

View File

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