From 8bfa936361ec6840099cc5d90984c86f3074909a Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 29 Sep 2021 14:15:12 +0200 Subject: [PATCH 01/38] fix delete etud --- app/auth/models.py | 2 +- app/views/scolar.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/auth/models.py b/app/auth/models.py index 3a994e95..ed20d5ee 100644 --- a/app/auth/models.py +++ b/app/auth/models.py @@ -422,7 +422,7 @@ class UserRole(db.Model): def get_super_admin(): - """L'utilisateur admin (où le premier, s'il y en a plusieurs). + """L'utilisateur admin (ou 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() diff --git a/app/views/scolar.py b/app/views/scolar.py index 9dffed32..6750ddf4 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -1589,6 +1589,7 @@ def etudident_delete(etudid, dialog_confirmed=False): "admissions", "adresse", "absences", + "absences_notifications", "billet_absence", ] cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) From 51fec2d301b7f5224c483dc0f3ed1a6e0e604761 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 29 Sep 2021 14:47:43 +0200 Subject: [PATCH 02/38] =?UTF-8?q?Change=20le=20type=20de=20bulletin=20par?= =?UTF-8?q?=20d=C3=A9faut=20pour=20les=20nouveaux=20d=C3=A9partements.=20L?= =?UTF-8?q?e=20type=20"exemple"=20n'est=20propos=C3=A9=20qu'en=20dev.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 ++++++--- app/__init__.py | 10 +++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 753fc3dc..014e6869 100644 --- a/README.md +++ b/README.md @@ -106,13 +106,15 @@ 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 -puis de les lancer normalement, par exemple: +(si elle n'existe pas, voir plus loin pour la créer) puis de les lancer +normalement, par exemple: pytest tests/unit/test_sco_basic.py @@ -133,7 +135,8 @@ 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: +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 !). dropdb SCODOC_DEV tools/create_database.sh SCODOC_DEV # créé base SQL diff --git a/app/__init__.py b/app/__init__.py index a3f04839..25ea4f6b 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -259,15 +259,19 @@ 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 - sco_bulletins_generator.register_bulletin_class(BulletinGeneratorExample) - sco_bulletins_generator.register_bulletin_class(BulletinGeneratorLegacy) + # 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(BulletinGeneratorLegacy) 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 From 1f125d3a1da04c0798c33278061b3cdcdaa27c53 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 29 Sep 2021 20:08:18 +0200 Subject: [PATCH 03/38] fix: import etudiants hors semestre --- app/scodoc/sco_excel.py | 12 +++++------- app/scodoc/sco_import_etuds.py | 28 +++++++++++++--------------- app/views/scolar.py | 3 ++- sco_version.py | 2 +- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index e8c02fe9..6904062c 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -568,10 +568,9 @@ 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, ..) - """ ) @@ -580,10 +579,9 @@ def excel_file_to_list(filename): return _excel_to_list(filename) except: raise ScoValueError( - """scolars_import_excel_file: un contenu xlsx - semble corrompu ! - Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ...) - """ + """Le fichier xlsx attendu n'est pas lisible ! + Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ...) + """ ) diff --git a/app/scodoc/sco_import_etuds.py b/app/scodoc/sco_import_etuds.py index 1c366268..1107b11f 100644 --- a/app/scodoc/sco_import_etuds.py +++ b/app/scodoc/sco_import_etuds.py @@ -25,16 +25,16 @@ # ############################################################################## -""" Importation des etudiants à partir de fichiers CSV +""" Importation des étudiants à 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, + datafile: io.BytesIO, formsemestre_id=None, check_homonyms=True, require_ine=False, @@ -414,16 +414,14 @@ def scolars_import_excel_file( if NbHomonyms: NbImportedHomonyms += 1 # Insert in DB tables - formsemestre_to_invalidate.add( - _import_one_student( - cnx, - formsemestre_id, - values, - GroupIdInferers, - annee_courante, - created_etudids, - linenum, - ) + formsemestre_id_etud = _import_one_student( + cnx, + formsemestre_id, + values, + GroupIdInferers, + annee_courante, + created_etudids, + linenum, ) # Verification proportion d'homonymes: si > 10%, abandonne @@ -522,7 +520,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. @@ -566,7 +564,7 @@ def _import_one_student( ) do_formsemestre_inscription_with_modules( - args["formsemestre_id"], + int(args["formsemestre_id"]), etudid, group_ids, etat="I", diff --git a/app/views/scolar.py b/app/views/scolar.py index 6750ddf4..ab02fa31 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -1757,6 +1757,7 @@ 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 = ( @@ -1889,7 +1890,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=formsemestre_id, + formsemestre_id=int(formsemestre_id) if formsemestre_id else None, check_homonyms=tf[2]["check_homonyms"], require_ine=tf[2]["require_ine"], ) diff --git a/sco_version.py b/sco_version.py index dd1416cb..dc3e8dbd 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.0.44" +SCOVERSION = "9.0.45" SCONAME = "ScoDoc" From 8463d368a1f7e842910bdfae20158f0343131fad Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Thu, 30 Sep 2021 09:37:18 +0200 Subject: [PATCH 04/38] Fix: report_debouche_date --- app/scodoc/sco_debouche.py | 34 +++++++++++++++++++++++----------- app/views/notes.py | 4 ++++ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/app/scodoc/sco_debouche.py b/app/scodoc/sco_debouche.py index b32b39de..4c0b8916 100644 --- a/app/scodoc/sco_debouche.py +++ b/app/scodoc/sco_debouche.py @@ -48,9 +48,19 @@ 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() + 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" + ) + if format == "xls": keep_numeric = True # pas de conversion des notes en strings else: @@ -96,8 +106,9 @@ 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}, + {"start_date": start_date, "dept_id": g.scodoc_dept_id}, ) return [x["etudid"] for x in r] @@ -193,15 +204,16 @@ def table_debouche_etudids(etudids, keep_numeric=True): return tab -def report_debouche_ask_date(): +def report_debouche_ask_date(msg: str) -> str: """Formulaire demande date départ""" - return ( - html_sco_header.sco_header() - + """
- Date de départ de la recherche: -
""" - + html_sco_header.sco_footer() - ) + return f"""{html_sco_header.sco_header()} +

Table des débouchés des étudiants

+
+ {msg} + +
+ {html_sco_header.sco_footer()} + """ # ---------------------------------------------------------------------------- diff --git a/app/views/notes.py b/app/views/notes.py index 103121ec..a2f00a45 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -275,6 +275,10 @@ 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, From a447c6e5f99ed4763797c07c3638e6d42444ee8d Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Thu, 30 Sep 2021 14:35:21 +0200 Subject: [PATCH 05/38] =?UTF-8?q?Fix=20regression:=20validations=20UE=20qu?= =?UTF-8?q?and=20semestre=20valid=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_parcours_dut.py | 2 +- tests/unit/test_sco_basic.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/app/scodoc/sco_parcours_dut.py b/app/scodoc/sco_parcours_dut.py index be4f983f..cb10bf4b 100644 --- a/app/scodoc/sco_parcours_dut.py +++ b/app/scodoc/sco_parcours_dut.py @@ -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 isinstance(ue_status["moy"], float): + elif not isinstance(ue_status["moy"], float): # aucune note (pas de moyenne) dans l'UE: ne la valide pas code_ue = None elif valid_semestre: diff --git a/tests/unit/test_sco_basic.py b/tests/unit/test_sco_basic.py index 88f3bd99..6917ee61 100644 --- a/tests/unit/test_sco_basic.py +++ b/tests/unit/test_sco_basic.py @@ -27,6 +27,7 @@ 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 @@ -197,3 +198,19 @@ 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"} From 4a3e37d371ad1a362e389dad5e8fa668c6dd1fe3 Mon Sep 17 00:00:00 2001 From: Jean-Marie Place Date: Thu, 30 Sep 2021 17:11:03 +0200 Subject: [PATCH 06/38] ameliioration placement (adapte la taille du select au contenu) --- app/scodoc/sco_placement.py | 3 +-- app/templates/scodoc/forms/placement.html | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/scodoc/sco_placement.py b/app/scodoc/sco_placement.py index ac39c2bb..c2ef9d93 100644 --- a/app/scodoc/sco_placement.py +++ b/app/scodoc/sco_placement.py @@ -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 diff --git a/app/templates/scodoc/forms/placement.html b/app/templates/scodoc/forms/placement.html index 5d2c3c20..7631b743 100644 --- a/app/templates/scodoc/forms/placement.html +++ b/app/templates/scodoc/forms/placement.html @@ -3,7 +3,7 @@ {% macro render_field(field) %} {{ field.label }} - {{ field()|safe }} + {{ field(**kwargs)|safe }} {% if field.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) }}