resize select control for placement

This commit is contained in:
Jean-Marie Place 2021-09-30 16:52:23 +02:00
parent a447c6e5f9
commit 44db5028fd
19 changed files with 61 additions and 125 deletions

View File

@ -106,15 +106,13 @@ Ou avec couverture (`pip install pytest-cov`)
#### Utilisation des tests unitaires pour initialiser la base de dev
On peut aussi utiliser les tests unitaires pour mettre la base
de données de développement dans un état connu, par exemple pour éviter de
recréer à la main étudianst et semestres quand on développe.
de données de développement dans un état connu, par exemple pour éviter de recréer à la main étudianst et semestres quand on développe.
Il suffit de positionner une variable d'environnement indiquant la BD utilisée par les tests:
export SCODOC_TEST_DATABASE_URI=postgresql:///SCODOC_DEV
(si elle n'existe pas, voir plus loin pour la créer) puis de les lancer
normalement, par exemple:
puis de les lancer normalement, par exemple:
pytest tests/unit/test_sco_basic.py
@ -135,8 +133,7 @@ On utilise SQLAlchemy avec Alembic et Flask-Migrate.
Ne pas oublier de commiter les migrations (`git add migrations` ...).
Mémo pour développeurs: séquence re-création d'une base (vérifiez votre `.env`
ou variables d'environnement pour interroger la bonne base !).
Mémo pour développeurs: séquence re-création d'une base:
dropdb SCODOC_DEV
tools/create_database.sh SCODOC_DEV # créé base SQL

View File

@ -259,19 +259,15 @@ def create_app(config_class=DevConfig):
)
# ---- INITIALISATION SPECIFIQUES A SCODOC
from app.scodoc import sco_bulletins_generator
from app.scodoc.sco_bulletins_example import BulletinGeneratorExample
from app.scodoc.sco_bulletins_legacy import BulletinGeneratorLegacy
from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard
from app.scodoc.sco_bulletins_ucac import BulletinGeneratorUCAC
# l'ordre est important, le premeir sera le "défaut" pour les nouveaux départements.
sco_bulletins_generator.register_bulletin_class(BulletinGeneratorStandard)
sco_bulletins_generator.register_bulletin_class(BulletinGeneratorExample)
sco_bulletins_generator.register_bulletin_class(BulletinGeneratorLegacy)
sco_bulletins_generator.register_bulletin_class(BulletinGeneratorStandard)
sco_bulletins_generator.register_bulletin_class(BulletinGeneratorUCAC)
if app.testing or app.debug:
from app.scodoc.sco_bulletins_example import BulletinGeneratorExample
sco_bulletins_generator.register_bulletin_class(BulletinGeneratorExample)
return app

View File

@ -177,9 +177,8 @@ class User(UserMixin, db.Model):
if "roles_string" in data:
self.user_roles = []
for r_d in data["roles_string"].split(","):
if r_d:
role, dept = UserRole.role_dept_from_string(r_d)
self.add_role(role, dept)
role, dept = UserRole.role_dept_from_string(r_d)
self.add_role(role, dept)
def get_token(self, expires_in=3600):
now = datetime.utcnow()
@ -330,7 +329,7 @@ class Role(db.Model):
"""Roles for ScoDoc"""
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True) # TODO: , nullable=False))
name = db.Column(db.String(64), unique=True)
default = db.Column(db.Boolean, default=False, index=True)
permissions = db.Column(db.BigInteger) # 64 bits
users = db.relationship("User", secondary="user_role", viewonly=True)
@ -389,7 +388,7 @@ class UserRole(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
role_id = db.Column(db.Integer, db.ForeignKey("role.id"))
dept = db.Column(db.String(64)) # dept acronym ou NULL
dept = db.Column(db.String(64)) # dept acronym
user = db.relationship(
User, backref=db.backref("user_roles", cascade="all, delete-orphan")
)
@ -408,9 +407,6 @@ class UserRole(db.Model):
"""
fields = role_dept.split("_", 1) # maxsplit=1, le dept peut contenir un "_"
if len(fields) != 2:
current_app.logger.warning(
f"role_dept_from_string: Invalid role_dept '{role_dept}'"
)
raise ScoValueError("Invalid role_dept")
role_name, dept = fields
if dept == "":
@ -422,7 +418,7 @@ class UserRole(db.Model):
def get_super_admin():
"""L'utilisateur admin (ou le premier, s'il y en a plusieurs).
"""L'utilisateur admin (où le premier, s'il y en a plusieurs).
Utilisé par les tests unitaires et le script de migration.
"""
admin_role = Role.query.filter_by(name="SuperAdmin").first()

View File

@ -167,23 +167,6 @@ def bonus_iutlh(notes_sport, coefs, infos=None):
return bonus
def bonus_nantes(notes_sport, coefs, infos=None):
"""IUT de Nantes (Septembre 2018)
Nous avons différents types de bonification
bonfication Sport / Culture / engagement citoyen
Nous ajoutons sur le bulletin une bonification de 0,2 pour chaque item
la bonification totale ne doit pas excéder les 0,5 point.
Sur le bulletin nous ne mettons pas une note sur 20 mais directement les bonifications.
Dans ScoDoc: on a déclaré une UE "sport&culture" dans laquelle on aura des modules
pour chaque activité (Sport, Associations, ...)
avec à chaque fois une note (ScoDoc l'affichera comme une note sur 20, mais en fait ce sera la
valeur de la bonification: entrer 0,1/20 signifiera un bonus de 0,1 point la moyenne générale)
"""
bonus = min(0.5, sum([x for x in notes_sport])) # plafonnement à 0.5 points
return bonus
# Bonus sport IUT Tours
def bonus_tours(notes_sport, coefs, infos=None):
"""Calcul bonus sport & culture IUT Tours sur moyenne generale

View File

@ -48,19 +48,9 @@ import sco_version
def report_debouche_date(start_year=None, format="html"):
"""Rapport (table) pour les débouchés des étudiants sortis
à partir de l'année indiquée.
"""
"""Rapport (table) pour les débouchés des étudiants sortis à partir de l'année indiquée."""
if not start_year:
return report_debouche_ask_date("Année de début de la recherche")
else:
try:
start_year = int(start_year)
except ValueError:
return report_debouche_ask_date(
"Année invalide. Année de début de la recherche"
)
return report_debouche_ask_date()
if format == "xls":
keep_numeric = True # pas de conversion des notes en strings
else:
@ -106,9 +96,8 @@ def get_etudids_with_debouche(start_year):
FROM notes_formsemestre_inscription i, notes_formsemestre s, itemsuivi it
WHERE i.etudid = it.etudid
AND i.formsemestre_id = s.id AND s.date_fin >= %(start_date)s
AND s.dept_id = %(dept_id)s
""",
{"start_date": start_date, "dept_id": g.scodoc_dept_id},
{"start_date": start_date},
)
return [x["etudid"] for x in r]
@ -204,16 +193,15 @@ def table_debouche_etudids(etudids, keep_numeric=True):
return tab
def report_debouche_ask_date(msg: str) -> str:
def report_debouche_ask_date():
"""Formulaire demande date départ"""
return f"""{html_sco_header.sco_header()}
<h2>Table des débouchés des étudiants</h2>
<form method="GET">
{msg}
<input type="text" name="start_year" value="" size=10/>
</form>
{html_sco_header.sco_footer()}
"""
return (
html_sco_header.sco_header()
+ """<form method="GET">
Date de départ de la recherche: <input type="text" name="start_year" value="" size=10/>
</form>"""
+ html_sco_header.sco_footer()
)
# ----------------------------------------------------------------------------

View File

@ -568,9 +568,10 @@ def excel_bytes_to_list(bytes_content):
return _excel_to_list(filelike)
except:
raise ScoValueError(
"""Le fichier xlsx attendu n'est pas lisible !
Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ..)
"""
scolars_import_excel_file: un contenu xlsx semble corrompu!
peut-être avez vous fourni un fichier au mauvais format (txt, xls, ..)
"""
)
@ -579,9 +580,10 @@ def excel_file_to_list(filename):
return _excel_to_list(filename)
except:
raise ScoValueError(
"""Le fichier xlsx attendu n'est pas lisible !
Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ...)
"""
"""scolars_import_excel_file: un contenu xlsx
semble corrompu !
Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ...)
"""
)

View File

@ -25,16 +25,16 @@
#
##############################################################################
""" Importation des étudiants à partir de fichiers CSV
""" Importation des etudiants à partir de fichiers CSV
"""
import collections
import io
import os
import re
import time
from datetime import date
import flask
from flask import g, url_for
import app.scodoc.sco_utils as scu
@ -252,7 +252,7 @@ def students_import_excel(
def scolars_import_excel_file(
datafile: io.BytesIO,
datafile,
formsemestre_id=None,
check_homonyms=True,
require_ine=False,
@ -414,14 +414,16 @@ def scolars_import_excel_file(
if NbHomonyms:
NbImportedHomonyms += 1
# Insert in DB tables
formsemestre_id_etud = _import_one_student(
cnx,
formsemestre_id,
values,
GroupIdInferers,
annee_courante,
created_etudids,
linenum,
formsemestre_to_invalidate.add(
_import_one_student(
cnx,
formsemestre_id,
values,
GroupIdInferers,
annee_courante,
created_etudids,
linenum,
)
)
# Verification proportion d'homonymes: si > 10%, abandonne
@ -520,7 +522,7 @@ def _import_one_student(
annee_courante,
created_etudids,
linenum,
) -> int:
):
"""
Import d'un étudiant et inscription dans le semestre.
Return: id du semestre dans lequel il a été inscrit.
@ -564,7 +566,7 @@ def _import_one_student(
)
do_formsemestre_inscription_with_modules(
int(args["formsemestre_id"]),
args["formsemestre_id"],
etudid,
group_ids,
etat="I",

View File

@ -231,10 +231,8 @@ def import_users(users):
roles_list = []
for role in u["roles"].split(","):
try:
role = role.strip()
if role:
_, _ = UserRole.role_dept_from_string(role)
roles_list.append(role)
_, _ = UserRole.role_dept_from_string(role.strip())
roles_list.append(role.strip())
except ScoValueError as value_error:
user_ok = False
append_msg("role %s : %s" % (role, value_error))

View File

@ -61,7 +61,7 @@ def do_evaluation_listenotes():
Affichage des notes d'une évaluation
args: evaluation_id ou moduleimpl_id
(si moduleimpl_id, affiche toutes les évaluations du module)
(si moduleimpl_id, affiche toutes les évaluatons du module)
"""
mode = None
vals = scu.get_request_args()
@ -69,7 +69,7 @@ def do_evaluation_listenotes():
evaluation_id = int(vals["evaluation_id"])
mode = "eval"
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
if "moduleimpl_id" in vals and vals["moduleimpl_id"]:
if "moduleimpl_id" in vals:
moduleimpl_id = int(vals["moduleimpl_id"])
mode = "module"
evals = sco_evaluations.do_evaluation_list({"moduleimpl_id": moduleimpl_id})

View File

@ -916,7 +916,7 @@ def formsemestre_validate_ues(formsemestre_id, etudid, code_etat_sem, assiduite)
and ue_status["moy"] >= nt.parcours.NOTES_BARRE_VALID_UE
):
code_ue = ADM
elif not isinstance(ue_status["moy"], float):
elif isinstance(ue_status["moy"], float):
# aucune note (pas de moyenne) dans l'UE: ne la valide pas
code_ue = None
elif valid_semestre:

View File

@ -88,10 +88,9 @@ def _get_group_info(evaluation_id):
groups_tree[partition][group_name] = group_id
if partition != TOUS:
has_groups = True
nb_groups = len(groups_tree)
else:
has_groups = False
nb_groups = 1
nb_groups = sum([len(groups_tree[p]) for p in groups_tree])
return groups_tree, has_groups, nb_groups

View File

@ -3,7 +3,7 @@
{% macro render_field(field) %}
<tr>
<td class="wtf-field">{{ field.label }}</td>
<td class="wtf-field">{{ field()|safe }}
<td class="wtf-field">{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
@ -27,7 +27,7 @@
{{ render_field(form.nb_rangs) }}
{{ render_field(form.etiquetage) }}
{% if form.has_groups %}
{{ render_field(form.groups) }}
{{ render_field(form.groups, size=form.nb_groups) }}
<!-- Tentative de recréer le choix des groupes sous forme de cases à cocher // demande à créer des champs wtf dynamiquement
{% for partition in form.groups_tree %}
<tr>

View File

@ -275,10 +275,6 @@ def formsemestre_bulletinetud(
force_publishing=False,
prefer_mail_perso=False,
):
if not etudid:
raise ScoValueError("Paramètre manquant: etudid est requis")
if not formsemestre_id:
raise ScoValueError("Paramètre manquant: formsemestre_id est requis")
return sco_bulletins.formsemestre_bulletinetud(
etudid=etudid,
formsemestre_id=formsemestre_id,

View File

@ -1589,7 +1589,6 @@ def etudident_delete(etudid, dialog_confirmed=False):
"admissions",
"adresse",
"absences",
"absences_notifications",
"billet_absence",
]
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
@ -1757,7 +1756,6 @@ def check_group_apogee(group_id, etat=None, fix=False, fixmail=False):
@scodoc7func
def form_students_import_excel(formsemestre_id=None):
"formulaire import xls"
formsemestre_id = int(formsemestre_id) if formsemestre_id else None
if formsemestre_id:
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
dest_url = (
@ -1890,7 +1888,7 @@ Les champs avec un astérisque (*) doivent être présents (nulls non autorisés
else:
return sco_import_etuds.students_import_excel(
tf[2]["csvfile"],
formsemestre_id=int(formsemestre_id) if formsemestre_id else None,
formsemestre_id=formsemestre_id,
check_homonyms=tf[2]["check_homonyms"],
require_ine=tf[2]["require_ine"],
)

View File

@ -168,21 +168,18 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
orig_roles = { # set des roles existants avant édition
UserRole.role_dept_from_string(role_dept)
for role_dept in initvalues["roles"]
if role_dept
}
if not initvalues["active"]:
editable_roles_set = set() # can't change roles of a disabled user
editable_roles_strings = {
r.name + "_" + (dept or "") for (r, dept) in editable_roles_set
}
orig_roles_strings = {r.name + "_" + (dept or "") for (r, dept) in orig_roles}
editable_roles_strings = {r.name + "_" + dept for (r, dept) in editable_roles_set}
orig_roles_strings = {r.name + "_" + dept for (r, dept) in orig_roles}
# add existing user roles
displayed_roles = list(editable_roles_set.union(orig_roles))
displayed_roles.sort(key=lambda x: (x[1], x[0].name))
displayed_roles_strings = [
r.name + "_" + (dept or "") for (r, dept) in displayed_roles
displayed_roles_strings = [r.name + "_" + dept for (r, dept) in displayed_roles]
displayed_roles_labels = [
"{dept}: {r.name}".format(dept=dept, r=r) for (r, dept) in displayed_roles
]
displayed_roles_labels = [f"{dept}: {r.name}" for (r, dept) in displayed_roles]
disabled_roles = {} # pour desactiver les roles que l'on ne peut pas editer
for i in range(len(displayed_roles_strings)):
if displayed_roles_strings[i] not in editable_roles_strings:

View File

@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION = "9.0.45"
SCOVERSION = "9.0.43"
SCONAME = "ScoDoc"

View File

@ -95,6 +95,7 @@ def _clear_users_db():
try:
db.session.query(UserRole).delete()
db.session.query(User).delete()
db.session.query(User).delete()
db.session.commit()
except:
db.session.rollback()

View File

@ -27,7 +27,6 @@ from app.scodoc import sco_codes_parcours
from app.scodoc import sco_evaluations
from app.scodoc import sco_formsemestre_validation
from app.scodoc import sco_parcours_dut
from app.scodoc import sco_cache
from app.scodoc import sco_saisie_notes
from app.scodoc import sco_utils as scu
@ -198,19 +197,3 @@ def run_sco_basic(verbose=False):
assert not sco_parcours_dut.formsemestre_has_decisions(
sem["formsemestre_id"]
), "décisions non effacées"
# --- Décision de jury et validations des ECTS d'UE
for etud in etuds[:5]: # les etudiants notés
sco_formsemestre_validation.formsemestre_validation_etud_manu(
sem["formsemestre_id"],
etud["etudid"],
code_etat=sco_codes_parcours.ADJ,
assidu=True,
redirect=False,
)
# Vérifie que toutes les UE des étudiants notés ont été acquises:
nt = sco_cache.NotesTableCache.get(sem["formsemestre_id"])
for etud in etuds[:5]:
dec_ues = nt.get_etud_decision_ues(etud["etudid"])
for ue_id in dec_ues:
assert dec_ues[ue_id]["code"] in {"ADM", "CMP"}

View File

@ -30,7 +30,7 @@ def import_scodoc7_user_db(scodoc7_db="dbname=SCOUSERS"):
# ensure that user_name will match VALID_LOGIN_EXP
user_name = scu.purge_chars(
user_name,
allowed_chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@\\-_.",
allowed_chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@\\-_.",
)
if user_name != u7["user_name"]:
msg = f"""Changing login '{u7["user_name"]}' to '{user_name}'"""