From 61830180a5fc841eb6b7b42c7955c75fa6ddb0b7 Mon Sep 17 00:00:00 2001 From: leonard_montalbano Date: Mon, 9 May 2022 16:26:23 +0200 Subject: [PATCH] merge du master + ajout de abs_group_etat + ajout de test pour departements et etudiants --- app/api/absences.py | 22 +- app/api/departements.py | 160 +++++---- app/api/etudiants.py | 156 ++++----- app/api/evaluations.py | 2 +- app/api/formations.py | 75 +--- app/api/formsemestres.py | 322 ++++++++---------- app/api/partitions.py | 12 +- app/api/tools.py | 8 +- app/but/bulletin_but_pdf.py | 7 +- app/models/etudiants.py | 1 + app/models/evaluations.py | 5 +- app/models/formsemestre.py | 14 +- app/pe/pe_settag.py | 4 +- app/scodoc/sco_abs_views.py | 2 +- app/scodoc/sco_bulletins_legacy.py | 11 +- app/scodoc/sco_dept.py | 6 +- app/scodoc/sco_formsemestre_inscriptions.py | 13 +- app/scodoc/sco_moduleimpl_inscriptions.py | 9 +- app/scodoc/sco_page_etud.py | 23 +- app/scodoc/sco_preferences.py | 17 +- app/scodoc/sco_saisie_notes.py | 37 +- migrations/env.py | 22 +- ...773_scodoc_9_0_4_ajout_id_scodoc7_pour_.py | 14 +- ...added_cascade_on_absences_notifications.py | 29 +- .../39818df276aa_cascades_sur_itemsuivi.py | 52 ++- ...065fb2d20_flag_bloquage_calcul_moyennes.py | 13 +- ...7947e5_scodoc_9_0_4_code_module_en_text.py | 26 +- .../6cfc21a7ae1b_coefs_modules_but.py | 27 +- .../75cf18659984_cascade_tags_modules.py | 45 ++- .../versions/92789d50f6b6_refcomp_index.py | 89 +++-- ...217bf588f4c_scodoc_9_0_13_essai_cascade.py | 47 ++- .../versions/c8efc54586d8_ue_semestre_idx.py | 14 +- ...b2d0092_scodoc_9_0_5_ajout_dept_id_sur_.py | 18 +- ...e7d2e01be1_augmente_taille_codes_apogee.py | 92 +++-- .../f73251d1d825_table_configuration_site.py | 23 +- ...13c9fbd_modif_contrainte_sur_formations.py | 22 +- misc/SuppressAccents.py | 129 ++++--- tests/api/test_api_departements.py | 187 +++++----- tests/api/test_api_etudiants.py | 191 +++-------- tests/api/test_api_formations.py | 132 ++----- tests/api/test_api_formsemestre.py | 196 ++++------- tests/api/test_api_permissions.py | 2 + tests/api/tools_test_api.py | 134 +++++++- tests/unit/test_export_xml.py | 2 +- 44 files changed, 1169 insertions(+), 1243 deletions(-) diff --git a/app/api/absences.py b/app/api/absences.py index db11ebb8..b7c5ee07 100644 --- a/app/api/absences.py +++ b/app/api/absences.py @@ -5,7 +5,7 @@ from flask import jsonify from app.api import bp from app.api.errors import error_response from app.api.auth import token_auth, token_permission_required -from app.api.tools import get_etu_from_etudid_or_nip_or_ine +from app.api.tools import get_etud_from_etudid_or_nip_or_ine from app.scodoc import notesdb as ndb from app.scodoc import sco_abs @@ -50,12 +50,11 @@ def absences(etudid: int = None, nip: int = None, ine: int = None): """ if etudid is None: # Récupération de l'étudiant - etud = get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine) + etud = get_etud_from_etudid_or_nip_or_ine(etudid, nip, ine) if etud is None: return error_response( - 409, - message="La requête ne peut être traitée en l’état actuel.\n " - "Veuillez vérifier que l'id de l'étudiant (etudid, nip, ine) est valide", + 404, + message="id de l'étudiant (etudid, nip, ine) inconnu", ) etudid = etud.etudid @@ -103,16 +102,13 @@ def absences_just(etudid: int = None, nip: int = None, ine: int = None): ] """ if etudid is None: - # Récupération de l'étudiant - try: - etu = get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine) - etudid = etu.etudid - except AttributeError: + etud = get_etud_from_etudid_or_nip_or_ine(etudid, nip, ine) + if etud is None: return error_response( - 409, - message="La requête ne peut être traitée en l’état actuel.\n " - "Veuillez vérifier que l'id de l'étudiant (etudid, nip, ine) est valide", + 404, + message="id de l'étudiant (etudid, nip, ine) inconnu", ) + etudid = etud.etudid # Récupération des absences justifiées de l'étudiant abs_just = [ diff --git a/app/api/departements.py b/app/api/departements.py index ae8bc775..fca5d6a8 100644 --- a/app/api/departements.py +++ b/app/api/departements.py @@ -1,57 +1,66 @@ ############################################### Departements ########################################################## -import json from flask import jsonify +import app from app import models from app.api import bp from app.api.auth import token_auth, token_permission_required -from app.api.errors import error_response +from app.models import Departement, FormSemestre from app.scodoc.sco_permissions import Permission -@bp.route("/departements", methods=["GET"]) +def get_departement(dept_ident: str) -> Departement: + "Le departement, par id ou acronyme. Erreur 404 si pas trouvé." + try: + dept_id = int(dept_ident) + except ValueError: + dept_id = None + if dept_id is None: + return Departement.query.filter_by(acronym=dept_ident).first_or_404() + return Departement.query.get_or_404(dept_id) + + +@bp.route("/departements_ids", methods=["GET"]) @token_auth.login_required @token_permission_required(Permission.APIView) -def departements(): +def departements_ids(): + """Liste des ids de départements""" + return jsonify([dept.id for dept in Departement.query]) + + +@bp.route("/departement/", methods=["GET"]) +@token_auth.login_required +@token_permission_required(Permission.APIView) +def departement(dept_ident: str): """ - Retourne la liste des départements visibles + Info sur un département. Accès par id ou acronyme. Exemple de résultat : - [ { "id": 1, "acronym": "TAPI", "description": null, "visible": true, "date_creation": "Fri, 15 Apr 2022 12:19:28 GMT" - }, - { - "id": 2, - "acronym": "MMI", - "description": null, - "visible": false, - "date_creation": "Fri, 18 Apr 2022 11:20:8 GMT" - }, - ... - ] + } """ - # Récupération de tous les départements - depts = models.Departement.query.all() - - # Mise en place de la liste avec tous les départements - data = [d.to_dict() for d in depts] - - return jsonify(data) + dept = get_departement(dept_ident) + return jsonify(dept.to_dict()) -@bp.route("/departements//etudiants/list", methods=["GET"]) -@bp.route( - "/departements//etudiants/list/", methods=["GET"] -) +@bp.route("/departements", methods=["GET"]) @token_auth.login_required @token_permission_required(Permission.APIView) -def list_etudiants(dept: str, formsemestre_id=None): +def departements(): + """Liste les départements""" + return jsonify([dept.to_dict() for dept in Departement.query]) + + +@bp.route("/departement//etudiants", methods=["GET"]) +@token_auth.login_required +@token_permission_required(Permission.APIView) +def list_etudiants(dept_ident: str): """ Retourne la liste des étudiants d'un département @@ -61,54 +70,39 @@ def list_etudiants(dept: str, formsemestre_id=None): Exemple de résultat : [ { - "civilite": "X", - "code_ine": null, - "code_nip": null, + "civilite": "M", + "ine": "7899X61616", + "nip": "F6777H88", "date_naissance": null, - "email": null, + "email": "toto@toto.fr", "emailperso": null, "etudid": 18, "nom": "MOREL", "prenom": "JACQUES" }, - { - "civilite": "X", - "code_ine": null, - "code_nip": null, - "date_naissance": null, - "email": null, - "emailperso": null, - "etudid": 19, - "nom": "FOURNIER", - "prenom": "ANNE" - }, ... ] """ - # Si le formsemestre_id a été renseigné - if formsemestre_id is not None: - # Récupération du formsemestre - formsemestre = models.FormSemestre.query.filter_by( - id=formsemestre_id - ).first_or_404() - # Récupération du département - departement = formsemestre.departement + # Le département, spécifié par un id ou un acronyme + dept = get_departement(dept_ident) - # Si le formsemestre_id n'a pas été renseigné - else: - # Récupération du formsemestre - departement = models.Departement.query.filter_by(acronym=dept).first_or_404() - - # Mise en forme des données - list_etu = [etu.to_dict_bul(include_urls=False) for etu in departement.etudiants] - - return jsonify(list_etu) + return jsonify([etud.to_dict_short() for etud in dept.etudiants]) -@bp.route("/departements//semestres_courants", methods=["GET"]) +@bp.route("/departement//formsemestres_ids", methods=["GET"]) @token_auth.login_required @token_permission_required(Permission.APIView) -def liste_semestres_courant(dept: str): +def formsemestres_ids(dept_ident: str): + """liste des ids formsemestre du département""" + # Le département, spécifié par un id ou un acronyme + dept = get_departement(dept_ident) + return jsonify([formsemestre.id for formsemestre in dept.formsemestres]) + + +@bp.route("/departement//formsemestres_courants", methods=["GET"]) +@token_auth.login_required +@token_permission_required(Permission.APIView) +def liste_semestres_courant(dept_ident: str): """ Liste des semestres actifs d'un départements donné @@ -117,11 +111,16 @@ def liste_semestres_courant(dept: str): Exemple de résultat : [ { - "date_fin": "31/08/2022", + "titre": "master machine info", + "gestion_semestrielle": false, + "scodoc7_id": null, + "date_debut": "01/09/2021", + "bul_bgcolor": null, + "date_fin": "15/12/2022", "resp_can_edit": false, "dept_id": 1, "etat": true, - "resp_can_change_ens": true, + "resp_can_change_ens": false, "id": 1, "modalite": "FI", "ens_can_edit_eval": false, @@ -131,32 +130,27 @@ def liste_semestres_courant(dept: str): "semestre_id": 1, "bul_hide_xml": false, "elt_annee_apo": null, - "titre": "Semestre test", "block_moyennes": false, - "scodoc7_id": null, - "date_debut": "01/09/2021", - "gestion_semestrielle": false, - "bul_bgcolor": "white", "formsemestre_id": 1, - "titre_num": "Semestre test semestre 1", + "titre_num": "master machine info semestre 1", "date_debut_iso": "2021-09-01", - "date_fin_iso": "2022-08-31", + "date_fin_iso": "2022-12-15", "responsables": [ - 12, - 42 - ], - "titre_court": "BUT MMI" + 3, + 2 + ] }, ... ] """ - # Récupération des départements comportant l'acronym mit en paramètre - dept = models.Departement.query.filter_by(acronym=dept).first_or_404() + # Le département, spécifié par un id ou un acronyme + dept = get_departement(dept_ident) - # Récupération des semestres suivant id_dept - semestres = models.FormSemestre.query.filter_by(dept_id=dept.id, etat=True) + # Les semestres en cours de ce département + formsemestres = models.FormSemestre.query.filter( + FormSemestre.dept_id == dept.id, + FormSemestre.date_debut <= app.db.func.now(), + FormSemestre.date_fin >= app.db.func.now(), + ) - # Mise en forme des données - data = [d.to_dict_api() for d in semestres] - - return jsonify(data) + return jsonify([d.to_dict() for d in formsemestres]) diff --git a/app/api/etudiants.py b/app/api/etudiants.py index dd9935df..afeb8aba 100644 --- a/app/api/etudiants.py +++ b/app/api/etudiants.py @@ -7,12 +7,10 @@ from app import models from app.api import bp from app.api.errors import error_response from app.api.auth import token_auth, token_permission_required -from app.api.tools import get_etu_from_etudid_or_nip_or_ine +from app.api.tools import get_etud_from_etudid_or_nip_or_ine from app.models import FormSemestreInscription, FormSemestre, Identite from app.scodoc import sco_bulletins from app.scodoc import sco_groups -from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud -from app.scodoc.sco_bulletins_pdf import get_etud_bulletins_pdf from app.scodoc.sco_permissions import Permission @@ -22,7 +20,7 @@ from app.scodoc.sco_permissions import Permission @token_permission_required(Permission.APIView) def etudiants_courant(long=False): """ - Retourne la liste des étudiants courant + Liste des étudiants inscrits dans un formsemestre actuellement en cours. Exemple de résultat : [ @@ -43,7 +41,6 @@ def etudiants_courant(long=False): ... ] """ - # Récupération de tous les étudiants etuds = Identite.query.filter( Identite.id == FormSemestreInscription.etudid, FormSemestreInscription.formsemestre_id == FormSemestre.id, @@ -99,8 +96,12 @@ def etudiant(etudid: int = None, nip: int = None, ine: int = None): } """ # Récupération de l'étudiant - etud = get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine) - + etud = get_etud_from_etudid_or_nip_or_ine(etudid, nip, ine) + if etud is None: + return error_response( + 404, + message="id de l'étudiant (etudid, nip, ine) inconnu", + ) # Mise en forme des données data = etud.to_dict_bul(include_urls=False) @@ -122,110 +123,81 @@ def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None) Exemple de résultat : [ - { - "date_fin": "31/08/2022", - "resp_can_edit": false, - "dept_id": 1, - "etat": true, - "resp_can_change_ens": true, - "id": 1, - "modalite": "FI", - "ens_can_edit_eval": false, - "formation_id": 1, - "gestion_compensation": false, - "elt_sem_apo": null, - "semestre_id": 1, - "bul_hide_xml": false, - "elt_annee_apo": null, - "titre": "Semestre test", - "block_moyennes": false, - "scodoc7_id": null, - "date_debut": "01/09/2021", - "gestion_semestrielle": false, - "bul_bgcolor": "white", - "formsemestre_id": 1, - "titre_num": "Semestre test semestre 1", - "date_debut_iso": "2021-09-01", - "date_fin_iso": "2022-08-31", - "responsables": [ - 12, - 42 - ], - "titre_court": "BUT MMI" - }, - ... + { + "date_fin": "31/08/2022", + "resp_can_edit": false, + "dept_id": 1, + "etat": true, + "resp_can_change_ens": true, + "id": 1, + "modalite": "FI", + "ens_can_edit_eval": false, + "formation_id": 1, + "gestion_compensation": false, + "elt_sem_apo": null, + "semestre_id": 1, + "bul_hide_xml": false, + "elt_annee_apo": null, + "titre": "Semestre test", + "block_moyennes": false, + "scodoc7_id": null, + "date_debut": "01/09/2021", + "gestion_semestrielle": false, + "bul_bgcolor": "white", + "formsemestre_id": 1, + "titre_num": "Semestre test semestre 1", + "date_debut_iso": "2021-09-01", + "date_fin_iso": "2022-08-31", + "responsables": [] + }, + ... ] """ # Récupération de l'étudiant - etud = get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine) + etud = get_etud_from_etudid_or_nip_or_ine(etudid, nip, ine) + if etud is None: + return error_response( + 404, + message="id de l'étudiant (etudid, nip, ine) inconnu", + ) formsemestres = models.FormSemestre.query.filter( models.FormSemestreInscription.etudid == etud.id, models.FormSemestreInscription.formsemestre_id == models.FormSemestre.id, ).order_by(models.FormSemestre.date_debut) - return jsonify([formsemestre.to_dict_api() for formsemestre in formsemestres]) + return jsonify([formsemestre.to_dict() for formsemestre in formsemestres]) @bp.route( "/etudiant/etudid//formsemestre//bulletin", methods=["GET"], - defaults={"version": "long", "format": "json"}, + defaults={"version": "long"}, ) @bp.route( "/etudiant/nip//formsemestre//bulletin", methods=["GET"], - defaults={"version": "long", "format": "json"}, + defaults={"version": "long"}, ) @bp.route( "/etudiant/ine//formsemestre//bulletin", methods=["GET"], - defaults={"version": "long", "format": "json"}, -) -@bp.route( - "/etudiant/etudid//formsemestre//bulletin/pdf", - methods=["GET"], - defaults={"version": "long", "format": "pdf"}, -) -@bp.route( - "/etudiant/nip//formsemestre//bulletin/pdf", - methods=["GET"], - defaults={"version": "long", "format": "pdf"}, -) -@bp.route( - "/etudiant/ine//formsemestre//bulletin/pdf", - methods=["GET"], - defaults={"version": "long", "format": "pdf"}, + defaults={"version": "long"}, ) @bp.route( "/etudiant/etudid//formsemestre//bulletin/short", methods=["GET"], - defaults={"version": "short", "format": "json"}, + defaults={"version": "short"}, ) @bp.route( "/etudiant/nip//formsemestre//bulletin/short", methods=["GET"], - defaults={"version": "short", "format": "json"}, + defaults={"version": "short"}, ) @bp.route( "/etudiant/ine//formsemestre//bulletin/short", methods=["GET"], - defaults={"version": "short", "format": "json"}, -) -@bp.route( - "/etudiant/etudid//formsemestre//bulletin/short/pdf", - methods=["GET"], - defaults={"version": "short", "format": "pdf"}, -) -@bp.route( - "/etudiant/nip//formsemestre//bulletin/short/pdf", - methods=["GET"], - defaults={"version": "short", "format": "pdf"}, -) -@bp.route( - "/etudiant/ine//formsemestre//bulletin/short/pdf", - methods=["GET"], - defaults={"version": "short", "format": "pdf"}, + defaults={"version": "short"}, ) @token_auth.login_required @token_permission_required(Permission.APIView) @@ -235,7 +207,6 @@ def etudiant_bulletin_semestre( nip: int = None, ine: int = None, version="long", - format="json", ): """ Retourne le bulletin d'un étudiant en fonction de son id et d'un semestre donné @@ -421,23 +392,15 @@ def etudiant_bulletin_semestre( app.set_sco_dept(dept.acronym) - # Récupération de l'étudiant - try: - etu = get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine) - except AttributeError: + etud = get_etud_from_etudid_or_nip_or_ine(etudid, nip, ine) + if etud is None: return error_response( - 409, - message="La requête ne peut être traitée en l’état actuel.\n " - "Veuillez vérifier que l'id de l'étudiant (etudid, nip, ine) est valide", + 404, + message="id de l'étudiant (etudid, nip, ine) inconnu", ) - if format == "json": - return sco_bulletins.get_formsemestre_bulletin_etud_json( - formsemestre, etu, version - ) - else: - etudid = etu.id - print(etudid) - return do_formsemestre_bulletinetud(formsemestre, etudid, version, format) + return sco_bulletins.get_formsemestre_bulletin_etud_json( + formsemestre, etud, version + ) @bp.route( @@ -490,12 +453,11 @@ def etudiant_groups( ] """ if etudid is None: - etud = get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine) + etud = get_etud_from_etudid_or_nip_or_ine(etudid, nip, ine) if etud is None: return error_response( - 409, - message="La requête ne peut être traitée en l’état actuel.\n " - "Veuillez vérifier que l'id de l'étudiant (etudid, nip, ine) est valide", + 404, + message="id de l'étudiant (etudid, nip, ine) inconnu", ) etudid = etud.etudid diff --git a/app/api/evaluations.py b/app/api/evaluations.py index 7fb1e6c5..cef58a42 100644 --- a/app/api/evaluations.py +++ b/app/api/evaluations.py @@ -99,7 +99,7 @@ def evaluation_notes(evaluation_id: int): data = do_evaluation_get_all_notes(evaluation_id) except AttributeError: # ??? return error_response( - 409, + 404, message="La requête ne peut être traitée en l’état actuel.", ) diff --git a/app/api/formations.py b/app/api/formations.py index e57f74f7..f8842b90 100644 --- a/app/api/formations.py +++ b/app/api/formations.py @@ -29,10 +29,10 @@ def formations_ids(): return jsonify(data) -@bp.route("/formations/", methods=["GET"]) +@bp.route("/formation/", methods=["GET"]) @token_auth.login_required @token_permission_required(Permission.APIView) -def formations_by_id(formation_id: int): +def formation_by_id(formation_id: int): """ Retourne une formation en fonction d'un id donné @@ -63,12 +63,12 @@ def formations_by_id(formation_id: int): @bp.route( - "/formations/formation_export/", + "/formation/formation_export/", methods=["GET"], defaults={"export_ids": False}, ) @bp.route( - "/formations/formation_export//with_ids", + "/formation/formation_export//with_ids", methods=["GET"], defaults={"export_ids": True}, ) @@ -177,16 +177,12 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False): # Utilisation de la fonction formation_export data = sco_formations.formation_export(formation_id, export_ids) except ValueError: - return error_response( - 409, - message="La requête ne peut être traitée en l’état actuel. \n" - "Veillez vérifier la conformité du 'formation_id'", - ) + return error_response(500, message="Erreur inconnue") return jsonify(data) -@bp.route("/formations/moduleimpl/", methods=["GET"]) +@bp.route("/formation/moduleimpl/", methods=["GET"]) @token_auth.login_required @token_permission_required(Permission.APIView) def moduleimpl(moduleimpl_id: int): @@ -199,7 +195,6 @@ def moduleimpl(moduleimpl_id: int): { "id": 1, "formsemestre_id": 1, - "computation_expr": null, "module_id": 1, "responsable_id": 2, "moduleimpl_id": 1, @@ -231,63 +226,7 @@ def moduleimpl(moduleimpl_id: int): @bp.route( - "/formations/moduleimpl/formsemestre//list", - methods=["GET"], -) -@token_auth.login_required -@token_permission_required(Permission.APIView) -def moduleimpls_sem(formsemestre_id: int): - """ - Retourne la liste des moduleimpl d'un semestre - - formsemestre_id : l'id d'un formsemestre - - Exemple d'utilisation : - [ - { - "id": 1, - "formsemestre_id": 1, - "computation_expr": null, - "module_id": 1, - "responsable_id": 2, - "module": { - "heures_tp": 0.0, - "code_apogee": "", - "titre": "Initiation aux r\u00e9seaux informatiques", - "coefficient": 1.0, - "module_type": 2, - "id": 1, - "ects": null, - "abbrev": "Init aux r\u00e9seaux informatiques", - "ue_id": 1, - "code": "R101", - "formation_id": 1, - "heures_cours": 0.0, - "matiere_id": 1, - "heures_td": 0.0, - "semestre_id": 1, - "numero": 10, - "module_id": 1 - }, - "moduleimpl_id": 1, - "ens": [] - }, - ... - ] - """ - formsemestre = models.FormSemestre.query.filter_by( - id=formsemestre_id - ).first_or_404() - - moduleimpls = formsemestre.modimpls_sorted - - data = [moduleimpl.to_dict() for moduleimpl in moduleimpls] - - return jsonify(data) - - -@bp.route( - "/formations//referentiel_competences", + "/formation//referentiel_competences", methods=["GET"], ) @token_auth.login_required diff --git a/app/api/formsemestres.py b/app/api/formsemestres.py index a9116a94..577b2dbc 100644 --- a/app/api/formsemestres.py +++ b/app/api/formsemestres.py @@ -4,14 +4,11 @@ from flask import jsonify import app from app import models from app.api import bp -from app.api.errors import error_response from app.api.auth import token_auth, token_permission_required -from app.api.tools import get_etu_from_etudid_or_nip_or_ine -from app.models import FormSemestre, FormSemestreEtape +from app.models import Departement, FormSemestre, FormSemestreEtape from app.scodoc.sco_bulletins import get_formsemestre_bulletin_etud_json -from app.scodoc.sco_bulletins_json import make_json_formsemestre_bulletinetud from app.scodoc.sco_permissions import Permission -from app.scodoc.sco_pvjury import formsemestre_pvjury +from app.scodoc.sco_utils import ModuleType @bp.route("/formsemestre/", methods=["GET"]) @@ -19,47 +16,50 @@ from app.scodoc.sco_pvjury import formsemestre_pvjury @token_permission_required(Permission.APIView) def formsemestre(formsemestre_id: int): """ - Retourne l'information sur le formsemestre correspondant au formsemestre_id + Information sur le formsemestre indiqué. - formsemestre_id : l'id d'un formsemestre + formsemestre_id : l'id du formsemestre Exemple de résultat : { - "date_fin": "31/08/2022", - "resp_can_edit": false, - "dept_id": 1, - "etat": true, - "resp_can_change_ens": true, - "id": 1, - "modalite": "FI", - "ens_can_edit_eval": false, - "formation_id": 1, - "gestion_compensation": false, - "elt_sem_apo": null, - "semestre_id": 1, - "bul_hide_xml": false, - "elt_annee_apo": null, - "titre": "Semestre test", - "block_moyennes": false, - "scodoc7_id": null, - "date_debut": "01/09/2021", - "gestion_semestrielle": false, - "bul_bgcolor": "white", - "formsemestre_id": 1, - "titre_num": "Semestre test semestre 1", - "date_debut_iso": "2021-09-01", - "date_fin_iso": "2022-08-31", - "responsables": [ - 12, - 42 - ], - "titre_court": "BUT MMI" - } + "block_moyennes": false, + "bul_bgcolor": "white", + "bul_hide_xml": false, + "date_debut_iso": "2021-09-01", + "date_debut": "01/09/2021", + "date_fin_iso": "2022-08-31", + "date_fin": "31/08/2022", + "dept_id": 1, + "elt_annee_apo": null, + "elt_sem_apo": null, + "ens_can_edit_eval": false, + "etat": true, + "formation_id": 1, + "formsemestre_id": 1, + "gestion_compensation": false, + "gestion_semestrielle": false, + "id": 1, + "modalite": "FI", + "resp_can_change_ens": true, + "resp_can_edit": false, + "responsables": [1, 99], // uids + "scodoc7_id": null, + "semestre_id": 1, + "titre_formation" : "BUT GEA", + "titre_num": "BUT GEA semestre 1", + "titre": "BUT GEA", + } + """ - formsemestre = models.FormSemestre.query.filter_by( + formsemestre: FormSemestre = models.FormSemestre.query.filter_by( id=formsemestre_id ).first_or_404() - data = formsemestre.to_dict_api() + data = formsemestre.to_dict() + # Pour le moment on a besoin de fixer le departement + # pour accéder aux préferences + dept = Departement.query.get(formsemestre.dept_id) + app.set_sco_dept(dept.acronym) + data["session_id"] = formsemestre.session_id() return jsonify(data) @@ -73,47 +73,17 @@ def formsemestre_apo(etape_apo: str): etape_apo : un code étape apogée Exemple de résultat : - [ - { - "date_fin": "31/08/2022", - "resp_can_edit": false, - "dept_id": 1, - "etat": true, - "resp_can_change_ens": true, - "id": 1, - "modalite": "FI", - "ens_can_edit_eval": false, - "formation_id": 1, - "gestion_compensation": false, - "elt_sem_apo": null, - "semestre_id": 1, - "bul_hide_xml": false, - "elt_annee_apo": null, - "titre": "Semestre test", - "block_moyennes": false, - "scodoc7_id": null, - "date_debut": "01/09/2021", - "gestion_semestrielle": false, - "bul_bgcolor": "white", - "formsemestre_id": 1, - "titre_num": "Semestre test semestre 1", - "date_debut_iso": "2021-09-01", - "date_fin_iso": "2022-08-31", - "responsables": [ - 12, - 42 - ], - "titre_court": "BUT MMI" - }, - ... - ] + [ + { ...formsemestre... + }, ... + ] """ formsemestres = FormSemestre.query.filter( FormSemestreEtape.etape_apo == etape_apo, FormSemestreEtape.formsemestre_id == FormSemestre.id, ) - return jsonify([formsemestre.to_dict_api() for formsemestre in formsemestres]) + return jsonify([formsemestre.to_dict() for formsemestre in formsemestres]) @bp.route("/formsemestre//bulletins", methods=["GET"]) @@ -349,114 +319,96 @@ def bulletins(formsemestre_id: int): # return jsonify(data) -# XXX A spécifier et compléter TODO -# @bp.route( -# "/formsemestre//programme", -# methods=["GET"], -# ) -# @token_auth.login_required -# @token_permission_required(Permission.APIView) -# def programme(formsemestre_id: int): -# """ -# Retourne la liste des Ues, ressources et SAE d'un semestre -# -# formsemestre_id : l'id d'un formsemestre -# -# Exemple de résultat : -# { -# "ues": [ -# { -# "type": 0, -# "formation_id": 1, -# "ue_code": "UCOD11", -# "id": 1, -# "ects": 12.0, -# "acronyme": "RT1.1", -# "is_external": false, -# "numero": 1, -# "code_apogee": "", -# "titre": "Administrer les r\u00e9seaux et l\u2019Internet", -# "coefficient": 0.0, -# "semestre_idx": 1, -# "color": "#B80004", -# "ue_id": 1 -# }, -# ... -# ], -# "ressources": [ -# { -# "titre": "Fondamentaux de la programmation", -# "coefficient": 1.0, -# "module_type": 2, -# "id": 17, -# "ects": null, -# "abbrev": null, -# "ue_id": 3, -# "code": "R107", -# "formation_id": 1, -# "heures_cours": 0.0, -# "matiere_id": 3, -# "heures_td": 0.0, -# "semestre_id": 1, -# "heures_tp": 0.0, -# "numero": 70, -# "code_apogee": "", -# "module_id": 17 -# }, -# ... -# ], -# "saes": [ -# { -# "titre": "Se pr\u00e9senter sur Internet", -# "coefficient": 1.0, -# "module_type": 3, -# "id": 14, -# "ects": null, -# "abbrev": null, -# "ue_id": 3, -# "code": "SAE14", -# "formation_id": 1, -# "heures_cours": 0.0, -# "matiere_id": 3, -# "heures_td": 0.0, -# "semestre_id": 1, -# "heures_tp": 0.0, -# "numero": 40, -# "code_apogee": "", -# "module_id": 14 -# }, -# ... -# ] -# } -# """ -# -# formsemestre: FormSemestre = models.FormSemestre.query.filter_by( -# id=formsemestre_id -# ).first_or_404() -# -# ues = formsemestre.query_ues() -# -# ues_dict = [] -# ressources = [] -# saes = [] -# -# for ue in ues: -# ues_dict.append(ue.to_dict()) -# ressources = ue.get_ressources() -# saes = ue.get_saes() -# -# data_ressources = [] -# for ressource in ressources: -# data_ressources.append(ressource.to_dict()) -# -# data_saes = [] -# for sae in saes: -# data_saes.append(sae.to_dict()) -# -# data = { -# "ues": ues_dict, -# "ressources": data_ressources, -# "saes": data_saes, -# } -# -# return data +@bp.route( + "/formsemestre//programme", + methods=["GET"], +) +@token_auth.login_required +@token_permission_required(Permission.APIView) +def formsemestre_programme(formsemestre_id: int): + """ + Retourne la liste des Ues, ressources et SAE d'un semestre + + formsemestre_id : l'id d'un formsemestre + + Exemple de résultat : + { + "ues": [ + { + "type": 0, + "formation_id": 1, + "ue_code": "UCOD11", + "id": 1, + "ects": 12.0, + "acronyme": "RT1.1", + "is_external": false, + "numero": 1, + "code_apogee": "", + "titre": "Administrer les r\u00e9seaux et l\u2019Internet", + "coefficient": 0.0, + "semestre_idx": 1, + "color": "#B80004", + "ue_id": 1 + }, + ... + ], + "ressources": [ + { + "ens": [ 10, 18 ], + "formsemestre_id": 1, + "id": 15, + "module": { + "abbrev": "Programmer", + "code": "SAE15", + "code_apogee": "V7GOP", + "coefficient": 1.0, + "formation_id": 1, + "heures_cours": 0.0, + "heures_td": 0.0, + "heures_tp": 0.0, + "id": 15, + "matiere_id": 3, + "module_id": 15, + "module_type": 3, + "numero": 50, + "semestre_id": 1, + "titre": "Programmer en Python", + "ue_id": 3 + }, + "module_id": 15, + "moduleimpl_id": 15, + "responsable_id": 2 + }, + ... + ], + "saes": [ + { + ... + }, + ... + ], + "modules" : [ ... les modules qui ne sont ni des SAEs ni des ressources ... ] + } + """ + formsemestre: FormSemestre = models.FormSemestre.query.filter_by( + id=formsemestre_id + ).first_or_404() + + ues = formsemestre.query_ues() + m_list = { + ModuleType.RESSOURCE: [], + ModuleType.SAE: [], + ModuleType.STANDARD: [], + } + for modimpl in formsemestre.modimpls_sorted: + d = modimpl.to_dict() + m_list[modimpl.module.module_type].append(d) + + return jsonify( + { + "ues": [ue.to_dict() for ue in ues], + "ressources": m_list[ModuleType.RESSOURCE], + "saes": m_list[ModuleType.SAE], + "modules": m_list[ModuleType.STANDARD], + } + ) diff --git a/app/api/partitions.py b/app/api/partitions.py index ed75fd11..1ee74c46 100644 --- a/app/api/partitions.py +++ b/app/api/partitions.py @@ -113,11 +113,7 @@ def etud_in_group(group_id: int, etat=None): data = get_group_members(group_id, etat) if len(data) == 0: - return error_response( - 409, - message="La requête ne peut être traitée en l’état actuel. \n" - "Aucun groupe ne correspond au 'group_id' renseigné", - ) + return error_response(404, message="group_id inconnu") return jsonify(data) @@ -146,8 +142,4 @@ def set_groups( setGroups(partition_id, groups_lists, groups_to_create, groups_to_delete) return error_response(200, message="Groups set") except ValueError: - return error_response( - 409, - message="La requête ne peut être traitée en l’état actuel. \n" - "Veillez vérifier la conformité des éléments passé en paramètres", - ) + return error_response(404, message="Erreur") diff --git a/app/api/tools.py b/app/api/tools.py index 35b07cf2..e03d8334 100644 --- a/app/api/tools.py +++ b/app/api/tools.py @@ -1,15 +1,17 @@ from app import models -def get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine): +def get_etud_from_etudid_or_nip_or_ine( + etudid=None, nip=None, ine=None +) -> models.Identite: """ - Fonction qui retourne un etudiant en fonction de l'etudid, code nip et code ine rentré en paramètres + etudiant en fonction de l'etudid, code nip et code ine rentré en paramètres etudid : None ou un int etudid nip : None ou un int code_nip ine : None ou un int code_ine - Exemple de résultat: + Return None si étudiant inexistant. """ if etudid is None: if nip is None: # si ine diff --git a/app/but/bulletin_but_pdf.py b/app/but/bulletin_but_pdf.py index 36d11d1e..e3e40c51 100644 --- a/app/but/bulletin_but_pdf.py +++ b/app/but/bulletin_but_pdf.py @@ -127,6 +127,9 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard): def ue_rows(self, rows: list, ue_acronym: str, ue: dict, title_bg: tuple): "Décrit une UE dans la table synthèse: titre, sous-titre et liste modules" + if (ue["type"] == UE_SPORT) and len(ue.get("modules", [])) == 0: + # ne mentionne l'UE que s'il y a des modules + return # 1er ligne titre UE moy_ue = ue.get("moyenne") t = { @@ -206,7 +209,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard): for mod_code, mod in ue["modules"].items(): rows.append( { - "titre": f"{mod_code} {mod['titre']}", + "titre": f"{mod_code or ''} {mod['titre'] or ''}", } ) self.evaluations_rows(rows, mod["evaluations"]) @@ -313,7 +316,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard): "lignes des évaluations" for e in evaluations: t = { - "titre": f"{e['description']}", + "titre": f"{e['description'] or ''}", "moyenne": e["note"]["value"], "_moyenne_pdf": Paragraph( f"""{e["note"]["value"]}""" diff --git a/app/models/etudiants.py b/app/models/etudiants.py index 917b0136..912136e6 100644 --- a/app/models/etudiants.py +++ b/app/models/etudiants.py @@ -146,6 +146,7 @@ class Identite(db.Model): return { "id": self.id, "nip": self.code_nip, + "ine": self.code_ine, "nom": self.nom, "nom_usuel": self.nom_usuel, "prenom": self.prenom, diff --git a/app/models/evaluations.py b/app/models/evaluations.py index 46244a9a..84383adc 100644 --- a/app/models/evaluations.py +++ b/app/models/evaluations.py @@ -178,11 +178,12 @@ def evaluation_enrich_dict(e): else: e["descrheure"] = "" # matin, apresmidi: utile pour se referer aux absences: - if heure_debut_dt < datetime.time(12, 00): + + if e["jour"] and heure_debut_dt < datetime.time(12, 00): e["matin"] = 1 else: e["matin"] = 0 - if heure_fin_dt > datetime.time(12, 00): + if e["jour"] and heure_fin_dt > datetime.time(12, 00): e["apresmidi"] = 1 else: e["apresmidi"] = 0 diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 4095ef5c..5bcef071 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -139,6 +139,7 @@ class FormSemestre(db.Model): else: d["date_fin"] = d["date_fin_iso"] = "" d["responsables"] = [u.id for u in self.responsables] + d["titre_formation"] = self.titre_formation() return d def to_dict_api(self): @@ -351,9 +352,10 @@ class FormSemestre(db.Model): ANNEE=annee universitaire de debut (exemple: un S2 de 2013-2014 sera S2-2013) """ - imputation_dept = sco_preferences.get_preference("ImputationDept", self.id) + prefs = sco_preferences.SemPreferences(dept_id=self.dept_id) + imputation_dept = prefs["ImputationDept"] if not imputation_dept: - imputation_dept = sco_preferences.get_preference("DeptName") + imputation_dept = prefs["DeptName"] imputation_dept = imputation_dept.upper() parcours_name = self.formation.get_parcours().NAME modalite = self.modalite @@ -368,7 +370,7 @@ class FormSemestre(db.Model): scu.annee_scolaire_debut(self.date_debut.year, self.date_debut.month) ) return scu.sanitize_string( - "-".join((imputation_dept, parcours_name, modalite, semestre_id, annee_sco)) + f"{imputation_dept}-{parcours_name}-{modalite}-{semestre_id}-{annee_sco}" ) def titre_annee(self) -> str: @@ -380,6 +382,12 @@ class FormSemestre(db.Model): titre_annee += "-" + str(self.date_fin.year) return titre_annee + def titre_formation(self): + """Titre avec formation, court, pour passerelle: "BUT R&T" + (méthode de formsemestre car on pourrait ajouter le semestre, ou d'autres infos, à voir) + """ + return self.formation.acronyme + def titre_mois(self) -> str: """Le titre et les dates du semestre, pour affichage dans des listes Ex: "BUT QLIO (PN 2022) semestre 1 FI (Sept 2022 - Jan 2023)" diff --git a/app/pe/pe_settag.py b/app/pe/pe_settag.py index f4ada213..beee42f9 100644 --- a/app/pe/pe_settag.py +++ b/app/pe/pe_settag.py @@ -97,7 +97,7 @@ class SetTag(pe_tagtable.TableTag): """Mémorise les semtag nécessaires au jury.""" self.SemTagDict = {fid: SemTagDict[fid] for fid in self.get_Fids_in_settag()} if PE_DEBUG >= 1: - pe_print(u" => %d semestres fusionnés" % len(self.SemTagDict)) + pe_print(" => %d semestres fusionnés" % len(self.SemTagDict)) # ------------------------------------------------------------------------------------------------------------------- def comp_data_settag(self): @@ -243,7 +243,7 @@ class SetTagInterClasse(pe_tagtable.TableTag): fid: SetTagDict[fid] for fid in self.get_Fids_in_settag() if fid != None } if PE_DEBUG >= 1: - pe_print(u" => %d semestres utilisés" % len(self.SetTagDict)) + pe_print(" => %d semestres utilisés" % len(self.SetTagDict)) # ------------------------------------------------------------------------------------------------------------------- def comp_data_settag(self): diff --git a/app/scodoc/sco_abs_views.py b/app/scodoc/sco_abs_views.py index a4e1d3a7..8427777f 100644 --- a/app/scodoc/sco_abs_views.py +++ b/app/scodoc/sco_abs_views.py @@ -965,7 +965,7 @@ def _tables_abs_etud( )[0] if format == "html": ex.append( - f"""{mod["module"]["code"] or "(module sans code)"}""" ) diff --git a/app/scodoc/sco_bulletins_legacy.py b/app/scodoc/sco_bulletins_legacy.py index 314abb0d..1dc01ad5 100644 --- a/app/scodoc/sco_bulletins_legacy.py +++ b/app/scodoc/sco_bulletins_legacy.py @@ -132,10 +132,13 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator): if sco_preferences.get_preference( "bul_show_minmax_mod", formsemestre_id ): - rang_minmax = '%s [%s, %s]' % ( - mod["mod_rang_txt"], - scu.fmt_note(mod["stats"]["min"]), - scu.fmt_note(mod["stats"]["max"]), + rang_minmax = ( + '%s [%s, %s]' + % ( + mod["mod_rang_txt"], + scu.fmt_note(mod["stats"]["min"]), + scu.fmt_note(mod["stats"]["max"]), + ) ) else: rang_minmax = mod["mod_rang_txt"] # vide si pas option rang diff --git a/app/scodoc/sco_dept.py b/app/scodoc/sco_dept.py index 14c47651..42f3db76 100644 --- a/app/scodoc/sco_dept.py +++ b/app/scodoc/sco_dept.py @@ -131,8 +131,10 @@ def index_html(showcodes=0, showsemtable=0): if not showsemtable: H.append( f"""
-

Voir tous les semestres ({len(othersems)} verrouillés) +

Voir table des semestres (dont {len(othersems)} + verrouillé{'s' if len(othersems) else ''})

""" ) diff --git a/app/scodoc/sco_formsemestre_inscriptions.py b/app/scodoc/sco_formsemestre_inscriptions.py index b3fe9853..11563c0d 100644 --- a/app/scodoc/sco_formsemestre_inscriptions.py +++ b/app/scodoc/sco_formsemestre_inscriptions.py @@ -534,11 +534,14 @@ def formsemestre_inscription_option(etudid, formsemestre_id): ue_status = nt.get_etud_ue_status(etudid, ue_id) if ue_status and ue_status["is_capitalized"]: sem_origin = sco_formsemestre.get_formsemestre(ue_status["formsemestre_id"]) - ue_descr += ' (capitalisée le %s)' % ( - sem_origin["formsemestre_id"], - etudid, - sem_origin["titreannee"], - ndb.DateISOtoDMY(ue_status["event_date"]), + ue_descr += ( + ' (capitalisée le %s)' + % ( + sem_origin["formsemestre_id"], + etudid, + sem_origin["titreannee"], + ndb.DateISOtoDMY(ue_status["event_date"]), + ) ) descr.append( ( diff --git a/app/scodoc/sco_moduleimpl_inscriptions.py b/app/scodoc/sco_moduleimpl_inscriptions.py index 76871cf3..ce706318 100644 --- a/app/scodoc/sco_moduleimpl_inscriptions.py +++ b/app/scodoc/sco_moduleimpl_inscriptions.py @@ -303,9 +303,12 @@ def moduleimpl_inscriptions_stats(formsemestre_id): ) for mod in options: if can_change: - c_link = '%s' % ( - mod["moduleimpl_id"], - mod["descri"] or "(inscrire des étudiants)", + c_link = ( + '%s' + % ( + mod["moduleimpl_id"], + mod["descri"] or "(inscrire des étudiants)", + ) ) else: c_link = mod["descri"] diff --git a/app/scodoc/sco_page_etud.py b/app/scodoc/sco_page_etud.py index 3a99d223..a030f514 100644 --- a/app/scodoc/sco_page_etud.py +++ b/app/scodoc/sco_page_etud.py @@ -296,17 +296,18 @@ def ficheEtud(etudid=None): if not sco_permissions_check.can_suppress_annotation(a["id"]): a["dellink"] = "" else: - a[ - "dellink" - ] = '%s' % ( - etudid, - a["id"], - scu.icontag( - "delete_img", - border="0", - alt="suppress", - title="Supprimer cette annotation", - ), + a["dellink"] = ( + '%s' + % ( + etudid, + a["id"], + scu.icontag( + "delete_img", + border="0", + alt="suppress", + title="Supprimer cette annotation", + ), + ) ) author = sco_users.user_info(a["author"]) alist.append( diff --git a/app/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py index 9ccb6362..49445bc9 100644 --- a/app/scodoc/sco_preferences.py +++ b/app/scodoc/sco_preferences.py @@ -132,9 +132,12 @@ def clear_base_preferences(): g._SCO_BASE_PREFERENCES = {} # { dept_id: BasePreferences instance } -def get_base_preferences(): - """Return global preferences for the current department""" - dept_id = g.scodoc_dept_id +def get_base_preferences(dept_id: int = None): + """Return global preferences for the specified department + or the current departement + """ + if dept_id is None: + dept_id = g.scodoc_dept_id if not hasattr(g, "_SCO_BASE_PREFERENCES"): g._SCO_BASE_PREFERENCES = {} if not dept_id in g._SCO_BASE_PREFERENCES: @@ -142,12 +145,12 @@ def get_base_preferences(): return g._SCO_BASE_PREFERENCES[dept_id] -def get_preference(name, formsemestre_id=None): +def get_preference(name, formsemestre_id=None, dept_id=None): """Returns value of named preference. All preferences have a sensible default value, so this function always returns a usable value for all defined preferences names. """ - return get_base_preferences().get(formsemestre_id, name) + return get_base_preferences(dept_id=dept_id).get(formsemestre_id, name) def _convert_pref_type(p, pref_spec): @@ -2145,9 +2148,9 @@ class BasePreferences(object): class SemPreferences: """Preferences for a formsemestre""" - def __init__(self, formsemestre_id=None): + def __init__(self, formsemestre_id=None, dept_id=None): self.formsemestre_id = formsemestre_id - self.base_prefs = get_base_preferences() + self.base_prefs = get_base_preferences(dept_id=dept_id) def __getitem__(self, name): return self.base_prefs.get(self.formsemestre_id, name) diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py index 9cda2372..733ef173 100644 --- a/app/scodoc/sco_saisie_notes.py +++ b/app/scodoc/sco_saisie_notes.py @@ -799,22 +799,22 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]): evals = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id}) if not evals: raise ScoValueError("invalid evaluation_id") - E = evals[0] - M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + eval_dict = evals[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=eval_dict["moduleimpl_id"])[0] formsemestre_id = M["formsemestre_id"] 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"]: - indication_date = ndb.DateDMYtoISO(E["jour"]) + if eval_dict["jour"]: + indication_date = ndb.DateDMYtoISO(eval_dict["jour"]) else: - indication_date = scu.sanitize_filename(E["description"])[:12] - evalname = "%s-%s" % (Mod["code"], indication_date) + indication_date = scu.sanitize_filename(eval_dict["description"])[:12] + eval_name = "%s-%s" % (Mod["code"], indication_date) - if E["description"]: - evaltitre = "%s du %s" % (E["description"], E["jour"]) + if eval_dict["description"]: + evaltitre = "%s du %s" % (eval_dict["description"], eval_dict["jour"]) else: - evaltitre = "évaluation du %s" % E["jour"] + evaltitre = "évaluation du %s" % eval_dict["jour"] description = "%s en %s (%s) resp. %s" % ( evaltitre, Mod["abbrev"] or "", @@ -847,7 +847,7 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]): # une liste de liste de chaines: lignes de la feuille de calcul L = [] - etuds = _get_sorted_etuds(E, etudids, formsemestre_id) + etuds = _get_sorted_etuds(eval_dict, etudids, formsemestre_id) for e in etuds: etudid = e["etudid"] groups = sco_groups.get_etud_groups(etudid, formsemestre_id) @@ -865,8 +865,10 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]): ] ) - filename = "notes_%s_%s" % (evalname, gr_title_filename) - xls = sco_excel.excel_feuille_saisie(E, sem["titreannee"], description, lines=L) + filename = "notes_%s_%s" % (eval_name, gr_title_filename) + xls = sco_excel.excel_feuille_saisie( + eval_dict, sem["titreannee"], description, lines=L + ) return scu.send_file(xls, filename, scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE) # return sco_excel.send_excel_file(xls, filename) @@ -1008,10 +1010,9 @@ def saisie_notes(evaluation_id, group_ids=[]): return "\n".join(H) -def _get_sorted_etuds(E, etudids, formsemestre_id): - sem = sco_formsemestre.get_formsemestre(formsemestre_id) +def _get_sorted_etuds(eval_dict: dict, etudids: list, formsemestre_id: int): notes_db = sco_evaluation_db.do_evaluation_get_all_notes( - E["evaluation_id"] + eval_dict["evaluation_id"] ) # Notes existantes cnx = ndb.GetDBConnexion() etuds = [] @@ -1028,9 +1029,9 @@ def _get_sorted_etuds(E, etudids, formsemestre_id): e["groups"] = sco_groups.get_etud_groups(etudid, formsemestre_id) # Information sur absence (tenant compte de la demi-journée) - jour_iso = ndb.DateDMYtoISO(E["jour"]) + jour_iso = ndb.DateDMYtoISO(eval_dict["jour"]) warn_abs_lst = [] - if E["matin"]: + if eval_dict["matin"]: nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=1) nbabsjust = sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=1) if nbabs: @@ -1038,7 +1039,7 @@ def _get_sorted_etuds(E, etudids, formsemestre_id): warn_abs_lst.append("absent justifié le matin !") else: warn_abs_lst.append("absent le matin !") - if E["apresmidi"]: + if eval_dict["apresmidi"]: nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=0) nbabsjust = sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=0) if nbabs: diff --git a/migrations/env.py b/migrations/env.py index 68feded2..0c6eddcb 100755 --- a/migrations/env.py +++ b/migrations/env.py @@ -14,17 +14,17 @@ config = context.config # Interpret the config file for Python logging. # This line sets up loggers basically. fileConfig(config.config_file_name) -logger = logging.getLogger('alembic.env') +logger = logging.getLogger("alembic.env") # add your model's MetaData object here # for 'autogenerate' support # from myapp import mymodel # target_metadata = mymodel.Base.metadata config.set_main_option( - 'sqlalchemy.url', - str(current_app.extensions['migrate'].db.get_engine().url).replace( - '%', '%%')) -target_metadata = current_app.extensions['migrate'].db.metadata + "sqlalchemy.url", + str(current_app.extensions["migrate"].db.get_engine().url).replace("%", "%%"), +) +target_metadata = current_app.extensions["migrate"].db.metadata # other values from the config, defined by the needs of env.py, # can be acquired: @@ -45,9 +45,7 @@ def run_migrations_offline(): """ url = config.get_main_option("sqlalchemy.url") - context.configure( - url=url, target_metadata=target_metadata, literal_binds=True - ) + context.configure(url=url, target_metadata=target_metadata, literal_binds=True) with context.begin_transaction(): context.run_migrations() @@ -65,20 +63,20 @@ def run_migrations_online(): # when there are no changes to the schema # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html def process_revision_directives(context, revision, directives): - if getattr(config.cmd_opts, 'autogenerate', False): + if getattr(config.cmd_opts, "autogenerate", False): script = directives[0] if script.upgrade_ops.is_empty(): directives[:] = [] - logger.info('No changes in schema detected.') + logger.info("No changes in schema detected.") - connectable = current_app.extensions['migrate'].db.get_engine() + connectable = current_app.extensions["migrate"].db.get_engine() with connectable.connect() as connection: context.configure( connection=connection, target_metadata=target_metadata, process_revision_directives=process_revision_directives, - **current_app.extensions['migrate'].configure_args + **current_app.extensions["migrate"].configure_args ) with context.begin_transaction(): diff --git a/migrations/versions/017e32eb4773_scodoc_9_0_4_ajout_id_scodoc7_pour_.py b/migrations/versions/017e32eb4773_scodoc_9_0_4_ajout_id_scodoc7_pour_.py index 41f5f29a..8aff9461 100644 --- a/migrations/versions/017e32eb4773_scodoc_9_0_4_ajout_id_scodoc7_pour_.py +++ b/migrations/versions/017e32eb4773_scodoc_9_0_4_ajout_id_scodoc7_pour_.py @@ -10,21 +10,23 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '017e32eb4773' -down_revision = '6b071b7947e5' +revision = "017e32eb4773" +down_revision = "6b071b7947e5" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.add_column('identite', sa.Column('scodoc7_id', sa.Text(), nullable=True)) - op.add_column('notes_formsemestre', sa.Column('scodoc7_id', sa.Text(), nullable=True)) + op.add_column("identite", sa.Column("scodoc7_id", sa.Text(), nullable=True)) + op.add_column( + "notes_formsemestre", sa.Column("scodoc7_id", sa.Text(), nullable=True) + ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('notes_formsemestre', 'scodoc7_id') - op.drop_column('identite', 'scodoc7_id') + op.drop_column("notes_formsemestre", "scodoc7_id") + op.drop_column("identite", "scodoc7_id") # ### end Alembic commands ### diff --git a/migrations/versions/1efe07413835_added_cascade_on_absences_notifications.py b/migrations/versions/1efe07413835_added_cascade_on_absences_notifications.py index c568000c..0985efb5 100644 --- a/migrations/versions/1efe07413835_added_cascade_on_absences_notifications.py +++ b/migrations/versions/1efe07413835_added_cascade_on_absences_notifications.py @@ -10,21 +10,38 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '1efe07413835' -down_revision = '75cf18659984' +revision = "1efe07413835" +down_revision = "75cf18659984" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('absences_notifications_formsemestre_id_fkey', 'absences_notifications', type_='foreignkey') - op.create_foreign_key(None, 'absences_notifications', 'notes_formsemestre', ['formsemestre_id'], ['id'], ondelete='CASCADE') + op.drop_constraint( + "absences_notifications_formsemestre_id_fkey", + "absences_notifications", + type_="foreignkey", + ) + op.create_foreign_key( + None, + "absences_notifications", + "notes_formsemestre", + ["formsemestre_id"], + ["id"], + ondelete="CASCADE", + ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(None, 'absences_notifications', type_='foreignkey') - op.create_foreign_key('absences_notifications_formsemestre_id_fkey', 'absences_notifications', 'notes_formsemestre', ['formsemestre_id'], ['id']) + op.drop_constraint(None, "absences_notifications", type_="foreignkey") + op.create_foreign_key( + "absences_notifications_formsemestre_id_fkey", + "absences_notifications", + "notes_formsemestre", + ["formsemestre_id"], + ["id"], + ) # ### end Alembic commands ### diff --git a/migrations/versions/39818df276aa_cascades_sur_itemsuivi.py b/migrations/versions/39818df276aa_cascades_sur_itemsuivi.py index a64002be..7cbd8801 100644 --- a/migrations/versions/39818df276aa_cascades_sur_itemsuivi.py +++ b/migrations/versions/39818df276aa_cascades_sur_itemsuivi.py @@ -10,25 +10,57 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '39818df276aa' -down_revision = '1efe07413835' +revision = "39818df276aa" +down_revision = "1efe07413835" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('itemsuivi_tags_assoc_tag_id_fkey', 'itemsuivi_tags_assoc', type_='foreignkey') - op.drop_constraint('itemsuivi_tags_assoc_itemsuivi_id_fkey', 'itemsuivi_tags_assoc', type_='foreignkey') - op.create_foreign_key(None, 'itemsuivi_tags_assoc', 'itemsuivi', ['itemsuivi_id'], ['id'], ondelete='CASCADE') - op.create_foreign_key(None, 'itemsuivi_tags_assoc', 'itemsuivi_tags', ['tag_id'], ['id'], ondelete='CASCADE') + op.drop_constraint( + "itemsuivi_tags_assoc_tag_id_fkey", "itemsuivi_tags_assoc", type_="foreignkey" + ) + op.drop_constraint( + "itemsuivi_tags_assoc_itemsuivi_id_fkey", + "itemsuivi_tags_assoc", + type_="foreignkey", + ) + op.create_foreign_key( + None, + "itemsuivi_tags_assoc", + "itemsuivi", + ["itemsuivi_id"], + ["id"], + ondelete="CASCADE", + ) + op.create_foreign_key( + None, + "itemsuivi_tags_assoc", + "itemsuivi_tags", + ["tag_id"], + ["id"], + ondelete="CASCADE", + ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(None, 'itemsuivi_tags_assoc', type_='foreignkey') - op.drop_constraint(None, 'itemsuivi_tags_assoc', type_='foreignkey') - op.create_foreign_key('itemsuivi_tags_assoc_itemsuivi_id_fkey', 'itemsuivi_tags_assoc', 'itemsuivi', ['itemsuivi_id'], ['id']) - op.create_foreign_key('itemsuivi_tags_assoc_tag_id_fkey', 'itemsuivi_tags_assoc', 'itemsuivi_tags', ['tag_id'], ['id']) + op.drop_constraint(None, "itemsuivi_tags_assoc", type_="foreignkey") + op.drop_constraint(None, "itemsuivi_tags_assoc", type_="foreignkey") + op.create_foreign_key( + "itemsuivi_tags_assoc_itemsuivi_id_fkey", + "itemsuivi_tags_assoc", + "itemsuivi", + ["itemsuivi_id"], + ["id"], + ) + op.create_foreign_key( + "itemsuivi_tags_assoc_tag_id_fkey", + "itemsuivi_tags_assoc", + "itemsuivi_tags", + ["tag_id"], + ["id"], + ) # ### end Alembic commands ### diff --git a/migrations/versions/669065fb2d20_flag_bloquage_calcul_moyennes.py b/migrations/versions/669065fb2d20_flag_bloquage_calcul_moyennes.py index 9479c828..9d50cb4a 100644 --- a/migrations/versions/669065fb2d20_flag_bloquage_calcul_moyennes.py +++ b/migrations/versions/669065fb2d20_flag_bloquage_calcul_moyennes.py @@ -10,19 +10,24 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '669065fb2d20' -down_revision = 'a217bf588f4c' +revision = "669065fb2d20" +down_revision = "a217bf588f4c" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.add_column('notes_formsemestre', sa.Column('block_moyennes', sa.Boolean(), server_default='false', nullable=False)) + op.add_column( + "notes_formsemestre", + sa.Column( + "block_moyennes", sa.Boolean(), server_default="false", nullable=False + ), + ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('notes_formsemestre', 'block_moyennes') + op.drop_column("notes_formsemestre", "block_moyennes") # ### end Alembic commands ### diff --git a/migrations/versions/6b071b7947e5_scodoc_9_0_4_code_module_en_text.py b/migrations/versions/6b071b7947e5_scodoc_9_0_4_code_module_en_text.py index c16e8582..9a41e8dd 100644 --- a/migrations/versions/6b071b7947e5_scodoc_9_0_4_code_module_en_text.py +++ b/migrations/versions/6b071b7947e5_scodoc_9_0_4_code_module_en_text.py @@ -10,25 +10,31 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '6b071b7947e5' -down_revision = '993ce4a01d57' +revision = "6b071b7947e5" +down_revision = "993ce4a01d57" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('notes_modules', 'code', - existing_type=sa.VARCHAR(length=32), - type_=sa.Text(), - existing_nullable=False) + op.alter_column( + "notes_modules", + "code", + existing_type=sa.VARCHAR(length=32), + type_=sa.Text(), + existing_nullable=False, + ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('notes_modules', 'code', - existing_type=sa.Text(), - type_=sa.VARCHAR(length=32), - existing_nullable=False) + op.alter_column( + "notes_modules", + "code", + existing_type=sa.Text(), + type_=sa.VARCHAR(length=32), + existing_nullable=False, + ) # ### end Alembic commands ### diff --git a/migrations/versions/6cfc21a7ae1b_coefs_modules_but.py b/migrations/versions/6cfc21a7ae1b_coefs_modules_but.py index 78bc6d74..94eece1c 100644 --- a/migrations/versions/6cfc21a7ae1b_coefs_modules_but.py +++ b/migrations/versions/6cfc21a7ae1b_coefs_modules_but.py @@ -10,26 +10,33 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '6cfc21a7ae1b' -down_revision = 'ada0d1f3d84f' +revision = "6cfc21a7ae1b" +down_revision = "ada0d1f3d84f" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.create_table('module_ue_coef', - sa.Column('module_id', sa.Integer(), nullable=False), - sa.Column('ue_id', sa.Integer(), nullable=False), - sa.Column('coef', sa.Float(), nullable=False), - sa.ForeignKeyConstraint(['module_id'], ['notes_modules.id'], ), - sa.ForeignKeyConstraint(['ue_id'], ['notes_ue.id'], ), - sa.PrimaryKeyConstraint('module_id', 'ue_id') + op.create_table( + "module_ue_coef", + sa.Column("module_id", sa.Integer(), nullable=False), + sa.Column("ue_id", sa.Integer(), nullable=False), + sa.Column("coef", sa.Float(), nullable=False), + sa.ForeignKeyConstraint( + ["module_id"], + ["notes_modules.id"], + ), + sa.ForeignKeyConstraint( + ["ue_id"], + ["notes_ue.id"], + ), + sa.PrimaryKeyConstraint("module_id", "ue_id"), ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('module_ue_coef') + op.drop_table("module_ue_coef") # ### end Alembic commands ### diff --git a/migrations/versions/75cf18659984_cascade_tags_modules.py b/migrations/versions/75cf18659984_cascade_tags_modules.py index 1a498b07..db034e14 100644 --- a/migrations/versions/75cf18659984_cascade_tags_modules.py +++ b/migrations/versions/75cf18659984_cascade_tags_modules.py @@ -10,25 +10,50 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '75cf18659984' -down_revision = 'd74b4e16fb3c' +revision = "75cf18659984" +down_revision = "d74b4e16fb3c" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('notes_modules_tags_tag_id_fkey', 'notes_modules_tags', type_='foreignkey') - op.drop_constraint('notes_modules_tags_module_id_fkey', 'notes_modules_tags', type_='foreignkey') - op.create_foreign_key(None, 'notes_modules_tags', 'notes_tags', ['tag_id'], ['id'], ondelete='CASCADE') - op.create_foreign_key(None, 'notes_modules_tags', 'notes_modules', ['module_id'], ['id'], ondelete='CASCADE') + op.drop_constraint( + "notes_modules_tags_tag_id_fkey", "notes_modules_tags", type_="foreignkey" + ) + op.drop_constraint( + "notes_modules_tags_module_id_fkey", "notes_modules_tags", type_="foreignkey" + ) + op.create_foreign_key( + None, "notes_modules_tags", "notes_tags", ["tag_id"], ["id"], ondelete="CASCADE" + ) + op.create_foreign_key( + None, + "notes_modules_tags", + "notes_modules", + ["module_id"], + ["id"], + ondelete="CASCADE", + ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(None, 'notes_modules_tags', type_='foreignkey') - op.drop_constraint(None, 'notes_modules_tags', type_='foreignkey') - op.create_foreign_key('notes_modules_tags_module_id_fkey', 'notes_modules_tags', 'notes_modules', ['module_id'], ['id']) - op.create_foreign_key('notes_modules_tags_tag_id_fkey', 'notes_modules_tags', 'notes_tags', ['tag_id'], ['id']) + op.drop_constraint(None, "notes_modules_tags", type_="foreignkey") + op.drop_constraint(None, "notes_modules_tags", type_="foreignkey") + op.create_foreign_key( + "notes_modules_tags_module_id_fkey", + "notes_modules_tags", + "notes_modules", + ["module_id"], + ["id"], + ) + op.create_foreign_key( + "notes_modules_tags_tag_id_fkey", + "notes_modules_tags", + "notes_tags", + ["tag_id"], + ["id"], + ) # ### end Alembic commands ### diff --git a/migrations/versions/92789d50f6b6_refcomp_index.py b/migrations/versions/92789d50f6b6_refcomp_index.py index 8182597d..adc190ed 100644 --- a/migrations/versions/92789d50f6b6_refcomp_index.py +++ b/migrations/versions/92789d50f6b6_refcomp_index.py @@ -10,46 +10,77 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '92789d50f6b6' -down_revision = '00ad500fb118' +revision = "92789d50f6b6" +down_revision = "00ad500fb118" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('modules_acs') - op.drop_table('app_crit') - op.add_column('apc_annee_parcours', sa.Column('ordre', sa.Integer(), nullable=True)) - op.drop_column('apc_annee_parcours', 'numero') - op.create_index(op.f('ix_apc_app_critique_code'), 'apc_app_critique', ['code'], unique=False) - op.create_unique_constraint('apc_competence_referentiel_id_titre_key', 'apc_competence', ['referentiel_id', 'titre']) - op.create_index(op.f('ix_apc_competence_titre'), 'apc_competence', ['titre'], unique=False) - op.add_column('apc_referentiel_competences', sa.Column('scodoc_date_loaded', sa.DateTime(), nullable=True)) - op.add_column('apc_referentiel_competences', sa.Column('scodoc_orig_filename', sa.Text(), nullable=True)) + op.drop_table("modules_acs") + op.drop_table("app_crit") + op.add_column("apc_annee_parcours", sa.Column("ordre", sa.Integer(), nullable=True)) + op.drop_column("apc_annee_parcours", "numero") + op.create_index( + op.f("ix_apc_app_critique_code"), "apc_app_critique", ["code"], unique=False + ) + op.create_unique_constraint( + "apc_competence_referentiel_id_titre_key", + "apc_competence", + ["referentiel_id", "titre"], + ) + op.create_index( + op.f("ix_apc_competence_titre"), "apc_competence", ["titre"], unique=False + ) + op.add_column( + "apc_referentiel_competences", + sa.Column("scodoc_date_loaded", sa.DateTime(), nullable=True), + ) + op.add_column( + "apc_referentiel_competences", + sa.Column("scodoc_orig_filename", sa.Text(), nullable=True), + ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('apc_referentiel_competences', 'scodoc_orig_filename') - op.drop_column('apc_referentiel_competences', 'scodoc_date_loaded') - op.drop_index(op.f('ix_apc_competence_titre'), table_name='apc_competence') - op.drop_constraint('apc_competence_referentiel_id_titre_key', 'apc_competence', type_='unique') - op.drop_index(op.f('ix_apc_app_critique_code'), table_name='apc_app_critique') - op.add_column('apc_annee_parcours', sa.Column('numero', sa.INTEGER(), autoincrement=False, nullable=True)) - op.drop_column('apc_annee_parcours', 'ordre') - op.create_table('app_crit', - sa.Column('id', sa.INTEGER(), server_default=sa.text("nextval('app_crit_id_seq'::regclass)"), autoincrement=True, nullable=False), - sa.Column('code', sa.TEXT(), autoincrement=False, nullable=False), - sa.Column('titre', sa.TEXT(), autoincrement=False, nullable=True), - sa.PrimaryKeyConstraint('id', name='app_crit_pkey'), - postgresql_ignore_search_path=False + op.drop_column("apc_referentiel_competences", "scodoc_orig_filename") + op.drop_column("apc_referentiel_competences", "scodoc_date_loaded") + op.drop_index(op.f("ix_apc_competence_titre"), table_name="apc_competence") + op.drop_constraint( + "apc_competence_referentiel_id_titre_key", "apc_competence", type_="unique" ) - op.create_table('modules_acs', - sa.Column('module_id', sa.INTEGER(), autoincrement=False, nullable=True), - sa.Column('ac_id', sa.INTEGER(), autoincrement=False, nullable=True), - sa.ForeignKeyConstraint(['ac_id'], ['app_crit.id'], name='modules_acs_ac_id_fkey'), - sa.ForeignKeyConstraint(['module_id'], ['notes_modules.id'], name='modules_acs_module_id_fkey') + op.drop_index(op.f("ix_apc_app_critique_code"), table_name="apc_app_critique") + op.add_column( + "apc_annee_parcours", + sa.Column("numero", sa.INTEGER(), autoincrement=False, nullable=True), + ) + op.drop_column("apc_annee_parcours", "ordre") + op.create_table( + "app_crit", + sa.Column( + "id", + sa.INTEGER(), + server_default=sa.text("nextval('app_crit_id_seq'::regclass)"), + autoincrement=True, + nullable=False, + ), + sa.Column("code", sa.TEXT(), autoincrement=False, nullable=False), + sa.Column("titre", sa.TEXT(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint("id", name="app_crit_pkey"), + postgresql_ignore_search_path=False, + ) + op.create_table( + "modules_acs", + sa.Column("module_id", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column("ac_id", sa.INTEGER(), autoincrement=False, nullable=True), + sa.ForeignKeyConstraint( + ["ac_id"], ["app_crit.id"], name="modules_acs_ac_id_fkey" + ), + sa.ForeignKeyConstraint( + ["module_id"], ["notes_modules.id"], name="modules_acs_module_id_fkey" + ), ) # ### end Alembic commands ### diff --git a/migrations/versions/a217bf588f4c_scodoc_9_0_13_essai_cascade.py b/migrations/versions/a217bf588f4c_scodoc_9_0_13_essai_cascade.py index 3e0c53b5..40c45ddf 100644 --- a/migrations/versions/a217bf588f4c_scodoc_9_0_13_essai_cascade.py +++ b/migrations/versions/a217bf588f4c_scodoc_9_0_13_essai_cascade.py @@ -10,27 +10,50 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'a217bf588f4c' -down_revision = 'f73251d1d825' +revision = "a217bf588f4c" +down_revision = "f73251d1d825" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('notes_semset_formsemestre', 'semset_id', - existing_type=sa.INTEGER(), - nullable=False) - op.drop_constraint('notes_semset_formsemestre_semset_id_fkey', 'notes_semset_formsemestre', type_='foreignkey') - op.create_foreign_key(None, 'notes_semset_formsemestre', 'notes_semset', ['semset_id'], ['id'], ondelete='CASCADE') + op.alter_column( + "notes_semset_formsemestre", + "semset_id", + existing_type=sa.INTEGER(), + nullable=False, + ) + op.drop_constraint( + "notes_semset_formsemestre_semset_id_fkey", + "notes_semset_formsemestre", + type_="foreignkey", + ) + op.create_foreign_key( + None, + "notes_semset_formsemestre", + "notes_semset", + ["semset_id"], + ["id"], + ondelete="CASCADE", + ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(None, 'notes_semset_formsemestre', type_='foreignkey') - op.create_foreign_key('notes_semset_formsemestre_semset_id_fkey', 'notes_semset_formsemestre', 'notes_semset', ['semset_id'], ['id']) - op.alter_column('notes_semset_formsemestre', 'semset_id', - existing_type=sa.INTEGER(), - nullable=True) + op.drop_constraint(None, "notes_semset_formsemestre", type_="foreignkey") + op.create_foreign_key( + "notes_semset_formsemestre_semset_id_fkey", + "notes_semset_formsemestre", + "notes_semset", + ["semset_id"], + ["id"], + ) + op.alter_column( + "notes_semset_formsemestre", + "semset_id", + existing_type=sa.INTEGER(), + nullable=True, + ) # ### end Alembic commands ### diff --git a/migrations/versions/c8efc54586d8_ue_semestre_idx.py b/migrations/versions/c8efc54586d8_ue_semestre_idx.py index 7eeb0e7e..180529b8 100644 --- a/migrations/versions/c8efc54586d8_ue_semestre_idx.py +++ b/migrations/versions/c8efc54586d8_ue_semestre_idx.py @@ -10,21 +10,23 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'c8efc54586d8' -down_revision = '6cfc21a7ae1b' +revision = "c8efc54586d8" +down_revision = "6cfc21a7ae1b" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.add_column('notes_ue', sa.Column('semestre_idx', sa.Integer(), nullable=True)) - op.create_index(op.f('ix_notes_ue_semestre_idx'), 'notes_ue', ['semestre_idx'], unique=False) + op.add_column("notes_ue", sa.Column("semestre_idx", sa.Integer(), nullable=True)) + op.create_index( + op.f("ix_notes_ue_semestre_idx"), "notes_ue", ["semestre_idx"], unique=False + ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_index(op.f('ix_notes_ue_semestre_idx'), table_name='notes_ue') - op.drop_column('notes_ue', 'semestre_idx') + op.drop_index(op.f("ix_notes_ue_semestre_idx"), table_name="notes_ue") + op.drop_column("notes_ue", "semestre_idx") # ### end Alembic commands ### diff --git a/migrations/versions/d3d92b2d0092_scodoc_9_0_5_ajout_dept_id_sur_.py b/migrations/versions/d3d92b2d0092_scodoc_9_0_5_ajout_dept_id_sur_.py index 8ab88cac..13623afa 100644 --- a/migrations/versions/d3d92b2d0092_scodoc_9_0_5_ajout_dept_id_sur_.py +++ b/migrations/versions/d3d92b2d0092_scodoc_9_0_5_ajout_dept_id_sur_.py @@ -10,23 +10,25 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'd3d92b2d0092' -down_revision = '017e32eb4773' +revision = "d3d92b2d0092" +down_revision = "017e32eb4773" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.add_column('itemsuivi_tags', sa.Column('dept_id', sa.Integer(), nullable=True)) - op.create_index(op.f('ix_itemsuivi_tags_dept_id'), 'itemsuivi_tags', ['dept_id'], unique=False) - op.create_foreign_key(None, 'itemsuivi_tags', 'departement', ['dept_id'], ['id']) + op.add_column("itemsuivi_tags", sa.Column("dept_id", sa.Integer(), nullable=True)) + op.create_index( + op.f("ix_itemsuivi_tags_dept_id"), "itemsuivi_tags", ["dept_id"], unique=False + ) + op.create_foreign_key(None, "itemsuivi_tags", "departement", ["dept_id"], ["id"]) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(None, 'itemsuivi_tags', type_='foreignkey') - op.drop_index(op.f('ix_itemsuivi_tags_dept_id'), table_name='itemsuivi_tags') - op.drop_column('itemsuivi_tags', 'dept_id') + op.drop_constraint(None, "itemsuivi_tags", type_="foreignkey") + op.drop_index(op.f("ix_itemsuivi_tags_dept_id"), table_name="itemsuivi_tags") + op.drop_column("itemsuivi_tags", "dept_id") # ### end Alembic commands ### diff --git a/migrations/versions/f6e7d2e01be1_augmente_taille_codes_apogee.py b/migrations/versions/f6e7d2e01be1_augmente_taille_codes_apogee.py index d1b2a9e3..3740714e 100644 --- a/migrations/versions/f6e7d2e01be1_augmente_taille_codes_apogee.py +++ b/migrations/versions/f6e7d2e01be1_augmente_taille_codes_apogee.py @@ -10,49 +10,73 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'f6e7d2e01be1' -down_revision = 'd3d92b2d0092' +revision = "f6e7d2e01be1" +down_revision = "d3d92b2d0092" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('notes_formsemestre_etapes', 'etape_apo', - existing_type=sa.VARCHAR(length=16), - type_=sa.String(length=24), - existing_nullable=True) - op.alter_column('notes_formsemestre_inscription', 'etape', - existing_type=sa.VARCHAR(length=16), - type_=sa.String(length=24), - existing_nullable=True) - op.alter_column('notes_modules', 'code_apogee', - existing_type=sa.VARCHAR(length=16), - type_=sa.String(length=24), - existing_nullable=True) - op.alter_column('notes_ue', 'code_apogee', - existing_type=sa.VARCHAR(length=16), - type_=sa.String(length=24), - existing_nullable=True) + op.alter_column( + "notes_formsemestre_etapes", + "etape_apo", + existing_type=sa.VARCHAR(length=16), + type_=sa.String(length=24), + existing_nullable=True, + ) + op.alter_column( + "notes_formsemestre_inscription", + "etape", + existing_type=sa.VARCHAR(length=16), + type_=sa.String(length=24), + existing_nullable=True, + ) + op.alter_column( + "notes_modules", + "code_apogee", + existing_type=sa.VARCHAR(length=16), + type_=sa.String(length=24), + existing_nullable=True, + ) + op.alter_column( + "notes_ue", + "code_apogee", + existing_type=sa.VARCHAR(length=16), + type_=sa.String(length=24), + existing_nullable=True, + ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('notes_ue', 'code_apogee', - existing_type=sa.String(length=24), - type_=sa.VARCHAR(length=16), - existing_nullable=True) - op.alter_column('notes_modules', 'code_apogee', - existing_type=sa.String(length=24), - type_=sa.VARCHAR(length=16), - existing_nullable=True) - op.alter_column('notes_formsemestre_inscription', 'etape', - existing_type=sa.String(length=24), - type_=sa.VARCHAR(length=16), - existing_nullable=True) - op.alter_column('notes_formsemestre_etapes', 'etape_apo', - existing_type=sa.String(length=24), - type_=sa.VARCHAR(length=16), - existing_nullable=True) + op.alter_column( + "notes_ue", + "code_apogee", + existing_type=sa.String(length=24), + type_=sa.VARCHAR(length=16), + existing_nullable=True, + ) + op.alter_column( + "notes_modules", + "code_apogee", + existing_type=sa.String(length=24), + type_=sa.VARCHAR(length=16), + existing_nullable=True, + ) + op.alter_column( + "notes_formsemestre_inscription", + "etape", + existing_type=sa.String(length=24), + type_=sa.VARCHAR(length=16), + existing_nullable=True, + ) + op.alter_column( + "notes_formsemestre_etapes", + "etape_apo", + existing_type=sa.String(length=24), + type_=sa.VARCHAR(length=16), + existing_nullable=True, + ) # ### end Alembic commands ### diff --git a/migrations/versions/f73251d1d825_table_configuration_site.py b/migrations/versions/f73251d1d825_table_configuration_site.py index e5973288..bbe2ceb0 100644 --- a/migrations/versions/f73251d1d825_table_configuration_site.py +++ b/migrations/versions/f73251d1d825_table_configuration_site.py @@ -10,26 +10,29 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'f73251d1d825' -down_revision = 'f6e7d2e01be1' +revision = "f73251d1d825" +down_revision = "f6e7d2e01be1" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.create_table('scodoc_site_config', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('name', sa.String(length=128), nullable=False), - sa.Column('value', sa.Text(), nullable=True), - sa.PrimaryKeyConstraint('id') + op.create_table( + "scodoc_site_config", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("name", sa.String(length=128), nullable=False), + sa.Column("value", sa.Text(), nullable=True), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index( + op.f("ix_scodoc_site_config_name"), "scodoc_site_config", ["name"], unique=False ) - op.create_index(op.f('ix_scodoc_site_config_name'), 'scodoc_site_config', ['name'], unique=False) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_index(op.f('ix_scodoc_site_config_name'), table_name='scodoc_site_config') - op.drop_table('scodoc_site_config') + op.drop_index(op.f("ix_scodoc_site_config_name"), table_name="scodoc_site_config") + op.drop_table("scodoc_site_config") # ### end Alembic commands ### diff --git a/migrations/versions/f86c013c9fbd_modif_contrainte_sur_formations.py b/migrations/versions/f86c013c9fbd_modif_contrainte_sur_formations.py index 8d888c68..d8e1abfc 100644 --- a/migrations/versions/f86c013c9fbd_modif_contrainte_sur_formations.py +++ b/migrations/versions/f86c013c9fbd_modif_contrainte_sur_formations.py @@ -10,21 +10,31 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'f86c013c9fbd' -down_revision = '669065fb2d20' +revision = "f86c013c9fbd" +down_revision = "669065fb2d20" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('notes_formations_acronyme_titre_version_key', 'notes_formations', type_='unique') - op.create_unique_constraint(None, 'notes_formations', ['dept_id', 'acronyme', 'titre', 'version']) + op.drop_constraint( + "notes_formations_acronyme_titre_version_key", + "notes_formations", + type_="unique", + ) + op.create_unique_constraint( + None, "notes_formations", ["dept_id", "acronyme", "titre", "version"] + ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(None, 'notes_formations', type_='unique') - op.create_unique_constraint('notes_formations_acronyme_titre_version_key', 'notes_formations', ['acronyme', 'titre', 'version']) + op.drop_constraint(None, "notes_formations", type_="unique") + op.create_unique_constraint( + "notes_formations_acronyme_titre_version_key", + "notes_formations", + ["acronyme", "titre", "version"], + ) # ### end Alembic commands ### diff --git a/misc/SuppressAccents.py b/misc/SuppressAccents.py index 3b119e59..b5ad591b 100644 --- a/misc/SuppressAccents.py +++ b/misc/SuppressAccents.py @@ -7,65 +7,84 @@ Source: http://wikipython.flibuste.net/moin.py/JouerAvecUnicode#head-1213938516c """ _reptable = {} + + def _fill_reptable(): _corresp = [ - (u"A", [0x00C0,0x00C1,0x00C2,0x00C3,0x00C4,0x00C5,0x0100,0x0102,0x0104]), - (u"AE", [0x00C6]), - (u"a", [0x00E0,0x00E1,0x00E2,0x00E3,0x00E4,0x00E5,0x0101,0x0103,0x0105]), - (u"ae", [0x00E6]), - (u"C", [0x00C7,0x0106,0x0108,0x010A,0x010C]), - (u"c", [0x00E7,0x0107,0x0109,0x010B,0x010D]), - (u"D", [0x00D0,0x010E,0x0110]), - (u"d", [0x00F0,0x010F,0x0111]), - (u"E", [0x00C8,0x00C9,0x00CA,0x00CB,0x0112,0x0114,0x0116,0x0118,0x011A]), - (u"e", [0x00E8,0x00E9,0x00EA,0x00EB,0x0113,0x0115,0x0117,0x0119,0x011B]), - (u"G", [0x011C,0x011E,0x0120,0x0122]), - (u"g", [0x011D,0x011F,0x0121,0x0123]), - (u"H", [0x0124,0x0126]), - (u"h", [0x0125,0x0127]), - (u"I", [0x00CC,0x00CD,0x00CE,0x00CF,0x0128,0x012A,0x012C,0x012E,0x0130]), - (u"i", [0x00EC,0x00ED,0x00EE,0x00EF,0x0129,0x012B,0x012D,0x012F,0x0131]), - (u"IJ", [0x0132]), - (u"ij", [0x0133]), - (u"J", [0x0134]), - (u"j", [0x0135]), - (u"K", [0x0136]), - (u"k", [0x0137,0x0138]), - (u"L", [0x0139,0x013B,0x013D,0x013F,0x0141]), - (u"l", [0x013A,0x013C,0x013E,0x0140,0x0142]), - (u"N", [0x00D1,0x0143,0x0145,0x0147,0x014A]), - (u"n", [0x00F1,0x0144,0x0146,0x0148,0x0149,0x014B]), - (u"O", [0x00D2,0x00D3,0x00D4,0x00D5,0x00D6,0x00D8,0x014C,0x014E,0x0150]), - (u"o", [0x00F2,0x00F3,0x00F4,0x00F5,0x00F6,0x00F8,0x014D,0x014F,0x0151]), - (u"OE", [0x0152]), - (u"oe", [0x0153]), - (u"R", [0x0154,0x0156,0x0158]), - (u"r", [0x0155,0x0157,0x0159]), - (u"S", [0x015A,0x015C,0x015E,0x0160]), - (u"s", [0x015B,0x015D,0x015F,0x01610,0x017F]), - (u"T", [0x0162,0x0164,0x0166]), - (u"t", [0x0163,0x0165,0x0167]), - (u"U", [0x00D9,0x00DA,0x00DB,0x00DC,0x0168,0x016A,0x016C,0x016E,0x0170,0x172]), - (u"u", [0x00F9,0x00FA,0x00FB,0x00FC,0x0169,0x016B,0x016D,0x016F,0x0171]), - (u"W", [0x0174]), - (u"w", [0x0175]), - (u"Y", [0x00DD,0x0176,0x0178]), - (u"y", [0x00FD,0x00FF,0x0177]), - (u"Z", [0x0179,0x017B,0x017D]), - (u"z", [0x017A,0x017C,0x017E]), - (u"2", [0x00B2]), # deux exposant - (u" ", [0x00A0]), #   - (u"", [0xB0]), # degre - (u"", [0xA9]), # copyright - (u"1/2", [0xBD]), # 1/2 - ] + ("A", [0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x0100, 0x0102, 0x0104]), + ("AE", [0x00C6]), + ("a", [0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x0101, 0x0103, 0x0105]), + ("ae", [0x00E6]), + ("C", [0x00C7, 0x0106, 0x0108, 0x010A, 0x010C]), + ("c", [0x00E7, 0x0107, 0x0109, 0x010B, 0x010D]), + ("D", [0x00D0, 0x010E, 0x0110]), + ("d", [0x00F0, 0x010F, 0x0111]), + ("E", [0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0112, 0x0114, 0x0116, 0x0118, 0x011A]), + ("e", [0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0113, 0x0115, 0x0117, 0x0119, 0x011B]), + ("G", [0x011C, 0x011E, 0x0120, 0x0122]), + ("g", [0x011D, 0x011F, 0x0121, 0x0123]), + ("H", [0x0124, 0x0126]), + ("h", [0x0125, 0x0127]), + ("I", [0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x0128, 0x012A, 0x012C, 0x012E, 0x0130]), + ("i", [0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x0129, 0x012B, 0x012D, 0x012F, 0x0131]), + ("IJ", [0x0132]), + ("ij", [0x0133]), + ("J", [0x0134]), + ("j", [0x0135]), + ("K", [0x0136]), + ("k", [0x0137, 0x0138]), + ("L", [0x0139, 0x013B, 0x013D, 0x013F, 0x0141]), + ("l", [0x013A, 0x013C, 0x013E, 0x0140, 0x0142]), + ("N", [0x00D1, 0x0143, 0x0145, 0x0147, 0x014A]), + ("n", [0x00F1, 0x0144, 0x0146, 0x0148, 0x0149, 0x014B]), + ("O", [0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D8, 0x014C, 0x014E, 0x0150]), + ("o", [0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F8, 0x014D, 0x014F, 0x0151]), + ("OE", [0x0152]), + ("oe", [0x0153]), + ("R", [0x0154, 0x0156, 0x0158]), + ("r", [0x0155, 0x0157, 0x0159]), + ("S", [0x015A, 0x015C, 0x015E, 0x0160]), + ("s", [0x015B, 0x015D, 0x015F, 0x01610, 0x017F]), + ("T", [0x0162, 0x0164, 0x0166]), + ("t", [0x0163, 0x0165, 0x0167]), + ( + "U", + [ + 0x00D9, + 0x00DA, + 0x00DB, + 0x00DC, + 0x0168, + 0x016A, + 0x016C, + 0x016E, + 0x0170, + 0x172, + ], + ), + ("u", [0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0169, 0x016B, 0x016D, 0x016F, 0x0171]), + ("W", [0x0174]), + ("w", [0x0175]), + ("Y", [0x00DD, 0x0176, 0x0178]), + ("y", [0x00FD, 0x00FF, 0x0177]), + ("Z", [0x0179, 0x017B, 0x017D]), + ("z", [0x017A, 0x017C, 0x017E]), + ("2", [0x00B2]), # deux exposant + (" ", [0x00A0]), #   + ("", [0xB0]), # degre + ("", [0xA9]), # copyright + ("1/2", [0xBD]), # 1/2 + ] global _reptable - for repchar,codes in _corresp : - for code in codes : + for repchar, codes in _corresp: + for code in codes: _reptable[code] = repchar + _fill_reptable() -def suppression_diacritics(s) : + + +def suppression_diacritics(s): """Suppression des accents et autres marques. @param s: le texte à nettoyer. @@ -73,6 +92,6 @@ def suppression_diacritics(s) : @return: le texte nettoyé de ses marques diacritiques. @rtype: unicode """ - if isinstance(s,str) : - s = unicode(s,"utf8","replace") + if isinstance(s, str): + s = unicode(s, "utf8", "replace") return s.translate(_reptable) diff --git a/tests/api/test_api_departements.py b/tests/api/test_api_departements.py index bb7dec66..bc0b5c4a 100644 --- a/tests/api/test_api_departements.py +++ b/tests/api/test_api_departements.py @@ -20,112 +20,100 @@ Utilisation : import requests from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers -from tests.api.tools_test_api import verify_fields +from tests.api.tools_test_api import verify_fields, DEPARTEMENT_FIELDS def test_departements(api_headers): + """ " + Routes: /departements_ids, /departement, /departement//formsemestres_ids + """ - Test 'departements' - - Route : - - /departements - """ - - fields = [ - "id", - "acronym", - "description", - "visible", - "date_creation", - ] - + # --- Liste des ids r = requests.get( - API_URL + "/departements", + API_URL + "/departements_ids", headers=api_headers, verify=CHECK_CERTIFICATE, ) assert r.status_code == 200 - assert len(r.json()) == 1 + departements_ids = r.json() + assert isinstance(departements_ids, list) + assert len(departements_ids) > 0 + assert all(isinstance(x, int) for x in departements_ids) - dept = r.json()[0] + dept_id = departements_ids[0] + # --- Infos sur un département, accès par id + r = requests.get( + f"{API_URL}/departement/{dept_id}", + headers=api_headers, + verify=CHECK_CERTIFICATE, + ) + assert r.status_code == 200 + dept_a = r.json() + assert verify_fields(dept_a, DEPARTEMENT_FIELDS) is True + # --- Infos sur un département, accès par acronyme4 + r = requests.get( + f"{API_URL}/departement/{dept_a['acronym']}", + headers=api_headers, + verify=CHECK_CERTIFICATE, + ) + assert r.status_code == 200 + dept_b = r.json() + assert dept_a == dept_b - fields_OK = verify_fields(dept, fields) - - assert fields_OK is True + # Liste des formsemestres + r = requests.get( + f"{API_URL}/departement/{dept_a['acronym']}/formsemestres_ids", + headers=api_headers, + verify=CHECK_CERTIFICATE, + ) + assert r.status_code == 200 + dept_ids = r.json() + assert isinstance(dept_ids, list) + assert all(isinstance(x, int) for x in dept_ids) + assert len(dept_ids) > 0 + assert dept_id in dept_ids def test_list_etudiants(api_headers): - """ - Test 'list_etudiants' - - Routes : - - /departements//etudiants/list - - /departements//etudiants/list/ - """ - fields = { - "civilite", - "code_ine", - "code_nip", - "date_naissance", - "email", - "emailperso", - "etudid", - "nom", - "prenom", - "nomprenom", - "lieu_naissance", - "dept_naissance", - "nationalite", - "boursier", - "id", - "codepostaldomicile", - "paysdomicile", - "telephonemobile", - "typeadresse", - "domicile", - "villedomicile", - "telephone", - "fax", - "description", - } + fields = {"id", "nip", "ine", "nom", "nom_usuel", "prenom", "civilite"} r = requests.get( - API_URL + "/departements/TAPI/etudiants/list", + API_URL + "/departement/TAPI/etudiants", headers=api_headers, verify=CHECK_CERTIFICATE, ) - - etu = r.json()[0] - - fields_OK = verify_fields(etu, fields) - assert r.status_code == 200 - assert len(r.json()) == 16 - assert fields_OK is True + etud = r.json()[0] + assert verify_fields(etud, fields) is True + assert isinstance(etud["id"], int) - r = requests.get( - API_URL + "/departements/TAPI/etudiants/list/1", - headers=api_headers, - verify=CHECK_CERTIFICATE, - ) - - etu = r.json()[0] - - fields_OK = verify_fields(etu, fields) - - assert r.status_code == 200 - assert len(r.json()) == 16 - assert fields_OK is True + # Vérification que chaque id, nip et ine sont uniques (EN CHANTIER) + # all_uniques = True + # d = dict() + # i = 0 + # + # for etu in r.json(): + # d[i] = [etu["id"], etu["nip"], etu["ine"]] + # i += 1 + # + # d[4][2] = 65 + # + # for i in range(len(d)-1): + # if d[i][0] == d[i+1][0]: + # all_uniques = False + # else: + # if d[i][1] == d[i+1][1]: + # all_uniques = False + # else: + # if d[i][2] == d[i+1][2]: + # all_uniques = False + # i += 1 + # + # assert all_uniques is True # liste_semestres_courant def test_semestres_courant(api_headers): - """ - Test 'liste_semestres_courant' - - Route : - - /departements//semestres_courants - """ fields = [ "titre", "gestion_semestrielle", @@ -149,22 +137,39 @@ def test_semestres_courant(api_headers): "block_moyennes", "formsemestre_id", "titre_num", + "titre_formation", "date_debut_iso", "date_fin_iso", "responsables", - "titre_court", ] - + dept_id = 1 r = requests.get( - API_URL + "/departements/TAPI/semestres_courants", + f"{API_URL}/departement/{dept_id}", headers=api_headers, verify=CHECK_CERTIFICATE, ) - - sem = r.json()[0] - - fields_OK = verify_fields(sem, fields) - assert r.status_code == 200 - assert len(r.json()) == 1 - assert fields_OK is True + dept = r.json() + assert dept["id"] == dept_id + # Accès via acronyme + r = requests.get( + f"{API_URL}/departement/{dept['acronym']}/formsemestres_courants", + headers=api_headers, + verify=CHECK_CERTIFICATE, + ) + assert r.status_code == 200 + result_a = r.json() + assert isinstance(result_a, list) # liste de formsemestres + assert len(result_a) > 0 + sem = result_a[0] + assert verify_fields(sem, fields) is True + + # accès via dept_id + r = requests.get( + f"{API_URL}/departement/{dept['id']}/formsemestres_courants", + headers=api_headers, + verify=CHECK_CERTIFICATE, + ) + assert r.status_code == 200 + result_b = r.json() + assert result_a == result_b diff --git a/tests/api/test_api_etudiants.py b/tests/api/test_api_etudiants.py index db53475d..b641c757 100644 --- a/tests/api/test_api_etudiants.py +++ b/tests/api/test_api_etudiants.py @@ -16,30 +16,19 @@ Utilisation : Lancer : pytest tests/api/test_api_etudiants.py """ -from random import randint import requests from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers from tests.api.tools_test_api import verify_fields +from tests.api.tools_test_api import ETUD_FIELDS, FSEM_FIELDS + -# etudiants_courant def test_etudiants_courant(api_headers): """ - Test 'etudiants_courant' - - Routes : - - /etudiants/courant - - /etudiants/courant/long + Route: /etudiants/courant """ - fields = [ - "id", - "nip", - "nom", - "nom_usuel", - "prenom", - "civilite", - ] + fields = {"id", "nip", "nom", "prenom", "civilite"} r = requests.get( API_URL + "/etudiants/courant", @@ -48,43 +37,26 @@ def test_etudiants_courant(api_headers): ) assert r.status_code == 200 etudiants = r.json() - assert len(etudiants) == 16 # XXX HARDCODED + assert len(etudiants) > 0 etud = etudiants[-1] + assert verify_fields(etud, fields) is True + assert isinstance(etud["id"], int) + assert isinstance(etud["nip"], str) + assert isinstance(etud["nom"], str) + assert isinstance(etud["prenom"], str) + assert isinstance(etud["civilite"], str) - fields_ok = verify_fields(etud, fields) + all_unique = True + list_ids = [etu["id"] for etu in etudiants] - assert fields_ok is True + for i in range(len(etudiants) - 1): + if etudiants.count(list_ids[i]) > 1: + all_unique = False + + assert all_unique is True ########## Version long ################ - - fields_long = [ - "civilite", - "code_ine", - "code_nip", - "date_naissance", - "email", - "emailperso", - "etudid", - "nom", - "prenom", - "nomprenom", - "lieu_naissance", - "dept_naissance", - "nationalite", - "boursier", - "id", - "codepostaldomicile", - "paysdomicile", - "telephonemobile", - "typeadresse", - "domicile", - "villedomicile", - "telephone", - "fax", - "description", - ] - r = requests.get( API_URL + "/etudiants/courant/long", headers=api_headers, @@ -95,46 +67,13 @@ def test_etudiants_courant(api_headers): assert len(etudiants) == 16 # HARDCODED etud = etudiants[-1] - fields_ok = verify_fields(etud, fields_long) - - assert fields_ok is True + assert verify_fields(etud, ETUD_FIELDS) is True def test_etudiant(api_headers): """ - Test 'etudiant' - - Routes : - - /etudiant/etudid/ - - /etudiant/nip/ - - /etudiant/ine/ + Route: """ - fields = [ - "civilite", - "code_ine", - "code_nip", - "date_naissance", - "email", - "emailperso", - "etudid", - "nom", - "prenom", - "nomprenom", - "lieu_naissance", - "dept_naissance", - "nationalite", - "boursier", - "id", - "domicile", - "villedomicile", - "telephone", - "fax", - "description", - "codepostaldomicile", - "paysdomicile", - "telephonemobile", - "typeadresse", - ] ######### Test etudid ######### r = requests.get( @@ -144,10 +83,8 @@ def test_etudiant(api_headers): ) assert r.status_code == 200 etud = r.json() - assert len(etud) == 24 # ? HARDCODED - fields_ok = verify_fields(etud, fields) - assert fields_ok is True + assert verify_fields(etud, ETUD_FIELDS) is True ######### Test code nip ######### @@ -158,8 +95,7 @@ def test_etudiant(api_headers): ) assert r.status_code == 200 etud = r.json() - assert len(etud) == 24 - fields_ok = verify_fields(etud, fields) + fields_ok = verify_fields(etud, ETUD_FIELDS) assert fields_ok is True ######### Test code ine ######### @@ -172,47 +108,14 @@ def test_etudiant(api_headers): assert r.status_code == 200 etud = r.json() assert len(etud) == 24 - fields_ok = verify_fields(etud, fields) + fields_ok = verify_fields(etud, ETUD_FIELDS) assert fields_ok is True def test_etudiant_formsemestres(api_headers): """ - Test 'etudiant_formsemestres' - - Routes : - - /etudiant/etudid//formsemestres - - /etudiant/nip//formsemestres - - /etudiant/ine//formsemestres + Route: /etudiant/etudid//formsemestres """ - fields = [ - "date_fin", - "resp_can_edit", - "dept_id", - "etat", - "resp_can_change_ens", - "id", - "modalite", - "ens_can_edit_eval", - "formation_id", - "gestion_compensation", - "elt_sem_apo", - "semestre_id", - "bul_hide_xml", - "elt_annee_apo", - "titre", - "block_moyennes", - "scodoc7_id", - "date_debut", - "gestion_semestrielle", - "bul_bgcolor", - "formsemestre_id", - "titre_num", - "date_debut_iso", - "date_fin_iso", - "responsables", - "titre_court", - ] ######### Test etudid ######### @@ -226,9 +129,7 @@ def test_etudiant_formsemestres(api_headers): assert len(formsemestres) == 1 formsemestre = formsemestres[0] - - fields_ok = verify_fields(formsemestre, fields) - assert fields_ok is True + assert verify_fields(formsemestre, FSEM_FIELDS) is True ######### Test code nip ######### r = requests.get( @@ -241,9 +142,7 @@ def test_etudiant_formsemestres(api_headers): assert len(formsemestres) == 1 formsemestre = formsemestres[0] - - fields_ok = verify_fields(formsemestre, fields) - assert fields_ok is True + assert verify_fields(formsemestre, FSEM_FIELDS) is True ######### Test code ine ######### r = requests.get( @@ -256,29 +155,13 @@ def test_etudiant_formsemestres(api_headers): assert len(formsemestres) == 1 formsemestre = formsemestres[0] - - fields_ok = verify_fields(formsemestre, fields) - assert fields_ok is True + assert verify_fields(formsemestre, FSEM_FIELDS) is True def test_etudiant_bulletin_semestre(api_headers): """ - Test 'etudiant_bulletin_semestre' - - Routes : - - /etudiant/etudid//formsemestre//bulletin - - /etudiant/nip//formsemestre//bulletin - - /etudiant/ine//formsemestre//bulletin - - /etudiant/etudid//formsemestre//bulletin/pdf - - /etudiant/nip//formsemestre//bulletin/pdf - - /etudiant/ine//formsemestre//bulletin/pdf - - /etudiant/etudid//formsemestre//bulletin/short - - /etudiant/nip//formsemestre//bulletin/short - - /etudiant/ine//formsemestre//bulletin/short - - /etudiant/etudid//formsemestre//bulletin/short/pdf - - /etudiant/nip//formsemestre//bulletin/short/pdf - - /etudiant/ine//formsemestre//bulletin/short/pdf - """ + Route: /etudiant/etudid//formsemestre//bulletin + """ ######### Test etudid ######### r = requests.get( @@ -311,15 +194,19 @@ def test_etudiant_bulletin_semestre(api_headers): bul = r.json() assert len(bul) == 13 # HARDCODED + ### --- Test étudiant inexistant + r = requests.get( + API_URL + "/etudiant/ine/189919919119191/formsemestre/1/bulletin", + headers=api_headers, + verify=CHECK_CERTIFICATE, + ) + assert r.status_code == 404 + def test_etudiant_groups(api_headers): """ - Test 'etudiant_groups' - - Routes : - - /etudiant/etudid//formsemestre//groups - - /etudiant/nip//formsemestre//groups - - /etudiant/ine//formsemestre//groups + Route: + /etudiant/etudid//formsemestre//groups """ fields = [ "partition_id", diff --git a/tests/api/test_api_formations.py b/tests/api/test_api_formations.py index 3f65ac6e..b61037da 100644 --- a/tests/api/test_api_formations.py +++ b/tests/api/test_api_formations.py @@ -21,15 +21,12 @@ import requests from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers from tests.api.tools_test_api import verify_fields +from tests.api.tools_test_api import FORMATION_FIELDS, MODIMPL_FIELDS -# formations def test_formations_ids(api_headers): """ - Test 'formations_ids' - - Routes : - - /formations_ids + Route: /formations_ids """ r = requests.get( API_URL + "/formations_ids", @@ -44,150 +41,67 @@ def test_formations_ids(api_headers): assert all(isinstance(x, int) for x in formations_ids) -# formations_by_id def test_formations_by_id(api_headers): """ - Test 'formations_by_id' - - Routes : - - /formations/ + Route: /formation/ """ - fields = [ - "id", - "acronyme", - "titre_officiel", - "formation_code", - "code_specialite", - "dept_id", - "titre", - "version", - "type_parcours", - "referentiel_competence_id", - "formation_id", - ] - r = requests.get( - API_URL + "/formations/1", + API_URL + "/formation/1", headers=api_headers, verify=CHECK_CERTIFICATE, ) assert r.status_code == 200 formation = r.json() - - fields_ok = verify_fields(formation, fields) - assert fields_ok is True + assert verify_fields(formation, FORMATION_FIELDS) is True # TODO tester le contenu de certains champs def test_formation_export(api_headers): """ - Test 'formation_export_by_formation_id' - - Routes : - - /formations/formation_export/ - - /formations/formation_export//with_ids + Route: /formation/formation_export/ """ - fields = [ - "id", - "acronyme", - "titre_officiel", - "formation_code", - "code_specialite", - "dept_id", - "titre", - "version", - "type_parcours", - "referentiel_competence_id", - "formation_id", - "ue", - ] r = requests.get( - API_URL + "/formations/formation_export/1", + API_URL + "/formation/formation_export/1", headers=api_headers, verify=CHECK_CERTIFICATE, ) assert r.status_code == 200 - export_formation = r.json() - - fields_ok = verify_fields(export_formation, fields) - assert fields_ok is True + assert verify_fields(export_formation, FORMATION_FIELDS) is True # TODO tester le contenu de certains champs +# TODO +# def test_formsemestre_apo(api_headers): +# r = requests.get( +# API_URL + "/formation/apo/", +# headers=api_headers, +# verify=CHECK_CERTIFICATE, +# ) +# assert r.status_code == 200 + + def test_moduleimpl(api_headers): """ - Test 'moduleimpl' - - Route : - - /formations/moduleimpl/ + Route: /formation/moduleimpl/ """ - fields = [ - "id", - "formsemestre_id", - "computation_expr", - "module_id", - "responsable_id", - "moduleimpl_id", - "ens", - "module", - ] - r = requests.get( - API_URL + "/formations/moduleimpl/1", + API_URL + "/formation/moduleimpl/1", headers=api_headers, verify=CHECK_CERTIFICATE, ) assert r.status_code == 200 moduleimpl = r.json() - - fields_ok = verify_fields(moduleimpl, fields) - assert fields_ok is True + assert verify_fields(moduleimpl, MODIMPL_FIELDS) is True # TODO tester le contenu de certains champs -def test_moduleimpls_sem(api_headers): - """ - Test 'moduleimpls_sem' - - Route : - - /formations/moduleimpl/formsemestre//list - """ - fields = [ - "id", - "formsemestre_id", - "computation_expr", - "module_id", - "responsable_id", - "moduleimpl_id", - "ens", - "module", - "moduleimpl_id", - "ens", - ] - r = requests.get( - API_URL + "/formations/moduleimpl/formsemestre/1/list", - headers=api_headers, - verify=CHECK_CERTIFICATE, - ) - assert r.status_code == 200 - moduleimpls = r.json() - moduleimpl = moduleimpls[0] - - fields_ok = verify_fields(moduleimpl, fields) - assert len(moduleimpls) == 21 # XXX HARDCODED ! - assert fields_ok is True - - def test_referentiel_competences(api_headers): """ - Test 'referentiel_competences' - - Route : - - /formations//referentiel_competences + Route: "/formation//referentiel_competences", """ r = requests.get( - API_URL + "/formations/1/referentiel_competences", + API_URL + "/formation/1/referentiel_competences", headers=api_headers, verify=CHECK_CERTIFICATE, ) diff --git a/tests/api/test_api_formsemestre.py b/tests/api/test_api_formsemestre.py index 568e7e8d..57d79772 100644 --- a/tests/api/test_api_formsemestre.py +++ b/tests/api/test_api_formsemestre.py @@ -18,17 +18,16 @@ Utilisation : """ import requests +from app.api.formsemestres import formsemestre from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers -from tests.api.tools_test_api import verify_fields +from tests.api.tools_test_api import MODIMPL_FIELDS, verify_fields +from tests.api.tools_test_api import FSEM_FIELDS, UE_FIELDS, MODULE_FIELDS def test_formsemestre(api_headers): """ - Test 'formsemestre' - - Route : - - /formsemestre/ + Route: /formsemestre/ """ r = requests.get( API_URL + "/formsemestre/1", @@ -36,64 +35,48 @@ def test_formsemestre(api_headers): verify=CHECK_CERTIFICATE, ) assert r.status_code == 200 - formsemestre = r.json() - - fields = [ - "date_fin", - "resp_can_edit", - "dept_id", - "etat", - "resp_can_change_ens", - "id", - "modalite", - "ens_can_edit_eval", - "formation_id", - "gestion_compensation", - "elt_sem_apo", - "semestre_id", - "bul_hide_xml", - "elt_annee_apo", - "titre", - "block_moyennes", - "scodoc7_id", - "date_debut", - "gestion_semestrielle", - "bul_bgcolor", - "formsemestre_id", - "titre_num", - "date_debut_iso", - "date_fin_iso", - "responsables", - ] - - fields_ok = verify_fields(formsemestre, fields) - - assert fields_ok is True + assert verify_fields(formsemestre, FSEM_FIELDS) -# TODO -# def test_formsemestre_apo(api_headers): -# """ -# Test 'formsemestre_apo' -# -# Route : -# - /formsemestre/apo/ -# """ -# r = requests.get( -# API_URL + "/formations/apo/", -# headers=api_headers, -# verify=CHECK_CERTIFICATE, -# ) -# assert r.status_code == 200 +def test_etudiant_bulletin(api_headers): + """ + Route: + """ + formsemestre_id = 1 + r = requests.get( + f"{API_URL}/etudiant/etudid/1/formsemestre/{formsemestre_id}/bulletin", + headers=api_headers, + verify=CHECK_CERTIFICATE, + ) + assert r.status_code == 200 + bull_a = r.json() + + r = requests.get( + f"{API_URL}/etudiant/nip/1/formsemestre/{formsemestre_id}/bulletin", + headers=api_headers, + verify=CHECK_CERTIFICATE, + ) + assert r.status_code == 200 + bull_b = r.json() + + r = requests.get( + f"{API_URL}/etudiant/ine/1/formsemestre/{formsemestre_id}/bulletin", + headers=api_headers, + verify=CHECK_CERTIFICATE, + ) + assert r.status_code == 200 + bull_c = r.json() + # elimine les dates de publication pour comparer les autres champs + del bull_a["date"] + del bull_b["date"] + del bull_c["date"] + assert bull_a == bull_b == bull_c def test_bulletins(api_headers): """ - Test 'bulletins' - - Route : - - /formsemestre//bulletins + Route: """ r = requests.get( API_URL + "/formsemestre/1/bulletins", @@ -105,12 +88,6 @@ def test_bulletins(api_headers): # # jury # def test_jury(): -# """ -# Test 'jury' -# -# Route : -# - /formsemestre//jury -# """ # r = requests.get( # API_URL + "/formsemestre/1/jury", # headers=api_headers, @@ -118,70 +95,11 @@ def test_bulletins(api_headers): # ) # assert r.status_code == 200 -# TODO A revoir -def test_programme(api_headers): + +def test_formsemestre_programme(api_headers): """ - Test 'programme' - - Route : - - /formsemestre//programme + Route: /formsemestre/1/programme """ - ue_fields = [ - "semestre_idx", - "type", - "formation_id", - "ue_code", - "id", - "ects", - "acronyme", - "is_external", - "numero", - "code_apogee", - "titre", - "coefficient", - "color", - "ue_id", - ] - - ressource_fields = [ - "heures_tp", - "code_apogee", - "titre", - "coefficient", - "module_type", - "id", - "ects", - "abbrev", - "ue_id", - "code", - "formation_id", - "heures_cours", - "matiere_id", - "heures_td", - "semestre_id", - "numero", - "module_id", - ] - - sae_fields = [ - "heures_tp", - "code_apogee", - "titre", - "coefficient", - "module_type", - "id", - "ects", - "abbrev", - "ue_id", - "code", - "formation_id", - "heures_cours", - "matiere_id", - "heures_td", - "semestre_id", - "numero", - "module_id", - ] r = requests.get( API_URL + "/formsemestre/1/programme", @@ -189,16 +107,22 @@ def test_programme(api_headers): verify=CHECK_CERTIFICATE, ) assert r.status_code == 200 - assert len(r.json()) == 3 + prog = r.json() + assert isinstance(prog, dict) + assert "ues" in prog + assert "modules" in prog + assert "ressources" in prog + assert "saes" in prog + assert isinstance(prog["ues"], list) + assert isinstance(prog["modules"], list) + ue = prog["ues"][0] + modules = prog["modules"] + # Il y a toujours au moins une SAE et une ressources dans notre base de test + ressource = prog["ressources"][0] + sae = prog["saes"][0] - ue = r.json()["ues"][0] - ressource = r.json()["ressources"][0] - sae = r.json()["saes"][0] - - fields_ue_OK = verify_fields(ue, ue_fields) - fields_ressource_OK = verify_fields(ressource, ressource_fields) - fields_sae_OK = verify_fields(sae, sae_fields) - - assert fields_ue_OK is True - assert fields_ressource_OK is True - assert fields_sae_OK is True + assert verify_fields(ue, UE_FIELDS) + if len(modules) > 1: + assert verify_fields(modules[0], MODIMPL_FIELDS) + assert verify_fields(ressource, MODIMPL_FIELDS) + assert verify_fields(sae, MODIMPL_FIELDS) diff --git a/tests/api/test_api_permissions.py b/tests/api/test_api_permissions.py index 4a9f2952..2ba8a23d 100644 --- a/tests/api/test_api_permissions.py +++ b/tests/api/test_api_permissions.py @@ -43,6 +43,8 @@ def test_permissions(api_headers): # "date_debut": # "date_fin": "dept": "TAPI", + "dept_ident": "TAPI", + "dept_id": 1, "etape_apo": "???", "etat": "I", "evaluation_id": 1, diff --git a/tests/api/tools_test_api.py b/tests/api/tools_test_api.py index d0a224c5..5aefb87b 100644 --- a/tests/api/tools_test_api.py +++ b/tests/api/tools_test_api.py @@ -2,16 +2,138 @@ """ -def verify_fields(json_response: dict, fields: set) -> bool: +def verify_fields(json_response: dict, expected_fields: set) -> bool: """ Vérifie si les champs attendu de la réponse json sont présents json_response : la réponse de la requête - fields : ensemble des champs à vérifier + expected_fields : ensemble des champs à vérifier Retourne True ou False """ - for field in json_response: - if field not in fields: - return False - return True + return all(field in json_response for field in expected_fields) + + +DEPARTEMENT_FIELDS = [ + "id", + "acronym", + "description", + "visible", + "date_creation", +] + +ETUD_FIELDS = { + "boursier", + "civilite", + "code_ine", + "code_nip", + "codepostaldomicile", + "date_naissance", + "dept_naissance", + "description", + "domicile", + "email", + "emailperso", + "etudid", + "id", + "lieu_naissance", + "nationalite", + "nom", + "nomprenom", + "paysdomicile", + "prenom", + "telephone", + "telephonemobile", + "typeadresse", + "villedomicile", +} + +FORMATION_FIELDS = { + "id", + "acronyme", + "titre_officiel", + "formation_code", + "code_specialite", + "dept_id", + "titre", + "version", + "type_parcours", + "referentiel_competence_id", + "formation_id", +} + +FSEM_FIELDS = { + "block_moyennes", + "bul_bgcolor", + "bul_hide_xml", + "date_debut_iso", + "date_debut", + "date_fin_iso", + "date_fin", + "dept_id", + "elt_annee_apo", + "elt_sem_apo", + "ens_can_edit_eval", + "etat", + "formation_id", + "formsemestre_id", + "gestion_compensation", + "gestion_semestrielle", + "id", + "modalite", + "resp_can_change_ens", + "resp_can_edit", + "responsables", + "semestre_id", + "titre_formation", + "titre_num", + "titre", +} + +MODIMPL_FIELDS = { + "id", + "formsemestre_id", + "computation_expr", + "module_id", + "responsable_id", + "moduleimpl_id", + "ens", + "module", +} + +MODULE_FIELDS = { + "heures_tp", + "code_apogee", + "titre", + "coefficient", + "module_type", + "id", + "ects", + "abbrev", + "ue_id", + "code", + "formation_id", + "heures_cours", + "matiere_id", + "heures_td", + "semestre_id", + "numero", + "module_id", +} + +UE_FIELDS = { + "semestre_idx", + "type", + "formation_id", + "ue_code", + "id", + "ects", + "acronyme", + "is_external", + "numero", + "code_apogee", + "titre", + "coefficient", + "color", + "ue_id", +} diff --git a/tests/unit/test_export_xml.py b/tests/unit/test_export_xml.py index c220638c..c14f4c1a 100644 --- a/tests/unit/test_export_xml.py +++ b/tests/unit/test_export_xml.py @@ -138,4 +138,4 @@ def test_export_xml(test_client): """ - assert xmls_compare(table_xml, expected_result) \ No newline at end of file + assert xmls_compare(table_xml, expected_result)