forked from ScoDoc/ScoDoc
resize select control for placement
This commit is contained in:
parent
a447c6e5f9
commit
44db5028fd
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
@ -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, ...)
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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))
|
||||
|
@ -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})
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
|
@ -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"],
|
||||
)
|
||||
|
@ -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:
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.0.45"
|
||||
SCOVERSION = "9.0.43"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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"}
|
||||
|
@ -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}'"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user