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.
flask db migrate -m "ScoDoc 9.0.x: ..." # ajuster le message !
flask db migrate -m "message explicatif....."
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 !).
dropdb SCODOC_DEV

View File

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

View File

@ -33,7 +33,7 @@ token_auth = HTTPTokenAuth()
@basic_auth.verify_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):
return user

View File

@ -29,7 +29,7 @@
"""
# PAS ENCORE IMPLEMENTEE, juste un essai
# 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_delete
# 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 wtforms import StringField, PasswordField, BooleanField, SubmitField
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
@ -43,8 +43,11 @@ class UserCreationForm(FlaskForm):
class ResetPasswordRequestForm(FlaskForm):
email = StringField(_l("Email"), validators=[DataRequired(), Email()])
submit = SubmitField(_l("Request Password Reset"))
email = StringField(
_l("Adresse email associée à votre compte ScoDoc:"),
validators=[DataRequired(), Email()],
)
submit = SubmitField(_l("Envoyer"))
class ResetPasswordForm(FlaskForm):
@ -52,7 +55,11 @@ class ResetPasswordForm(FlaskForm):
password2 = PasswordField(
_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):

View File

@ -10,6 +10,7 @@ import re
from time import time
from typing import Optional
import cracklib # pylint: disable=import-error
from flask import current_app, url_for, g
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@\\\-_\.]+$")
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):
"""ScoDoc users, handled by Flask / SQLAlchemy"""

View File

@ -46,7 +46,10 @@ def login():
if not next_page or url_parse(next_page).netloc != "":
next_page = url_for("scodoc.index")
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")
@ -95,7 +98,9 @@ def reset_password_request():
current_app.logger.info(
"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 render_template(
"auth/reset_password_request.html", title=_("Reset Password"), form=form
@ -113,6 +118,6 @@ def reset_password(token):
if form.validate_on_submit():
user.set_password(form.password.data)
db.session.commit()
flash(_("Your password has been reset."))
flash(_("Votre mot de passe a été changé."))
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
from werkzeug.exceptions import BadRequest
import flask
from flask import g
from flask import abort, current_app
from flask import request
from flask import g, current_app, request
from flask import abort, url_for, redirect
from flask_login import current_user
from flask_login import login_required
from flask import current_app
import flask_login
import app
@ -52,6 +50,15 @@ def scodoc(func):
@wraps(func)
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:
dept_acronym = kwargs["scodoc_dept"]
# current_app.logger.info("setting dept to " + dept_acronym)
@ -81,7 +88,7 @@ def permission_required(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
les informations d'auth en paramètres:
__ac_name, __ac_password

View File

@ -32,6 +32,7 @@ class NotesFormation(db.Model):
ues = db.relationship("NotesUE", backref="formation", lazy="dynamic")
formsemestres = db.relationship("FormSemestre", lazy="dynamic", backref="formation")
ues = db.relationship("NotesUE", lazy="dynamic", backref="formation")
def __repr__(self):
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:
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):
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())
numero = db.Column(db.Integer) # ordre de présentation
modules = db.relationship("NotesModule", lazy="dynamic", backref="matiere")
class NotesModule(db.Model):
"""Module"""
@ -110,6 +117,8 @@ class NotesModule(db.Model):
# id de l'element pedagogique Apogee correspondant:
code_apogee = db.Column(db.String(APO_CODE_STR_LEN))
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):

View File

@ -70,9 +70,14 @@ class FormSemestre(db.Model):
# code element annee Apogee, eg 'VRT1A' ou 'V2INLA,V2INCA,...'
elt_annee_apo = db.Column(db.Text())
# Relations:
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
# ne pas utiliser après migrate_scodoc7_dept_archive
scodoc7_id = db.Column(db.Text(), nullable=True)

View File

@ -44,7 +44,6 @@ import unicodedata
import app.scodoc.sco_utils as scu
from app import log
import six
PE_DEBUG = 0
@ -145,7 +144,7 @@ def escape_for_latex(s):
}
exp = re.compile(
"|".join(
re.escape(six.text_type(key))
re.escape(key)
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):
"""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.
cette procédure modifie la moyenne de chaque UE capitalisable.

View File

@ -752,6 +752,8 @@ if __name__ == "__main__":
)
document.build(objects)
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")
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["endpoint"], scodoc_dept=g.scodoc_dept, **args
)
elif "url" in item:
item["urlq"] = item["url"]
else:
item["urlq"] = "#"
item["attr"] = item.get("attr", "")

View File

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

View File

@ -27,10 +27,7 @@
"""Calculs sur les notes et cache des resultats
"""
import inspect
import os
import pdb
import time
from operator import itemgetter
from flask import g, url_for
@ -40,12 +37,8 @@ import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app import log
from app.scodoc.sco_formulas import NoteVector
from app.scodoc.sco_exceptions import (
AccessDenied,
NoteProcessError,
ScoException,
ScoValueError,
)
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_formsemestre import (
formsemestre_uecoef_list,
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())
"""
if modimpls is None:
modimpls = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id)
modimpls = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
uedict = {}
for modimpl in modimpls:
mod = sco_edit_module.do_module_list(args={"module_id": modimpl["module_id"]})[
0
]
mod = sco_edit_module.module_list(args={"module_id": modimpl["module_id"]})[0]
modimpl["module"] = mod
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
ues = list(uedict.values())
ues.sort(key=lambda u: u["numero"])
@ -219,26 +210,29 @@ class NotesTable(object):
valid_evals,
mods_att,
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._matmoys = {} # moyennes par matieres
self._valid_evals = {} # { evaluation_id : eval }
for e in valid_evals:
self._valid_evals[e["evaluation_id"]] = e # Liste des modules et 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:
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:
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
else:
ue = uedict[mod["ue_id"]]
modimpl["ue"] = ue # add ue dict to moduleimpl
self._matmoys[mod["matiere_id"]] = {}
mat = sco_edit_matiere.do_matiere_list(
args={"matiere_id": mod["matiere_id"]}
)[0]
mat = sco_edit_matiere.matiere_list(args={"matiere_id": mod["matiere_id"]})[
0
]
modimpl["mat"] = mat # add matiere dict to moduleimpl
# calcul moyennes du module et stocke dans le module
# 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"
% (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
if ue_id not in self._uecoef:
cl = formsemestre_uecoef_list(

View File

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

View File

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

View File

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

View File

@ -203,7 +203,9 @@ class BaseArchiver(object):
def get_archive_description(self, archive_id):
"""Return description of archive"""
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):
"""Creates a new archive for this object and returns its id."""
@ -232,9 +234,8 @@ class BaseArchiver(object):
try:
scu.GSL.acquire()
fname = os.path.join(archive_id, filename)
f = open(fname, "wb")
f.write(data)
f.close()
with open(fname, "wb") as f:
f.write(data)
finally:
scu.GSL.release()
return filename
@ -247,7 +248,9 @@ class BaseArchiver(object):
raise ValueError("invalid filename")
fname = os.path.join(archive_id, filename)
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):
"""Recupere donnees du fichier indiqué et envoie au client"""

View File

@ -360,7 +360,7 @@ def formsemestre_bulletinetud_published_dict(
"decisions_ue"
]: # 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():
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(
dict(
ue_id=ue["ue_id"],

View File

@ -385,7 +385,7 @@ def make_xml_formsemestre_bulletinetud(
"decisions_ue"
]: # 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():
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(
Element(
"decision_ue",

View File

@ -292,7 +292,7 @@ def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=Fa
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
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.

View File

@ -28,7 +28,6 @@
"""Semestres: Codes gestion parcours (constantes)
"""
import collections
from six.moves import range
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)

View File

@ -27,10 +27,10 @@
"""Calcul des moyennes de module
"""
import traceback
import pprint
import traceback
from flask import url_for, g
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app.scodoc.sco_utils import (
@ -40,7 +40,7 @@ from app.scodoc.sco_utils import (
EVALUATION_RATTRAPAGE,
EVALUATION_SESSION2,
)
from app.scodoc.sco_exceptions import ScoException
from app.scodoc.sco_exceptions import ScoValueError
from app import log
from app.scodoc import sco_abs
from app.scodoc import sco_edit_module
@ -65,7 +65,8 @@ def moduleimpl_has_expression(mod):
def formsemestre_expressions_use_abscounts(formsemestre_id):
"""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
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:
return True
# 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"]:
return True
return False
@ -128,7 +129,7 @@ def compute_user_formula(
coefs,
coefs_mask,
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,
):
"""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):
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
raise ScoException(
"""valeur moyenne %s hors limite pour <a href="formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s">%s</a>"""
% (user_moy, sem["formsemestre_id"], etudid, etud["nomprenom"])
raise ScoValueError(
f"""
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:
log(
@ -183,7 +189,7 @@ def compute_user_formula(
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
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).
@ -193,13 +199,13 @@ def do_moduleimpl_moyennes(nt, mod):
S'il manque des notes et que le coef n'est pas nul,
la moyenne n'est pas calculée: NA
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
moduleimpl_id = mod["moduleimpl_id"]
is_malus = mod["module"]["module_type"] == scu.MODULE_MALUS
sem = sco_formsemestre.get_formsemestre(mod["formsemestre_id"])
etudids = sco_moduleimpl.do_moduleimpl_listeetuds(
moduleimpl_id = modimpl["moduleimpl_id"]
is_malus = modimpl["module"]["module_type"] == scu.MODULE_MALUS
sem = sco_formsemestre.get_formsemestre(modimpl["formsemestre_id"])
etudids = sco_moduleimpl.moduleimpl_listeetuds(
moduleimpl_id
) # tous, y compris demissions
# Inscrits au semestre (pour traiter les demissions):
@ -207,7 +213,7 @@ def do_moduleimpl_moyennes(nt, mod):
[
x["etudid"]
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"])
) # la plus ancienne en tête
user_expr = moduleimpl_has_expression(mod)
user_expr = moduleimpl_has_expression(modimpl)
attente = False
# recupere les notes de toutes les evaluations
eval_rattr = None
@ -268,7 +274,7 @@ def do_moduleimpl_moyennes(nt, mod):
]
#
R = {}
formula = scu.unescape_html(mod["computation_expr"])
formula = scu.unescape_html(modimpl["computation_expr"])
formula_use_abs = "abs" in formula
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
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 } },
la liste des moduleimpls, la liste des evaluations valides,
liste des moduleimpls avec notes en attente.
@ -375,7 +381,7 @@ def do_formsemestre_moyennes(nt, formsemestre_id):
# args={"formsemestre_id": formsemestre_id}
# )
# 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
D = {}
valid_evals = []
@ -383,15 +389,16 @@ def do_formsemestre_moyennes(nt, formsemestre_id):
mods_att = []
expr_diags = []
for modimpl in modimpls:
mod = sco_edit_module.do_module_list(args={"module_id": modimpl["module_id"]})[
0
]
mod = sco_edit_module.module_list(args={"module_id": modimpl["module_id"]})[0]
modimpl["module"] = mod # add module dict to moduleimpl (used by nt)
moduleimpl_id = modimpl["moduleimpl_id"]
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 += valid_evals_mod
if attente:

View File

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

View File

@ -167,7 +167,8 @@ def _anonymize_db(ano_db_name):
def _get_scodoc_serial():
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:
return 0

View File

@ -104,7 +104,7 @@ def do_formation_delete(oid):
raise ScoLockedFormError()
cnx = ndb.GetDBConnexion()
# 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:
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])
return flask.redirect(
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):
"""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)
after = int(after) # 0: deplace avant, 1 deplace apres
if after not in (0, 1):
raise ValueError('invalid value for "after"')
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)
if len(others) > 1:
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:
return flask.redirect(
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):
"""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))
redirect = int(redirect)
after = int(after) # 0: deplace avant, 1 deplace apres
if after not in (0, 1):
raise ValueError('invalid value for "after"')
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:
idx = [p["ue_id"] for p in others].index(ue_id)
neigh = None # object to swap with
@ -378,7 +378,7 @@ def ue_move(ue_id, after=0, redirect=1):
if redirect:
return flask.redirect(
url_for(
"notes.ue_list",
"notes.ue_table",
scodoc_dept=g.scodoc_dept,
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"
cnx = ndb.GetDBConnexion()
return _matiereEditor.list(cnx, *args, **kw)
@ -60,12 +60,12 @@ def do_matiere_edit(*args, **kw):
cnx = ndb.GetDBConnexion()
# 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"]):
raise ScoLockedFormError()
# edit
_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)
@ -77,7 +77,7 @@ def do_matiere_create(args):
cnx = ndb.GetDBConnexion()
# 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
r = _matiereEditor.create(cnx, args)
@ -96,7 +96,7 @@ def matiere_create(ue_id=None):
"""Creation d'une matiere"""
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 = [
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,
@ -134,7 +134,7 @@ associé.
)
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:
@ -143,7 +143,7 @@ associé.
return flask.redirect(dest_url)
else:
# 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:
return (
"\n".join(H)
@ -164,8 +164,8 @@ def do_matiere_delete(oid):
cnx = ndb.GetDBConnexion()
# check
mat = do_matiere_list({"matiere_id": oid})[0]
ue = sco_edit_ue.do_ue_list({"ue_id": mat["ue_id"]})[0]
mat = matiere_list({"matiere_id": oid})[0]
ue = sco_edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]
locked = matiere_is_locked(mat["matiere_id"])
if locked:
log("do_matiere_delete: mat=%s" % mat)
@ -174,7 +174,7 @@ def do_matiere_delete(oid):
raise ScoLockedFormError()
log("do_matiere_delete: matiere_id=%s" % oid)
# 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:
sco_edit_module.do_module_delete(mod["module_id"])
_matiereEditor.delete(cnx, oid)
@ -193,8 +193,8 @@ def matiere_delete(matiere_id=None):
"""Delete an UE"""
from app.scodoc import sco_edit_ue
M = do_matiere_list(args={"matiere_id": matiere_id})[0]
UE = sco_edit_ue.do_ue_list(args={"ue_id": M["ue_id"]})[0]
M = matiere_list(args={"matiere_id": matiere_id})[0]
UE = sco_edit_ue.ue_list(args={"ue_id": M["ue_id"]})[0]
H = [
html_sco_header.sco_header(page_title="Suppression d'une matière"),
"<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_edit_ue
F = do_matiere_list(args={"matiere_id": matiere_id})
F = matiere_list(args={"matiere_id": matiere_id})
if not F:
raise ScoValueError("Matière inexistante !")
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:
raise ScoValueError("UE inexistante !")
U = U[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_ids = [u["ue_id"] for u in ues]
H = [
@ -286,7 +286,7 @@ associé.
return flask.redirect(dest_url)
else:
# 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):
return (
"\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"
cnx = ndb.GetDBConnexion()
return _moduleEditor.list(cnx, *args, **kw)
@ -126,8 +126,8 @@ def module_create(matiere_id=None):
if matiere_id is None:
raise ScoValueError("invalid matiere !")
M = sco_edit_matiere.do_matiere_list(args={"matiere_id": matiere_id})[0]
UE = sco_edit_ue.do_ue_list(args={"ue_id": M["ue_id"]})[0]
M = sco_edit_matiere.matiere_list(args={"matiere_id": matiere_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]
parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"])
semestres_indices = list(range(1, parcours.NB_SEM + 1))
@ -138,7 +138,7 @@ def module_create(matiere_id=None):
_MODULE_HELP,
]
# 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:
default_num = max([m["numero"] for m in Mods]) + 10
else:
@ -241,7 +241,7 @@ def module_create(matiere_id=None):
do_module_create(tf[2])
return flask.redirect(
url_for(
"notes.ue_list",
"notes.ue_table",
scodoc_dept=g.scodoc_dept,
formation_id=UE["formation_id"],
)
@ -252,18 +252,18 @@ def do_module_delete(oid):
"delete module"
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"]):
raise ScoLockedFormError()
# 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:
err_page = f"""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>
<p class="help">Il faut d'abord supprimer le semestre. Mais il est peut être préférable de
laisser ce programme intact et d'en créer une nouvelle version pour la modifier.
</p>
<a href="{url_for('notes.ue_list', scodoc_dept=g.scodoc_dept,
<a href="{url_for('notes.ue_table', scodoc_dept=g.scodoc_dept,
formation_id=mod["formation_id"])}">reprendre</a>
"""
raise ScoGenError(err_page)
@ -285,7 +285,7 @@ def module_delete(module_id=None):
"""Delete a module"""
if not module_id:
raise ScoValueError("invalid module !")
Mods = do_module_list(args={"module_id": module_id})
Mods = module_list(args={"module_id": module_id})
if not Mods:
raise ScoValueError("Module inexistant !")
Mod = Mods[0]
@ -317,7 +317,7 @@ def do_module_edit(val):
from app.scodoc import sco_edit_formation
# 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"]):
# formation verrouillée: empeche de modifier certains champs:
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):
"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
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:
raise ScoValueError("invalid module !")
Mod = do_module_list(args={"module_id": module_id})
Mod = module_list(args={"module_id": module_id})
if not Mod:
raise ScoValueError("invalid module !")
Mod = Mod[0]
@ -521,7 +521,7 @@ def edit_module_set_code_apogee(id=None, value=None):
value = value.strip("-_ \t")
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:
return "module invalide" # should not occur
@ -531,7 +531,7 @@ def edit_module_set_code_apogee(id=None, value=None):
return value
def module_list(formation_id):
def module_table(formation_id):
"""Liste des modules de la formation
(XXX inutile ou a revoir)
"""
@ -548,7 +548,7 @@ def module_list(formation_id):
]
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)
if editable:
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):
"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)
@ -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"""
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:
# Un seul module de malus par UE:
nb_mod_malus = len(
[
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
]
)
@ -610,7 +610,7 @@ def ue_add_malus_module(ue_id, titre=None, code=None):
"""Add a malus module in this 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:
titre = ""
@ -629,7 +629,7 @@ def ue_add_malus_module(ue_id, titre=None, code=None):
)
# 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
matiere_id = sco_edit_matiere.do_matiere_create(
{"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"
cnx = ndb.GetDBConnexion()
return _ueEditor.list(cnx, *args, **kw)
@ -97,9 +97,7 @@ def do_ue_create(args):
cnx = ndb.GetDBConnexion()
# check duplicates
ues = do_ue_list(
{"formation_id": args["formation_id"], "acronyme": args["acronyme"]}
)
ues = ue_list({"formation_id": args["formation_id"], "acronyme": args["acronyme"]})
if ues:
raise ScoValueError('Acronyme d\'UE "%s" déjà utilisé !' % args["acronyme"])
# create
@ -124,7 +122,7 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
cnx = ndb.GetDBConnexion()
log("do_ue_delete: ue_id=%s, delete_validations=%s" % (ue_id, delete_validations))
# check
ue = do_ue_list({"ue_id": ue_id})
ue = ue_list({"ue_id": ue_id})
if not ue:
raise ScoValueError("UE inexistante !")
ue = ue[0]
@ -152,7 +150,7 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
)
# 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:
sco_edit_matiere.do_matiere_delete(mat["matiere_id"])
# delete uecoef and events
@ -177,7 +175,7 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
if not force:
return flask.redirect(
url_for(
"notes.ue_list",
"notes.ue_table",
scodoc_dept=g.scodoc_dept,
formation_id=ue["formation_id"],
)
@ -197,7 +195,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None):
create = int(create)
if not create:
U = do_ue_list(args={"ue_id": ue_id})
U = ue_list(args={"ue_id": ue_id})
if not U:
raise ScoValueError("UE inexistante !")
U = U[0]
@ -371,7 +369,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None):
do_ue_edit(tf[2])
return flask.redirect(
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.
"""
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:
ue["semestre_id"] = Modlist[0]["semestre_id"]
else:
@ -393,7 +391,7 @@ def next_ue_numero(formation_id, semestre_id=None):
"""Numero d'une nouvelle UE dans cette formation.
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:
return 0
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):
"""Delete an UE"""
ue = do_ue_list(args={"ue_id": ue_id})
ue = ue_list(args={"ue_id": ue_id})
if not ue:
raise ScoValueError("UE inexistante !")
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)
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
editer (si non verrouillée).
éditer (si non verrouillée).
"""
from app.scodoc import sco_formations
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"])
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:
_add_ue_semestre_id(ue_list)
ue_list.sort(key=lambda u: (u["semestre_id"], u["numero"]))
@ -461,7 +459,7 @@ def ue_list(formation_id=None, msg=""):
else:
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_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>')
if not parcours.UE_IS_MODULE:
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:
if not parcours.UE_IS_MODULE:
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('<ul class="notes_module_list">')
Modlist = sco_edit_module.do_module_list(
Modlist = sco_edit_module.module_list(
args={"matiere_id": Mat["matiere_id"]}
)
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)
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:
ue_code = ue["ue_code"]
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:
H.append(
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}
</li>
"""
@ -897,13 +895,13 @@ def do_ue_edit(args, bypass_lock=False, dont_invalidate_cache=False):
"edit an UE"
# check
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"]):
raise ScoLockedFormError()
# check: acronyme unique dans cette formation
if "acronyme" in args:
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:
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")
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:
return "ue invalide"
@ -966,11 +964,11 @@ def formation_table_recap(formation_id, format="html"):
raise ScoValueError("invalid formation_id")
F = F[0]
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:
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:
Modlist = sco_edit_module.do_module_list(
Modlist = sco_edit_module.module_list(
args={"matiere_id": Mat["matiere_id"]}
)
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,
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])))

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 pprint
import traceback
import urllib
import app.scodoc.sco_utils as scu
from app import log
@ -80,7 +80,7 @@ def formsemestre_load_ics(sem):
ics_data = ""
else:
log("Loading edt from %s" % ics_url)
f = six.moves.urllib.request.urlopen(
f = urllib.request.urlopen(
ics_url, timeout=5
) # 5s TODO: add config parameter, eg for slow networks
ics_data = f.read()

View File

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

View File

@ -179,7 +179,7 @@ def do_evaluation_list(args, sortkey=None):
def do_evaluation_list_in_formsemestre(formsemestre_id):
"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 = []
for mod in mods:
evals += do_evaluation_list(args={"moduleimpl_id": mod["moduleimpl_id"]})
@ -213,7 +213,7 @@ def _check_evaluation_args(args):
jour = args.get("jour", None)
args["jour"] = 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"])
d, m, y = [int(x) for x in sem["date_debut"].split("/")]
date_debut = datetime.date(y, m, d)
@ -301,8 +301,8 @@ def do_evaluation_create(
r = _evaluationEditor.create(cnx, args)
# news
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0]
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
mod["moduleimpl_id"] = M["moduleimpl_id"]
mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
sco_news.add(
@ -332,7 +332,7 @@ def do_evaluation_edit(args):
cnx = ndb.GetDBConnexion()
_evaluationEditor.edit(cnx, args)
# 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"])
@ -357,10 +357,10 @@ def do_evaluation_delete(evaluation_id):
_evaluationEditor.delete(cnx, evaluation_id)
# 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"])
# 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["url"] = (
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):
"""donne infos sur l'etat du evaluation
{ 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
# ---- Liste des groupes complets et incomplets
E = do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0]
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_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
formsemestre_id = M["formsemestre_id"]
# 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"]:
continue
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"]
)[0]
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
# au module (pour gerer les modules optionnels correctement)
# 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"]
# insem = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits( formsemestre_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()
T = []
for e in evals:
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=e["moduleimpl_id"])[0]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0]
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=e["moduleimpl_id"])[0]
Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
if (e["evaluation_type"] != scu.EVALUATION_NORMALE) or (
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]
moduleimpl_id = E["moduleimpl_id"]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0]
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
formsemestre_id = M["formsemestre_id"]
u = sco_users.user_info(M["responsable_id"])
resp = u["prenomnom"]
@ -1065,7 +1062,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True):
]
if Mod["module_type"] == scu.MODULE_MALUS:
# 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)
# store min/max values used by JS client-side checks:
H.append(
@ -1115,7 +1112,7 @@ def evaluation_create_form(
the_eval = do_evaluation_list({"evaluation_id": evaluation_id})[0]
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
formsemestre_id = M["formsemestre_id"]
min_note_max = scu.NOTES_PRECISION # le plus petit bareme possible
@ -1174,7 +1171,7 @@ def evaluation_create_form(
else:
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">
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):
"""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é
"""
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:
style = self.default_style
if "font" in style:
@ -310,7 +325,8 @@ class ScoExcelSheet:
lines = comment.splitlines()
cell.comment.width = 7 * max([len(line) for line in 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):
cell.data_type = "d"
cell.number_format = FORMAT_DATE_DDMMYY
@ -318,6 +334,7 @@ class ScoExcelSheet:
cell.data_type = "n"
else:
cell.data_type = "s"
return cell
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
"""
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
for ue in ues:
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"]
if ue["ects"] is None:
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
for mat in mats:
matiere_id = mat["matiere_id"]
if not export_ids:
del mat["matiere_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
for mod in mods:
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:
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):
@ -364,7 +366,7 @@ def formation_create_new_version(formation_id, redirect=True):
if redirect:
return flask.redirect(
url_for(
"notes.ue_list",
"notes.ue_table",
scodoc_dept=g.scodoc_dept,
formation_id=new_id,
msg="Nouvelle version !",

View File

@ -246,7 +246,7 @@ def do_formsemestre_create(args, silent=False):
default=True,
redirect=0,
)
_group_id = sco_groups.createGroup(partition_id, default=True)
_group_id = sco_groups.create_group(partition_id, default=True)
# news
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_formations
from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups_copy
from app.scodoc import sco_modalites
from app.scodoc import sco_moduleimpl
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_preferences
from app.scodoc import sco_users
import six
def _default_sem_title(F):
@ -171,7 +171,7 @@ def do_formsemestre_createwithmodules(edit=False):
initvalues = sem
semestre_id = initvalues["semestre_id"]
# 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])
initvalues["tf-checked"] = ["MI" + str(x["module_id"]) 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( )
# mais si on veut l'ordre du PPN (groupe par UE et matieres) il faut:
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:
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:
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
for m in modsmat:
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)
checkedmods = [int(x[2:]) for x in tf[2]["tf-checked"]]
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]
mods_tocreate = [x for x in checkedmods if not x in existingmods]
# modules a existants a modifier
@ -767,7 +767,7 @@ def do_formsemestre_createwithmodules(edit=False):
"responsable_id": tf[2]["MI" + str(module_id)],
}
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"])]
# INSCRIPTIONS DES ETUDIANTS
log(
@ -801,7 +801,7 @@ def do_formsemestre_createwithmodules(edit=False):
ok, diag = formsemestre_delete_moduleimpls(formsemestre_id, mods_todelete)
msg += diag
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
)[0]["moduleimpl_id"]
modargs = {
@ -813,7 +813,7 @@ def do_formsemestre_createwithmodules(edit=False):
sco_moduleimpl.do_moduleimpl_edit(
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:
msg_html = (
@ -846,10 +846,10 @@ def formsemestre_delete_moduleimpls(formsemestre_id, module_ids_to_del):
msg = []
for module_id in module_ids_to_del:
# get id
moduleimpl_id = sco_moduleimpl.do_moduleimpl_list(
moduleimpl_id = sco_moduleimpl.moduleimpl_list(
formsemestre_id=formsemestre_id, module_id=module_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 ?
evals = sco_evaluations.do_evaluation_list({"moduleimpl_id": moduleimpl_id})
if evals:
@ -1015,7 +1015,7 @@ def do_formsemestre_clone(
formsemestre_id = sco_formsemestre.do_formsemestre_create(args)
log("created formsemestre %s" % formsemestre_id)
# 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:
args = mod_orig.copy()
args["formsemestre_id"] = formsemestre_id
@ -1074,32 +1074,11 @@ def do_formsemestre_clone(
args["formsemestre_id"] = formsemestre_id
_ = sco_compute_moy.formsemestre_ue_computation_expr_create(cnx, args)
# 5- Copy partitions
# 5- Copy partitions and groups
if clone_partitions:
listgroups = []
listnamegroups = []
# 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)
sco_groups_copy.clone_partitions_and_groups(
orig_formsemestre_id, 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).
"""
# 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:
mod["module_id"] = modules_old2new[mod["module_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)
# --- 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:
# evaluations
evals = sco_evaluations.do_evaluation_list(

View File

@ -439,7 +439,7 @@ def _list_ue_with_coef_and_validations(sem, etudid):
"""
cnx = ndb.GetDBConnexion()
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:
# add coefficient
uecoef = sco_formsemestre.formsemestre_uecoef_list(

View File

@ -237,7 +237,7 @@ def do_formsemestre_inscription_with_modules(
gdone[group_id] = 1
# 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
)
for mod in modimpls:
@ -448,7 +448,7 @@ def formsemestre_inscription_option(etudid, formsemestre_id):
]
# 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)
# Formulaire
modimpls_by_ue_ids = scu.DictDefault(defaultvalue=[]) # ue_id : [ moduleimpl_id ]
@ -680,7 +680,7 @@ def do_moduleimpl_incription_options(
# inscriptions
for moduleimpl_id in a_inscrire:
# 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:
raise ScoValueError(
"inscription: invalid moduleimpl_id: %s" % moduleimpl_id
@ -693,7 +693,7 @@ def do_moduleimpl_incription_options(
# desinscriptions
for moduleimpl_id in a_desinscrire:
# 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:
raise ScoValueError(
"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,
"endpoint": "notes.ue_list",
"endpoint": "notes.ue_table",
"args": {"formation_id": sem["formation_id"]},
"enabled": True,
"helpmsg": "Tableau de bord du semestre",
@ -453,7 +453,7 @@ def retreive_formsemestre_from_request() -> int:
if "formsemestre_id" in args:
formsemestre_id = args["formsemestre_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:
return None # suppressed ?
modimpl = modimpl[0]
@ -463,7 +463,7 @@ def retreive_formsemestre_from_request() -> int:
if not E:
return None # evaluation suppressed ?
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"]
elif "group_id" in args:
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)
F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0]
parcours = sco_codes_parcours.get_parcours_from_code(F["type_parcours"])
Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list(
formsemestre_id=formsemestre_id
)
Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
R = []
sum_coef = 0
@ -885,7 +883,7 @@ def html_expr_diagnostic(diagnostics):
last_id, last_msg = None, None
for diag in diagnostics:
if "moduleimpl_id" in diag:
mod = sco_moduleimpl.do_moduleimpl_withmodule_list(
mod = sco_moduleimpl.moduleimpl_withmodule_list(
moduleimpl_id=diag["moduleimpl_id"]
)[0]
H.append(
@ -898,7 +896,7 @@ def html_expr_diagnostic(diagnostics):
)
else:
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(
'<li>UE "%s": %s</li>'
% (ue["acronyme"] or ue["titre"] or "?", diag["msg"])
@ -928,7 +926,7 @@ def formsemestre_status_head(formsemestre_id=None, page_title=None):
),
f"""<table>
<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>""",
]
if sem["semestre_id"] >= 0:
@ -982,9 +980,7 @@ def formsemestre_status(formsemestre_id=None):
# porté du DTML
cnx = ndb.GetDBConnexion()
sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True)
Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list(
formsemestre_id=formsemestre_id
)
Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
# inscrits = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
# args={"formsemestre_id": formsemestre_id}
# )

View File

@ -27,7 +27,7 @@
"""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
from flask import url_for, g, request
@ -494,7 +494,7 @@ def formsemestre_recap_parcours_table(
with_links=False,
with_all_columns=True,
a_url="",
sem_info={},
sem_info=None,
show_details=False,
):
"""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
with_all_columns: si faux, pas de colonne "assiduité".
"""
sem_info = sem_info or {}
H = []
linktmpl = '<span onclick="toggle_vis(this);" class="toggle_sem sem_%%s">%s</span>'
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)
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_ids = [""] + [ue["ue_id"] for ue in ues]
tf = TrivialFormulator(
@ -1233,7 +1234,7 @@ def check_formation_ues(formation_id):
définition du programme: cette fonction retourne un bout de HTML
à 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 ] }
for ue in ues:
# 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.notesdb as ndb
from app import log
from app import log, cache
from app.scodoc.scolog import logdb
from app.scodoc import html_sco_header
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.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(
"partition",
@ -219,7 +205,7 @@ def get_default_group(formsemestre_id, fix_if_missing=False):
partition_id = partition_create(
formsemestre_id, default=True, redirect=False
)
group_id = createGroup(partition_id, default=True)
group_id = create_group(partition_id, default=True)
return group_id
# debug check
if len(r) != 1:
@ -452,6 +438,7 @@ def etud_add_group_infos(etud, sem, sep=" "):
return etud
@cache.memoize(timeout=50) # seconds
def get_etud_groups_in_partition(partition_id):
"""Returns { etudid : group }, with all students in this partition"""
infos = ndb.SimpleDictFetch(
@ -724,7 +711,7 @@ def setGroups(
# Supprime les groupes indiqués comme supprimés:
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
for line in groupsToCreate.split("\n"): # for each group_name (one per line)
@ -732,11 +719,7 @@ def setGroups(
group_name = fs[0].strip()
if not group_name:
continue
# ajax arguments are encoded in utf-8:
# group_name = six.text_type(group_name, "utf-8").encode(
# scu.SCO_ENCODING
# ) # #py3 #sco8
group_id = createGroup(partition_id, group_name)
group_id = create_group(partition_id, group_name)
# Place dans ce groupe les etudiants indiqués:
for etudid in fs[1:-1]:
change_etud_group_in_partition(etudid, group_id, partition)
@ -749,7 +732,7 @@ def setGroups(
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"""
partition = get_partition(partition_id)
formsemestre_id = partition["formsemestre_id"]
@ -769,12 +752,12 @@ def createGroup(partition_id, group_name="", default=False):
group_id = groupEditor.create(
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
def suppressGroup(group_id, partition_id=None):
def delete_group(group_id, partition_id=None):
"""form suppression d'un groupe.
(ne desinscrit pas les etudiants, change juste leur
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"]):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
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_delete(group)
@ -808,7 +791,7 @@ def partition_create(
if not sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
if partition_name:
partition_name = partition_name.strip()
partition_name = str(partition_name).strip()
if default:
partition_name = None
if not partition_name and not default:
@ -840,7 +823,7 @@ def partition_create(
return partition_id
def getArrowIconsTags():
def get_arrow_icons_tags():
"""returns html tags for arrows"""
#
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):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
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(
"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):
"""Set partition name"""
partition_name = partition_name.strip()
partition_name = str(partition_name).strip()
if not partition_name:
raise ValueError("partition name must be non empty")
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"""
if group_name:
group_name = group_name.strip()
@ -1229,7 +1212,7 @@ def group_rename(group_id):
)
else:
# 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):
@ -1304,7 +1287,7 @@ def groups_auto_repartition(partition_id=None):
# except:
# H.append('<p class="warning">Nom de groupe invalide: %s</p>'%group_name)
# 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
identdict = nt.identdict
@ -1364,6 +1347,7 @@ def create_etapes_partition(formsemestre_id, partition_name="apo_etapes"):
"""
from app.scodoc import sco_formsemestre_inscriptions
partition_name = str(partition_name)
log("create_etapes_partition(%s)" % formsemestre_id)
ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
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}
for etape in etapes:
if not (etape in groups_by_names):
gid = createGroup(pid, etape)
gid = create_group(pid, etape)
g = get_group(gid)
groups_by_names[etape] = g
# Place les etudiants dans les groupes
@ -1409,6 +1393,7 @@ def do_evaluation_listeetuds_groups(
Si include_dems, compte aussi les etudiants démissionnaires
(sinon, par défaut, seulement les 'I')
"""
# nb: pour notes_table / do_evaluation_etat, getallstudents est vrai et include_dems faux
fromtables = [
"notes_moduleimpl_inscription Im",
"notes_formsemestre_inscription Isem",
@ -1444,10 +1429,9 @@ def do_evaluation_listeetuds_groups(
req += " and Isem.etat='I'"
req += r
cnx = ndb.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
cursor = cnx.cursor()
cursor.execute(req, {"evaluation_id": evaluation_id})
res = cursor.fetchall()
return [x[0] for x in res]
return [x[0] for x in cursor]
def do_evaluation_listegroupes(evaluation_id, include_default=False):
@ -1461,7 +1445,7 @@ def do_evaluation_listegroupes(evaluation_id, include_default=False):
else:
c = " AND p.partition_name is not NULL"
cnx = ndb.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
cursor = cnx.cursor()
cursor.execute(
"""SELECT DISTINCT gd.id AS group_id
FROM group_descr gd, group_membership gm, partition p,
@ -1475,8 +1459,7 @@ def do_evaluation_listegroupes(evaluation_id, include_default=False):
+ c,
{"evaluation_id": evaluation_id},
)
res = cursor.fetchall()
group_ids = [x[0] for x in res]
group_ids = [x[0] for x in cursor]
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)
formsemestre_id = partition["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(
"scolar/affect_groups.html",
sco_header=html_sco_header.sco_header(

View File

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

View File

@ -27,27 +27,23 @@
"""Import d'utilisateurs via fichier Excel
"""
import random, time
import re
import random
import time
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header
from flask import g, url_for
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
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_preferences
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")
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.
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)
* call :func:`import_users` to actually do the job
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
"""
# Check current user privilege
auth_dept = current_user.dept
auth_name = str(current_user)
if not current_user.is_administrator():
raise AccessDenied("invalid user (%s) must be SuperAdmin" % auth_name)
@ -127,7 +122,8 @@ def import_excel_file(datafile):
del cols[tit]
if cols or unknown:
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)
)
# ok, same titles... : build the list of dictionaries
@ -138,10 +134,10 @@ def import_excel_file(datafile):
d[fs[i]] = line[i]
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.
@ -182,19 +178,17 @@ def import_users(users):
line = line + 1
user_ok, msg = sco_users.check_modif_user(
0,
ignore_optionals=False,
enforce_optionals=not force,
user_name=u["user_name"],
nom=u["nom"],
prenom=u["prenom"],
email=u["email"],
roles=u["roles"].split(","),
roles=[r for r in u["roles"].split(",") if r],
dept=u["dept"],
)
if not user_ok:
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()
#
# check identifiant
@ -224,7 +218,7 @@ def import_users(users):
import_ok = False
except ScoValueError as value_error:
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:
for u in created.values():
# Création de l'utilisateur (via SQLAlchemy)
@ -244,7 +238,7 @@ def import_users(users):
ALPHABET = r"""ABCDEFGHIJKLMNPQRSTUVWXYZ123456789123456789AEIOU"""
PASSLEN = 6
PASSLEN = 8
RNG = random.Random(time.time())
@ -259,23 +253,18 @@ def generate_password():
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"
if not u["email"]:
if not user["email"]:
return
u[
"url"
] = (
scu.ScoURL()
) # TODO set auth page URL ? (shared by all departments) ../auth/login
user["url"] = url_for("scodoc.index", _external=True)
txt = (
"""
Bonjour %(prenom)s %(nom)s,
"""
% u
% user
)
if reset:
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
sur %(url)s
"""
% u
% user
)
else:
txt += (
@ -300,16 +289,15 @@ Votre mot de passe est: %(passwd)s
Le logiciel est accessible sur: %(url)s
Vous êtes invité à changer ce mot de passe au plus vite (cliquez sur
votre nom en haut à gauche de la page d'accueil).
Vous êtes invité à changer ce mot de passe au plus vite (cliquez sur votre nom en haut à gauche de la page d'accueil).
"""
% u
% user
)
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
"""
@ -321,4 +309,4 @@ Pour plus d'informations sur ce logiciel, voir %s
else:
subject = "Votre accès ScoDoc"
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>"
E = evals[0]
moduleimpl_id = E["moduleimpl_id"]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0]
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
# (debug) check that all evals are in same module:
for e in evals:
@ -872,9 +872,7 @@ def formsemestre_check_absences_html(formsemestre_id):
</p>""",
]
# Modules, dans l'ordre
Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list(
formsemestre_id=formsemestre_id
)
Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
for M in Mlist:
evals = sco_evaluations.do_evaluation_list(
{"moduleimpl_id": M["moduleimpl_id"]}

View File

@ -100,7 +100,7 @@ def do_moduleimpl_delete(oid, formsemestre_id=None):
) # > 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"
args = locals()
cnx = ndb.GetDBConnexion()
@ -122,10 +122,11 @@ def do_moduleimpl_edit(args, formsemestre_id=None, cnx=None):
) # > modif moduleimpl
def do_moduleimpl_withmodule_list(
def moduleimpl_withmodule_list(
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.
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_module
args = locals()
modimpls = do_moduleimpl_list(
modimpls = moduleimpl_list(
**{
"moduleimpl_id": moduleimpl_id,
"formsemestre_id": formsemestre_id,
"module_id": module_id,
}
)
for mo in modimpls:
mo["module"] = sco_edit_module.do_module_list(
args={"module_id": mo["module_id"]}
)[0]
mo["ue"] = sco_edit_ue.do_ue_list(args={"ue_id": mo["module"]["ue_id"]})[0]
mo["matiere"] = sco_edit_matiere.do_matiere_list(
args={"matiere_id": mo["module"]["matiere_id"]}
)[0]
ues = {}
matieres = {}
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]
mi["module"] = modules[module_id]
ue_id = mi["module"]["ue_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]
mi["matiere"] = matieres[matiere_id]
# tri par semestre/UE/numero_matiere/numero_module
modimpls.sort(
@ -173,7 +185,7 @@ def do_moduleimpl_inscription_list(moduleimpl_id=None, etudid=None):
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"
req = """SELECT DISTINCT Im.etudid
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).
= 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
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
if not sem["etat"]:
@ -322,7 +334,7 @@ def can_change_module_resp(moduleimpl_id):
def can_change_ens(moduleimpl_id, raise_exc=True):
"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
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
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
"""
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"]
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)
# -- check lock
if not sem["etat"]:
@ -263,9 +263,7 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
can_change = authuser.has_permission(Permission.ScoEtudInscrit) and sem["etat"]
# Liste des modules
Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list(
formsemestre_id=formsemestre_id
)
Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
# Decrit les inscriptions aux modules:
commons = [] # modules communs a tous les etuds du semestre
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)
if UECaps:
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"])
for ue in ues:
H.append(

View File

@ -58,7 +58,7 @@ from app.scodoc import sco_users
def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0):
"Menu avec actions sur une evaluation"
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"])
@ -156,9 +156,9 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0):
def moduleimpl_status(moduleimpl_id=None, partition_id=None):
"""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"]
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)
F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0]
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"]
)
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"])
H = [

View File

@ -174,7 +174,7 @@ def _get_formsemestre_infos_from_news(n):
elif n["type"] == NEWS_NOTE:
moduleimpl_id = 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:
return {} # module does not exists anymore
return {} # pas d'indication du module

View File

@ -350,7 +350,6 @@ def pdf_basic_page(
# Gestion du lock pdf
import threading, time, six.moves.queue, six.moves._thread
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_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"])
if not sem["etat"]:
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)
if moduleimpl_id is None:
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"])
if (

View File

@ -244,10 +244,10 @@ class PlacementRunner:
# gr_title = sco_groups.listgroups_abbrev(d['groups'])
self.current_user = current_user
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
)[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"]}
)[0]
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.scodoc.sco_exceptions import ScoValueError
from app.scodoc import sco_preferences
import six
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)
if infos:
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:
log("invalid XML response from getEtapes Web Service\n%s" % etapes_url)
# 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"])
)
):
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)
except:
log("descr_decisions_ues: ue_id=%s decisions_ue=%s" % (ue_id, decisions_ue))

View File

@ -31,7 +31,6 @@
"""
import os
import tempfile
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
import re
import time
import datetime
@ -1399,7 +1398,8 @@ def graph_parcours(
# Genere graphe
_, path = tempfile.mkstemp(".gr")
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))
if not data:
log("graph.to_string=%s" % g.to_string())

View File

@ -45,6 +45,7 @@ from app.scodoc.sco_exceptions import (
AccessDenied,
InvalidNoteValue,
NoteProcessError,
ScoGenError,
ScoValueError,
)
from app.scodoc.sco_permissions import Permission
@ -72,13 +73,16 @@ def convert_note_from_string(
note_max,
note_min=scu.NOTES_MIN,
etudid=None,
absents=[],
tosuppress=[],
invalids=[],
absents=None,
tosuppress=None,
invalids=None,
):
"""converti une valeur (chaine saisie) vers une note numérique (float)
Les listes absents, tosuppress et invalids sont modifiées
"""
absents = absents or []
tosuppress = tosuppress or []
invalids = invalids or []
invalid = False
note_value = None
note = note.replace(",", ".")
@ -173,13 +177,10 @@ def do_evaluation_upload_xls():
evaluation_id = int(vals["evaluation_id"])
comment = vals["comment"]
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[
0
]
M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[0]
# Check access
# (admin, respformation, and responsable_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)
#
diag, lines = sco_excel.excel_file_to_list(vals["notefile"])
@ -250,8 +251,8 @@ def do_evaluation_upload_xls():
)
# news
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0]
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
mod["moduleimpl_id"] = M["moduleimpl_id"]
mod["url"] = url_for(
"notes.moduleimpl_status",
@ -289,9 +290,7 @@ def do_evaluation_upload_xls():
def do_evaluation_set_missing(evaluation_id, value, dialog_confirmed=False):
"""Initialisation des notes manquantes"""
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[
0
]
M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[0]
# Check access
# (admin, respformation, and responsable_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"
nb_changed, _, _ = _notes_add(current_user, evaluation_id, L, comment)
# news
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0]
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
mod["moduleimpl_id"] = M["moduleimpl_id"]
mod["url"] = url_for(
"notes.moduleimpl_status",
@ -426,8 +425,8 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
% E["moduleimpl_id"]
]
# news
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0]
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
mod["moduleimpl_id"] = M["moduleimpl_id"]
mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
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_suppress = 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 = (
[]
) # 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:
log("*** exception in _notes_add")
if do_it:
cnx.rollback() # abort
# inval cache
sco_cache.invalidate_formsemestre(
formsemestre_id=M["formsemestre_id"]
) # > modif notes (exception)
cnx.rollback() # abort
raise # re-raise exception
sco_cache.EvaluationCache.delete(evaluation_id)
raise ScoGenError("Erreur enregistrement note: merci de ré-essayer")
if do_it:
cnx.commit()
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
def saisie_notes_tableur(evaluation_id, group_ids=[]):
def saisie_notes_tableur(evaluation_id, group_ids=()):
"""Saisie des notes via un fichier Excel"""
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
if not evals:
raise ScoValueError("invalid evaluation_id")
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"]
if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]):
return (
@ -765,9 +765,9 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]):
if not evals:
raise ScoValueError("invalid evaluation_id")
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"]
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"])
mod_responsable = sco_users.user_info(M["responsable_id"])
if E["jour"]:
@ -845,7 +845,7 @@ def has_existing_decision(M, E, etudid):
return True
dec_ues = nt.get_etud_decision_ues(etudid)
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"]
if ue_id in dec_ues:
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:
raise ScoValueError("invalid evaluation_id")
E = evals[0]
M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[
0
]
M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[0]
formsemestre_id = M["formsemestre_id"]
# Check access
# (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)
)
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0]
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
Mod["url"] = url_for(
"notes.moduleimpl_status",
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]
# log("module_tag_set: module_id=%s taglist=%s" % (module_id, taglist))
# 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:
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):
"la liste de toutes les UE externes définies dans cette formation"
return sco_edit_ue.do_ue_list(
args={"formation_id": formation_id, "is_external": True}
)
return sco_edit_ue.ue_list(args={"formation_id": formation_id, "is_external": True})
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):
"""Page listing operations on evaluation"""
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)

View File

@ -32,9 +32,9 @@
import re
from flask import url_for, g, request
from flask.templating import render_template
from flask_login import current_user
import cracklib # pylint: disable=import-error
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
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(
edit,
ignore_optionals=False,
enforce_optionals=False,
user_name="",
nom="",
prenom="",
@ -400,9 +287,9 @@ def check_modif_user(
returns (ok, msg)
- ok : si vrai, peut continuer avec ces parametres
(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 ?
user = _user_list(user_name)
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)"
% 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
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
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
# check that tha same user_name has not already been described in this import
if not email:
@ -432,13 +319,22 @@ def check_modif_user(
return False, "l'adresse mail semble incorrecte"
# check département
if (
ignore_optionals
enforce_optionals
and dept != ""
and Departement.query.filter_by(acronym=dept).first() is None
):
return False, "département '%s' inexistant" % u["dept"] + MSG_OPT
if ignore_optionals and not roles:
return False, "département '%s' inexistant" % dept + MSG_OPT
if enforce_optionals and not roles:
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
# Des noms/prénoms semblables existent ?
nom = nom.lower().strip()
@ -450,7 +346,7 @@ def check_modif_user(
minmatch = 1
else:
minmatch = 0
if len(similar_users) > minmatch:
if enforce_optionals and len(similar_users) > minmatch:
return (
False,
"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):
# ne pas changer (ou vous perdez le support)
SCO_DUMP_UP_URL = "https://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/upload-dump"
# SCO_DUMP_UP_URL = "http://192.168.56.1:5000/upload_dump"
SCO_DUMP_UP_URL = "https://scodoc.org/scodoc-installmgr/upload-dump"
CSV_FIELDSEP = ";"
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 %}
{% block app_content %}
{% if message %}
<div class="alert alert-danger" role="alert">{{ message }}</div>
{% endif %}
<h1>Connexion</h1>
<div class="row">
<div class="col-md-4">

View File

@ -2,8 +2,13 @@
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Reset Your Password</h1>
<div class="row">
<h1>Changez votre mot de passe ScoDoc</h1>
<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">
{{ wtf.quick_form(form) }}
</div>

View File

@ -2,7 +2,7 @@
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Reset Password</h1>
<h1>Demande d'un nouveau mot de passe</h1>
<div class="row">
<div class="col-md-4">
{{ 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>
<a class="navbar-brand" href="{{ url_for('scodoc.index') }}">ScoDoc</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
{% if current_user.is_administrator() %}
<ul class="nav navbar-nav">
<li><a href="{{ url_for('scodoc.configuration') }}">configuration</a></li>
</ul>
{% if current_user.is_administrator() %}
<li><a href="{{ url_for('scodoc.configuration') }}">Configuration</a></li>
{% 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">
{% if current_user.is_anonymous %}
<li><a href="{{ url_for('auth.login') }}">connexion</a></li>

View File

@ -5,7 +5,7 @@
cliquez sur ce lien
</a>.
</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>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"""
# current_app.logger.info(f"start_scodoc_request")
ndb.open_db_connection()
if current_user.is_authenticated:
if current_user and current_user.is_authenticated:
current_user.last_seen = datetime.datetime.utcnow()
db.session.commit()
# 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 urllib
from flask import request
from flask_login import current_user
@ -166,14 +167,14 @@ def index_html(etud_nom=None, limit=50, offset="", format="html"):
if offset:
webparams["offset"] = max((offset or 0) - limit, 0)
prev_lnk = '<a class="stdlink" href="%s">précédentes</a>' % (
request.base_url + "?" + six.moves.urllib.parse.urlencode(webparams)
request.base_url + "?" + urllib.parse.urlencode(webparams)
)
else:
prev_lnk = ""
if len(entreprises) >= limit:
webparams["offset"] = (offset or 0) + limit
next_lnk = '<a class="stdlink" href="%s">suivantes</a>' % (
request.base_url + "?" + six.moves.urllib.parse.urlencode(webparams)
request.base_url + "?" + urllib.parse.urlencode(webparams)
)
else:
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
# laisser ce programme intact et d'en créer une nouvelle version pour la modifier.
# </p>
# <a href="url_for('notes.ue_list', scodoc-dept=g.scodoc_dept, formation_id='XXX')">reprendre</a>
# <a href="url_for('notes.ue_table', scodoc-dept=g.scodoc_dept, formation_id='XXX')">reprendre</a>
# """
# raise ScoGenError(err_page)
# # raise ScoGenError("une erreur banale")
@ -334,7 +334,7 @@ sco_publish(
Permission.ScoChangeFormation,
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(
"/edit_ue_set_code_apogee",
@ -396,7 +396,7 @@ sco_publish(
sco_edit_module.edit_module_set_code_apogee,
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_set",
@ -548,8 +548,8 @@ sco_publish(
)
sco_publish(
"/do_ue_list",
sco_edit_ue.do_ue_list,
"/ue_list",
sco_edit_ue.ue_list,
Permission.ScoView,
)
@ -1059,11 +1059,11 @@ def edit_moduleimpl_expr(moduleimpl_id):
@scodoc7func
def view_module_abs(moduleimpl_id, format="html"):
"""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"])
debut_sem = ndb.DateDMYtoISO(sem["date_debut"])
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 = []
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")
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 = [
html_sco_header.html_sem_header(
"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)
# 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 = {}
for mod in mods:
if not mod["responsable_id"] in sem_ens:
@ -1527,8 +1527,8 @@ def evaluation_delete(evaluation_id):
if not El:
raise ValueError("Evalution inexistante ! (%s)" % evaluation_id)
E = El[0]
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0]
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_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
etat = sco_evaluations.do_evaluation_etat(evaluation_id)
H = [
@ -2424,14 +2424,14 @@ def check_sem_integrity(formsemestre_id, fix=False):
"""
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_sem = []
formations_set = set() # les formations mentionnées dans les UE et modules
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"])
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"])
if ue["formation_id"] != mod["formation_id"]:
modimpl["mod"] = mod
@ -2490,12 +2490,12 @@ def check_sem_integrity(formsemestre_id, fix=False):
def check_form_integrity(formation_id, fix=False):
"debug"
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 = []
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:
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:
if mod["ue_id"] != ue["ue_id"]:
if fix:
@ -2534,9 +2534,7 @@ def check_formsemestre_integrity(formsemestre_id):
# de formations
diag = []
Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list(
formsemestre_id=formsemestre_id
)
Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
for mod in Mlist:
if mod["module"]["ue_id"] != mod["matiere"]["ue_id"]:
diag.append(
@ -2593,14 +2591,14 @@ def check_integrity_all():
# --------------------------------------------------------------------
# Support for legacy ScoDoc 7 API
# --------------------------------------------------------------------
@bp.route("/do_moduleimpl_list")
@bp.route("/moduleimpl_list")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func
def do_moduleimpl_list(
def moduleimpl_list(
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,
formsemestre_id=formsemestre_id,
module_id=module_id,
@ -2608,15 +2606,16 @@ def do_moduleimpl_list(
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
@permission_required(Permission.ScoView)
@scodoc7func
def do_moduleimpl_withmodule_list(
def moduleimpl_withmodule_list(
moduleimpl_id=None, formsemestre_id=None, module_id=None
):
"""API ScoDoc 7"""
data = sco_moduleimpl.do_moduleimpl_withmodule_list(
data = sco_moduleimpl.moduleimpl_withmodule_list(
moduleimpl_id=moduleimpl_id,
formsemestre_id=formsemestre_id,
module_id=module_id,

View File

@ -669,9 +669,18 @@ sco_publish(
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(
"/group_set_name",

View File

@ -38,10 +38,12 @@ import re
from xml.etree import ElementTree
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_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.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 Role
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.decorators import (
@ -63,9 +67,53 @@ from app.scodoc import sco_utils as scu
from app.scodoc import sco_xml
from app import log
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.TrivialFormulator import TrivialFormulator, tf_error_message
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("/")
@ -100,7 +148,12 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
initvalues = {}
edit = int(edit)
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()
if edit:
if not user_name:
@ -214,9 +267,11 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
"title": "Pseudo (login)",
"size": 20,
"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",
{
@ -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:
# 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:
H.append(tf_error_message("""Erreur: %s""" % err))
return "\n".join(H) + "\n" + tf[1] + F
ok, msg = sco_users.check_modif_user(
edit,
ignore_optionals=force,
enforce_optionals=not force,
user_name=user_name,
nom=vals["nom"],
prenom=vals["prenom"],
email=vals["email"],
dept=vals.get("dept", auth_dept),
roles=vals["roles"],
)
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"], "%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:
vals["date_expiration"] = None
except ValueError:
@ -444,18 +528,33 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
"identifiant invalide (pas d'accents ni de caractères spéciaux)"
)
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
if vals["password"]:
if vals["password"] != vals["password2"]:
msg = tf_error_message(
"""Les deux mots de passes ne correspondent pas !"""
)
return "\n".join(H) + msg + "\n" + tf[1] + F
if not sco_users.is_valid_password(vals["password"]):
msg = tf_error_message(
"""Mot de passe trop simple, recommencez !"""
)
return "\n".join(H) + msg + "\n" + tf[1] + F
if mode == "A":
vals["password"] = generate_password()
else:
if vals["password"]:
if vals["password"] != vals["password2"]:
msg = tf_error_message(
"""Les deux mots de passes ne correspondent pas !"""
)
return "\n".join(H) + msg + "\n" + tf[1] + F
if not is_valid_password(vals["password"]):
msg = tf_error_message(
"""Mot de passe trop simple, recommencez !"""
)
return "\n".join(H) + msg + "\n" + tf[1] + F
if not can_choose_dept:
vals["dept"] = auth_dept
# ok, go
@ -467,6 +566,22 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
u.from_dict(vals, new_user=True)
db.session.add(u)
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(
url_for(
"users.user_info_page",
@ -531,6 +646,16 @@ def import_users_form():
"xlsfile",
{"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"}),
),
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))
else:
# 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.append("<ul>")
for d in diag:
H.append("<li>%s</li>" % d)
H.append("</ul>")
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><a class="stdlink" href="%s">Continuer</a></p>' % dest)
else:
@ -562,8 +689,31 @@ def import_users_form():
@scodoc
@permission_required(Permission.ScoUsersView)
@scodoc7func
def user_info_page(user_name):
return sco_users.user_info_page(user_name=user_name)
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")
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")
@ -594,7 +744,7 @@ def get_user_list_xml(dept=None, start="", limit=25):
return scu.send_file(data, mime=scu.XML_MIMETYPE)
@bp.route("/form_change_password")
@bp.route("/form_change_password", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func
@ -603,36 +753,42 @@ def form_change_password(user_name=None):
Un utilisateur peut toujours changer son propre mot de passe.
"""
if not user_name:
u = current_user
user = current_user
else:
u = User.query.filter_by(user_name=user_name).first()
H = [html_sco_header.sco_header(user_check=False)]
F = html_sco_header.sco_footer()
user = User.query.filter_by(user_name=user_name).first()
# check access
if not can_handle_passwd(u):
return (
"\n".join(H)
+ "<p>Vous n'avez pas la permission de changer ce mot de passe</p>"
+ F
if not can_handle_passwd(user):
return "\n".join(
[
html_sco_header.sco_header(user_check=False),
"<p>Vous n'avez pas la permission de changer ce mot de passe</p>",
html_sco_header.sco_footer(),
]
)
#
H.append(
"""<h2>Changement du mot de passe de <font color="red">%(nomplogin)s</font></h2>
<p>
<form action="change_password" method="post"><table>
<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>
</table>
<input type="hidden" value="%(user_name)s" name="user_name">
<input type="submit" value="Changer">
</p>
<p class="help">Note: en ScoDoc 9, les utilisateurs peuvent changer eux-même leur mot de passe
en indiquant l'adresse mail associée à leur compte.
</p>
"""
% {"nomplogin": u.get_nomplogin(), "user_name": user_name}
form = ChangePasswordForm(user_name=user.user_name, email=user.email)
destination = url_for(
"users.user_info_page",
scodoc_dept=g.scodoc_dept,
user_name=user_name,
)
if request.method == "POST" and form.cancel.data: # cancel button clicked
return redirect(destination)
if form.validate_on_submit():
messages = []
if form.new_password.data != "": # change password
user.set_password(form.new_password.data)
messages.append("Mot de passe modifié")
if form.email.data.strip() != user.email: # change email
user.email = form.email.data.strip()
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"])
@ -660,7 +816,7 @@ def change_password(user_name, password, password2):
% user_name
)
else:
if not sco_users.is_valid_password(password):
if not is_valid_password(password):
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><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" )
# ---- 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']}")
mod = mods[0]

View File

@ -58,7 +58,6 @@ redis==3.5.3
reportlab==3.6.1
requests==2.26.0
rq==1.9.0
six==1.16.0
SQLAlchemy==1.4.22
toml==0.10.2
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
modules = sco_edit_module.do_module_list({"formation_id": formation_id})
modules = sco_edit_module.module_list({"formation_id": formation_id})
mods_imp = []
for mod in modules:
mi = G.create_moduleimpl(

View File

@ -170,7 +170,7 @@ class ScoFake(object):
if numero is None:
numero = sco_edit_ue.next_ue_numero(formation_id, 0)
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:
raise ScoValueError("ue not created !")
return oids[0]
@ -178,7 +178,7 @@ class ScoFake(object):
@logging_meth
def create_matiere(self, ue_id=None, titre=None, numero=None):
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:
raise ScoValueError("matiere not created !")
return oids[0]
@ -203,7 +203,7 @@ class ScoFake(object):
module_type=None,
):
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:
raise ScoValueError("module not created ! (oid=%s)" % oid)
return oids[0]
@ -251,7 +251,7 @@ class ScoFake(object):
if not responsable_id:
responsable_id = self.default_user.id
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:
raise ScoValueError("moduleimpl not created !")
return oids[0]

View File

@ -119,7 +119,7 @@ def test_abs_basic(test_client):
- sco_abs.get_abs_count(etudid, sem)
- ListeAbsEtud
- partition_create
- createGroup
- create_group
- set_group
- EtatAbsenceGr
- AddBilletAbsence
@ -268,7 +268,7 @@ def test_abs_basic(test_client):
partition_name="Eleve",
)
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

View File

@ -30,14 +30,14 @@
# - formation_list
# - formation_export
# - formsemestre_list
# - do_moduleimpl_list
# - moduleimpl_list
# - do_module_impl_with_module_list
# - do_formsemestre_delete
# - do_module_list
# - module_list
# - do_module_delete
# - do_matiere_list
# - matiere_list
# - do_matiere_delete
# - do_ue_list
# - ue_list
# - do_ue_delete
# - do_formation_delete
@ -231,28 +231,24 @@ def test_formations(test_client):
# --- Liste des modules
lim_sem1 = sco_moduleimpl.do_moduleimpl_list(
formsemestre_id=sem1["formsemestre_id"]
)
lim_sem1 = sco_moduleimpl.moduleimpl_list(formsemestre_id=sem1["formsemestre_id"])
assert len(lim_sem1) == 2
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"])
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
lim_modimpl_id = sco_moduleimpl.do_moduleimpl_list(
moduleimpl_id=mi["moduleimpl_id"]
)
lim_modimpl_id = sco_moduleimpl.moduleimpl_list(moduleimpl_id=mi["moduleimpl_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
liimp_sem1 = sco_moduleimpl.do_moduleimpl_withmodule_list(
liimp_sem1 = sco_moduleimpl.moduleimpl_withmodule_list(
formsemestre_id=sem1["formsemestre_id"]
)
@ -262,16 +258,14 @@ def test_formations(test_client):
liimp_sem1[0]["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"]
)
assert modt["module_id"] == liimp_sem2[0]["module_id"]
liimp_modid = sco_moduleimpl.do_moduleimpl_withmodule_list(
module_id=mod["module_id"]
)
liimp_modid = sco_moduleimpl.moduleimpl_withmodule_list(module_id=mod["module_id"])
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"]
)
@ -296,31 +290,29 @@ def test_formations(test_client):
# 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
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"]])
# 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
lim_sem2 = sco_moduleimpl.do_moduleimpl_list(
formsemestre_id=sem2["formsemestre_id"]
)
lim_sem2 = sco_moduleimpl.moduleimpl_list(formsemestre_id=sem2["formsemestre_id"])
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
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
li_ue = sco_edit_ue.do_ue_list()
li_ue = sco_edit_ue.ue_list()
assert len(li_ue) == 4
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
# --- Suppression d'une formation
@ -363,7 +355,7 @@ def test_import_formation(test_client):
)
]
# 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:
mi = G.create_moduleimpl(
module_id=mod["module_id"],

View File

@ -52,7 +52,7 @@ else
SN=""
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})"
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) }')
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"