WIP: new API revue et corrigée (en cours)

This commit is contained in:
Emmanuel Viennet 2022-07-19 22:17:10 +02:00
parent 455ce295d8
commit ca1fe679db
16 changed files with 370 additions and 965 deletions

View File

@ -31,4 +31,3 @@ from app.api import evaluations
from app.api import jury from app.api import jury
from app.api import absences from app.api import absences
from app.api import logos from app.api import logos
from app.api import debug

View File

@ -1,15 +1,22 @@
#################################################### Absences ######################################################### ##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""ScoDoc 9 API : Absences
"""
from flask import jsonify from flask import jsonify
from app.api import bp from app.api import bp
from app.api.errors import error_response from app.api.errors import error_response
from app.api.auth import token_auth, token_permission_required from app.api.auth import token_auth, token_permission_required
from app.models import Identite, FormSemestre from app.models import Identite
from app.scodoc import notesdb as ndb from app.scodoc import notesdb as ndb
from app.scodoc import sco_abs from app.scodoc import sco_abs
from app.scodoc.sco_abs import list_abs_date, annule_absence, annule_justif, add_abslist
# from app.scodoc.sco_abs import annule_absence, annule_justif, add_abslist
from app.scodoc.sco_groups import get_group_members from app.scodoc.sco_groups import get_group_members
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@ -53,10 +60,10 @@ def absences(etudid: int = None):
) )
# Absences de l'étudiant # Absences de l'étudiant
ndb.open_db_connection() ndb.open_db_connection()
absences = sco_abs.list_abs_date(etud.id) abs_list = sco_abs.list_abs_date(etud.id)
for absence in absences: for absence in abs_list:
absence["jour"] = absence["jour"].isoformat() absence["jour"] = absence["jour"].isoformat()
return jsonify(absences) return jsonify(abs_list)
@bp.route("/absences/etudid/<int:etudid>/just", methods=["GET"]) @bp.route("/absences/etudid/<int:etudid>/just", methods=["GET"])
@ -158,9 +165,6 @@ def abs_groupe_etat(group_id: int, date_debut=None, date_fin=None):
... ...
] ]
""" """
# Fonction utilisée : app.scodoc.sco_groups.get_group_members() et app.scodoc.sco_abs.list_abs_date()
# Utilisation de la fonction get_group_members
members = get_group_members(group_id) members = get_group_members(group_id)
data = [] data = []
@ -175,88 +179,89 @@ def abs_groupe_etat(group_id: int, date_debut=None, date_fin=None):
return jsonify(data) return jsonify(data)
@bp.route( # XXX TODO EV: A REVOIR (data json dans le POST + modifier les routes)
"/absences/etudid/<int:etudid>/list_abs/<string:list_abs>/reset_etud_abs", # @bp.route(
methods=["POST"], # "/absences/etudid/<int:etudid>/list_abs/<string:list_abs>/reset_etud_abs",
defaults={"just_or_not": 0}, # methods=["POST"],
) # defaults={"just_or_not": 0},
@bp.route( # )
"/absences/etudid/<int:etudid>/list_abs/<string:list_abs>/reset_etud_abs/only_not_just", # @bp.route(
methods=["POST"], # "/absences/etudid/<int:etudid>/list_abs/<string:list_abs>/reset_etud_abs/only_not_just",
defaults={"just_or_not": 1}, # methods=["POST"],
) # defaults={"just_or_not": 1},
@bp.route( # )
"/absences/etudid/<int:etudid>/list_abs/<string:list_abs>/reset_etud_abs/only_just", # @bp.route(
methods=["POST"], # "/absences/etudid/<int:etudid>/list_abs/<string:list_abs>/reset_etud_abs/only_just",
defaults={"just_or_not": 2}, # methods=["POST"],
) # defaults={"just_or_not": 2},
@token_auth.login_required # )
@token_permission_required(Permission.APIAbsChange) # @token_auth.login_required
def reset_etud_abs(etudid: int, list_abs, just_or_not: int = 0): # @token_permission_required(Permission.APIAbsChange)
""" # def reset_etud_abs(etudid: int, list_abs: str, just_or_not: int = 0):
Set la liste des absences d'un étudiant sur tout un semestre. # """
(les absences existant pour cet étudiant sur cette période sont effacées) # Set la liste des absences d'un étudiant sur tout un semestre.
# (les absences existant pour cet étudiant sur cette période sont effacées)
etudid : l'id d'un étudiant # etudid : l'id d'un étudiant
list_abs : json d'absences # list_abs : json d'absences
just_or_not : 0 (pour les absences justifiées et non justifiées), # just_or_not : 0 (pour les absences justifiées et non justifiées),
1 (pour les absences justifiées), # 1 (pour les absences justifiées),
2 (pour les absences non justifiées) # 2 (pour les absences non justifiées)
""" # """
# Toutes les absences # # Toutes les absences
if just_or_not == 0: # if just_or_not == 0:
# suppression des absences et justificatif déjà existant pour éviter les doublons # # suppression des absences et justificatif déjà existant pour éviter les doublons
for abs in list_abs: # for abs in list_abs:
# Récupération de la date au format iso # # Récupération de la date au format iso
jour = abs["jour"].isoformat() # jour = abs["jour"].isoformat()
if abs["matin"] is True: # if abs["matin"] is True:
annule_absence(etudid, jour, True) # annule_absence(etudid, jour, True)
annule_justif(etudid, jour, True) # annule_justif(etudid, jour, True)
else: # else:
annule_absence(etudid, jour, False) # annule_absence(etudid, jour, False)
annule_justif(etudid, jour, False) # annule_justif(etudid, jour, False)
# Ajout de la liste d'absences en base # # Ajout de la liste d'absences en base
add_abslist(list_abs) # add_abslist(list_abs)
# Uniquement les absences justifiées # # Uniquement les absences justifiées
elif just_or_not == 1: # elif just_or_not == 1:
list_abs_not_just = [] # list_abs_not_just = []
# Trie des absences justifiées # # Trie des absences justifiées
for abs in list_abs: # for abs in list_abs:
if abs["estjust"] is False: # if abs["estjust"] is False:
list_abs_not_just.append(abs) # list_abs_not_just.append(abs)
# suppression des absences et justificatif déjà existant pour éviter les doublons # # suppression des absences et justificatif déjà existant pour éviter les doublons
for abs in list_abs: # for abs in list_abs:
# Récupération de la date au format iso # # Récupération de la date au format iso
jour = abs["jour"].isoformat() # jour = abs["jour"].isoformat()
if abs["matin"] is True: # if abs["matin"] is True:
annule_absence(etudid, jour, True) # annule_absence(etudid, jour, True)
annule_justif(etudid, jour, True) # annule_justif(etudid, jour, True)
else: # else:
annule_absence(etudid, jour, False) # annule_absence(etudid, jour, False)
annule_justif(etudid, jour, False) # annule_justif(etudid, jour, False)
# Ajout de la liste d'absences en base # # Ajout de la liste d'absences en base
add_abslist(list_abs_not_just) # add_abslist(list_abs_not_just)
# Uniquement les absences non justifiées # # Uniquement les absences non justifiées
elif just_or_not == 2: # elif just_or_not == 2:
list_abs_just = [] # list_abs_just = []
# Trie des absences non justifiées # # Trie des absences non justifiées
for abs in list_abs: # for abs in list_abs:
if abs["estjust"] is True: # if abs["estjust"] is True:
list_abs_just.append(abs) # list_abs_just.append(abs)
# suppression des absences et justificatif déjà existant pour éviter les doublons # # suppression des absences et justificatif déjà existant pour éviter les doublons
for abs in list_abs: # for abs in list_abs:
# Récupération de la date au format iso # # Récupération de la date au format iso
jour = abs["jour"].isoformat() # jour = abs["jour"].isoformat()
if abs["matin"] is True: # if abs["matin"] is True:
annule_absence(etudid, jour, True) # annule_absence(etudid, jour, True)
annule_justif(etudid, jour, True) # annule_justif(etudid, jour, True)
else: # else:
annule_absence(etudid, jour, False) # annule_absence(etudid, jour, False)
annule_justif(etudid, jour, False) # annule_justif(etudid, jour, False)
# Ajout de la liste d'absences en base # # Ajout de la liste d'absences en base
add_abslist(list_abs_just) # add_abslist(list_abs_just)

View File

@ -1,31 +0,0 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Emmanuel Viennet emmanuel.viennet@viennet.net
#
##############################################################################
"""
!!! ATTENTION !!!
Fichier a utilisé uniquement à des fins de debug
"""

View File

@ -14,12 +14,13 @@ import app
from app.api import bp from app.api import bp
from app.api.errors import error_response from app.api.errors import error_response
from app.api.auth import token_auth, token_permission_required from app.api.auth import token_auth, token_permission_required
from app.api.tools import get_last_instance_etud_from_etudid_or_nip_or_ine from app.api import tools
from app.models import Departement, FormSemestreInscription, FormSemestre, Identite from app.models import Departement, FormSemestreInscription, FormSemestre, Identite
from app.scodoc import sco_bulletins from app.scodoc import sco_bulletins
from app.scodoc import sco_groups from app.scodoc import sco_groups
from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
from app.scodoc import sco_utils as scu
@bp.route("/etudiants/courants", defaults={"long": False}) @bp.route("/etudiants/courants", defaults={"long": False})
@ -106,7 +107,7 @@ def etudiant(etudid: int = None, nip: str = None, ine: str = None):
"description": "" "description": ""
} }
""" """
etud = get_last_instance_etud_from_etudid_or_nip_or_ine(etudid, nip, ine) etud = tools.get_etud(etudid, nip, ine)
if etud is None: if etud is None:
return error_response( return error_response(
@ -153,36 +154,39 @@ def etudiants(etudid: int = None, nip: str = None, ine: str = None):
def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None): def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None):
""" """
Liste des semestres qu'un étudiant a suivi, triés par ordre chronologique. Liste des semestres qu'un étudiant a suivi, triés par ordre chronologique.
Attention, si accès via NIP ou INE, les semestres peuvent être de départements différents
(si l'étudiant a changé de département). L'id du département est `dept_id`.
Accès par etudid, nip ou ine Accès par etudid, nip ou ine
Exemple de résultat : Exemple de résultat :
[ [
{ {
"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", "date_fin": "31/08/2022",
"resp_can_edit": false,
"dept_id": 1, "dept_id": 1,
"elt_annee_apo": null,
"elt_sem_apo": null,
"ens_can_edit_eval": false,
"etat": true, "etat": true,
"resp_can_change_ens": true, "formation_id": 1,
"formsemestre_id": 1,
"gestion_compensation": false,
"gestion_semestrielle": false,
"id": 1, "id": 1,
"modalite": "FI", "modalite": "FI",
"ens_can_edit_eval": false, "resp_can_change_ens": true,
"formation_id": 1, "resp_can_edit": false,
"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": [] "responsables": []
"scodoc7_id": null,
"semestre_id": 1,
"titre_num": "Semestre test semestre 1",
"titre": "Semestre test",
}, },
... ...
] ]
@ -263,22 +267,21 @@ def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None)
methods=["GET"], methods=["GET"],
defaults={"version": "short", "pdf": False}, defaults={"version": "short", "pdf": False},
) )
# Version PDF non fonctionnelle @bp.route(
# @bp.route( "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf",
# "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf", methods=["GET"],
# methods=["GET"], defaults={"version": "short", "pdf": True},
# defaults={"version": "short", "pdf": True}, )
# ) @bp.route(
# @bp.route( "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf",
# "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf", methods=["GET"],
# methods=["GET"], defaults={"version": "short", "pdf": True},
# defaults={"version": "short", "pdf": True}, )
# ) @bp.route(
# @bp.route( "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf",
# "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf", methods=["GET"],
# methods=["GET"], defaults={"version": "short", "pdf": True},
# defaults={"version": "short", "pdf": True}, )
# )
@token_auth.login_required @token_auth.login_required
@token_permission_required(Permission.APIView) @token_permission_required(Permission.APIView)
def etudiant_bulletin_semestre( # XXX TODO Ajouter la possibilité de retourner en version pdf def etudiant_bulletin_semestre( # XXX TODO Ajouter la possibilité de retourner en version pdf
@ -296,150 +299,8 @@ def etudiant_bulletin_semestre( # XXX TODO Ajouter la possibilité de retourner
etudid : l'etudid d'un étudiant etudid : l'etudid d'un étudiant
nip : le code nip d'un étudiant nip : le code nip d'un étudiant
ine : le code ine d'un étudiant ine : le code ine d'un étudiant
Exemple de résultat : Exemple de résultat : voir https://scodoc.org/ScoDoc9API/#bulletin
{
"version": "0",
"type": "BUT",
"date": "2022-04-27T07:18:16.450634Z",
"publie": true,
"etudiant": {
"civilite": "X",
"code_ine": "1",
"code_nip": "1",
"date_naissance": "",
"email": "SACHA.COSTA@example.com",
"emailperso": "",
"etudid": 1,
"nom": "COSTA",
"prenom": "SACHA",
"nomprenom": "Sacha COSTA",
"lieu_naissance": "",
"dept_naissance": "",
"nationalite": "",
"boursier": "",
"fiche_url": "/ScoDoc/TAPI/Scolarite/ficheEtud?etudid=1",
"photo_url": "/ScoDoc/TAPI/Scolarite/get_photo_image?etudid=1&size=small",
"id": 1,
"codepostaldomicile": "",
"paysdomicile": "",
"telephonemobile": "",
"typeadresse": "domicile",
"domicile": "",
"villedomicile": "",
"telephone": "",
"fax": "",
"description": "",
},
"formation": {
"id": 1,
"acronyme": "BUT R&amp;T",
"titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications",
"titre": "BUT R&amp;T",
},
"formsemestre_id": 1,
"etat_inscription": "I",
"options": {
"show_abs": true,
"show_abs_modules": false,
"show_ects": true,
"show_codemodules": false,
"show_matieres": false,
"show_rangs": true,
"show_ue_rangs": true,
"show_mod_rangs": true,
"show_moypromo": false,
"show_minmax": false,
"show_minmax_mod": false,
"show_minmax_eval": false,
"show_coef": true,
"show_ue_cap_details": false,
"show_ue_cap_current": true,
"show_temporary": true,
"temporary_txt": "Provisoire",
"show_uevalid": true,
"show_date_inscr": true,
},
"ressources": {
"R101": {
"id": 1,
"titre": "Initiation aux r\u00e9seaux informatiques",
"code_apogee": null,
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=1",
"moyenne": {},
"evaluations": [
{
"id": 1,
"description": "eval1",
"date": "2022-04-20",
"heure_debut": "08:00",
"heure_fin": "09:00",
"coef": "01.00",
"poids": {
"RT1.1": 1.0,
},
"note": {
"value": "12.00",
"min": "00.00",
"max": "18.00",
"moy": "10.88",
},
"url": "/ScoDoc/TAPI/Scolarite/Notes/evaluation_listenotes?evaluation_id=1",
}
],
},
},
"saes": {
"SAE11": {
"id": 2,
"titre": "Se sensibiliser \u00e0 l&apos;hygi\u00e8ne informatique et \u00e0 la cybers\u00e9curit\u00e9",
"code_apogee": null,
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=2",
"moyenne": {},
"evaluations": [],
},
},
"ues": {
"RT1.1": {
"id": 1,
"titre": "Administrer les r\u00e9seaux et l\u2019Internet",
"numero": 1,
"type": 0,
"color": "#B80004",
"competence": null,
"moyenne": {
"value": "08.50",
"min": "06.00",
"max": "16.50",
"moy": "11.31",
"rang": "12",
"total": 16,
},
"bonus": "00.00",
"malus": "00.00",
"capitalise": null,
"ressources": {
"R101": {"id": 1, "coef": 12.0, "moyenne": "12.00"},
},
"saes": {
"SAE11": {"id": 2, "coef": 16.0, "moyenne": "~"},
},
"ECTS": {"acquis": 0.0, "total": 12.0},
},
"semestre": {
"etapes": [],
"date_debut": "2021-09-01",
"date_fin": "2022-08-31",
"annee_universitaire": "2021 - 2022",
"numero": 1,
"inscription": "",
"groupes": [],
"absences": {"injustifie": 1, "total": 2},
"ECTS": {"acquis": 0, "total": 30.0},
"notes": {"value": "10.60", "min": "02.40", "moy": "11.05", "max": "17.40"},
"rang": {"value": "10", "total": 16},
},
},
}
""" """
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first_or_404() formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first_or_404()
dept = Departement.query.filter_by(id=formsemestre.dept_id).first_or_404() dept = Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
@ -466,11 +327,10 @@ def etudiant_bulletin_semestre( # XXX TODO Ajouter la possibilité de retourner
app.set_sco_dept(dept.acronym) app.set_sco_dept(dept.acronym)
if pdf: if pdf:
response = make_response( pdf_response, _ = do_formsemestre_bulletinetud(
do_formsemestre_bulletinetud(formsemestre, etudid, version, "pdf") formsemestre, etudid, version=version, format="pdf"
) )
response.headers["Content-Type"] = "application/json" return pdf_response
return response
return sco_bulletins.get_formsemestre_bulletin_etud_json( return sco_bulletins.get_formsemestre_bulletin_etud_json(
formsemestre, etud, version=version formsemestre, etud, version=version

View File

@ -1,10 +1,18 @@
############################################### Evaluations ########################################################### ##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""
ScoDoc 9 API : accès aux évaluations
"""
from flask import jsonify from flask import jsonify
import app import app
from app import models from app import models
from app.models import Evaluation
from app.api import bp from app.api import bp
from app.api.auth import token_auth, token_permission_required from app.api.auth import token_auth, token_permission_required
from app.api.errors import error_response from app.api.errors import error_response
@ -18,7 +26,7 @@ from app.scodoc.sco_permissions import Permission
@token_permission_required(Permission.APIView) @token_permission_required(Permission.APIView)
def evaluations(moduleimpl_id: int): def evaluations(moduleimpl_id: int):
""" """
Retourne la liste des évaluations à partir de l'id d'un moduleimpl Retourne la liste des évaluations d'un moduleimpl
moduleimpl_id : l'id d'un moduleimpl moduleimpl_id : l'id d'un moduleimpl
@ -40,7 +48,7 @@ def evaluations(moduleimpl_id: int):
"evaluation_id": 1, "evaluation_id": 1,
"jouriso": "2022-04-20", "jouriso": "2022-04-20",
"duree": "1h", "duree": "1h",
"descrheure": " de 08h00 \u00e0 09h00", "descrheure": " de 08h00 à 09h00",
"matin": 1, "matin": 1,
"apresmidi": 0 "apresmidi": 0
}, },
@ -88,16 +96,11 @@ def evaluation_notes(evaluation_id: int):
... ...
} }
""" """
# Fonction utilisée : app.scodoc.sco_evaluation_db.do_evaluation_get_all_notes()
evaluation = models.Evaluation.query.filter_by(id=evaluation_id).first_or_404() evaluation = models.Evaluation.query.filter_by(id=evaluation_id).first_or_404()
dept = models.Departement.query.filter_by( dept = evaluation.moduleimpl.formsemestre.departement
id=evaluation.moduleimpl.formsemestre.dept_id
).first()
app.set_sco_dept(dept.acronym) app.set_sco_dept(dept.acronym)
try: try:
# Utilisation de la fonction do_evaluation_get_all_notes
data = do_evaluation_get_all_notes(evaluation_id) data = do_evaluation_get_all_notes(evaluation_id)
except AttributeError: # ??? except AttributeError: # ???
return error_response( return error_response(

View File

@ -1,4 +1,13 @@
##############################################" Formations ############################################################ ##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""
ScoDoc 9 API : accès aux formations
"""
from flask import jsonify from flask import jsonify
import app import app
@ -10,6 +19,7 @@ from app.models.formations import Formation
from app.scodoc import sco_formations from app.scodoc import sco_formations
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@bp.route("/formations", methods=["GET"]) @bp.route("/formations", methods=["GET"])
@token_auth.login_required @token_auth.login_required
@token_permission_required(Permission.APIView) @token_permission_required(Permission.APIView)
@ -17,16 +27,11 @@ def formations():
""" """
Retourne la liste de toutes les formations (tous départements) Retourne la liste de toutes les formations (tous départements)
Exemple de résultat :
""" """
# Récupération de toutes les formations data = [d.to_dict() for d in models.Formation.query]
list_formations = models.Formation.query.all()
# Mise en forme des données
data = [d.to_dict() for d in list_formations]
return jsonify(data) return jsonify(data)
@bp.route("/formations_ids", methods=["GET"]) @bp.route("/formations_ids", methods=["GET"])
@token_auth.login_required @token_auth.login_required
@token_permission_required(Permission.APIView) @token_permission_required(Permission.APIView)
@ -36,12 +41,7 @@ def formations_ids():
Exemple de résultat : [ 17, 99, 32 ] Exemple de résultat : [ 17, 99, 32 ]
""" """
# Récupération de toutes les formations data = [d.id for d in models.Formation.query]
list_formations = models.Formation.query.all()
# Mise en forme des données
data = [d.id for d in list_formations]
return jsonify(data) return jsonify(data)
@ -50,7 +50,7 @@ def formations_ids():
@token_permission_required(Permission.APIView) @token_permission_required(Permission.APIView)
def formation_by_id(formation_id: int): def formation_by_id(formation_id: int):
""" """
Retourne une formation en fonction d'un id donné La formation d'id donné
formation_id : l'id d'une formation formation_id : l'id d'une formation
@ -58,7 +58,7 @@ def formation_by_id(formation_id: int):
{ {
"id": 1, "id": 1,
"acronyme": "BUT R&amp;T", "acronyme": "BUT R&amp;T",
"titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications", "titre_officiel": "Bachelor technologique réseaux et télécommunications",
"formation_code": "V1RET", "formation_code": "V1RET",
"code_specialite": null, "code_specialite": null,
"dept_id": 1, "dept_id": 1,
@ -69,13 +69,8 @@ def formation_by_id(formation_id: int):
"formation_id": 1 "formation_id": 1
} }
""" """
# Récupération de la formation formation = models.Formation.query.get_or_404(formation_id)
formation = models.Formation.query.filter_by(id=formation_id).first_or_404() return jsonify(formation.to_dict())
# Mise en forme des données
data = formation.to_dict()
return jsonify(data)
@bp.route( @bp.route(
@ -187,10 +182,8 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
} }
""" """
formation = Formation.query.get_or_404(formation_id) formation = Formation.query.get_or_404(formation_id)
dept = models.Departement.query.filter_by(id=formation.dept_id).first() app.set_sco_dept(formation.departement.acronym)
app.set_sco_dept(dept.acronym)
try: try:
# Utilisation de la fonction formation_export
data = sco_formations.formation_export(formation_id, export_ids) data = sco_formations.formation_export(formation_id, export_ids)
except ValueError: except ValueError:
return error_response(500, message="Erreur inconnue") return error_response(500, message="Erreur inconnue")
@ -236,9 +229,8 @@ def moduleimpl(moduleimpl_id: int):
} }
} }
""" """
modimpl = models.ModuleImpl.query.filter_by(id=moduleimpl_id).first_or_404() modimpl = models.ModuleImpl.query.get_or_404(moduleimpl_id)
data = modimpl.to_dict() return jsonify(modimpl.to_dict())
return jsonify(data)
@bp.route( @bp.route(
@ -253,10 +245,9 @@ def referentiel_competences(formation_id: int):
formation_id : l'id d'une formation formation_id : l'id d'une formation
return json, ou null si pas de référentiel associé. return null si pas de référentiel associé.
""" """
formation = models.Formation.query.filter_by(id=formation_id).first_or_404() formation = models.Formation.query.get_or_404(formation_id)
if formation.referentiel_competence is None: if formation.referentiel_competence is None:
return jsonify(None) return jsonify(None)
return jsonify(formation.referentiel_competence.to_dict()) return jsonify(formation.referentiel_competence.to_dict())

View File

@ -1,5 +1,12 @@
########################################## Formsemestres ############################################################## ##############################################################################
import statistics # ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""
ScoDoc 9 API : accès aux formsemestres
"""
from flask import jsonify from flask import jsonify
@ -7,11 +14,10 @@ import app
from app import models from app import models
from app.api import bp from app.api import bp
from app.api.auth import token_auth, token_permission_required from app.api.auth import token_auth, token_permission_required
from app.api.errors import error_response
from app.comp import res_sem from app.comp import res_sem
from app.comp.moy_mod import ModuleImplResults from app.comp.moy_mod import ModuleImplResults
from app.comp.res_compat import NotesTableCompat from app.comp.res_compat import NotesTableCompat
from app.models import Departement, FormSemestre, FormSemestreEtape, ModuleImpl from app.models import Evaluation, FormSemestre, FormSemestreEtape, ModuleImpl
from app.scodoc.sco_bulletins import get_formsemestre_bulletin_etud_json from app.scodoc.sco_bulletins import get_formsemestre_bulletin_etud_json
from app.scodoc.sco_groups import get_etud_groups from app.scodoc.sco_groups import get_etud_groups
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@ -22,7 +28,7 @@ import app.scodoc.sco_utils as scu
@bp.route("/formsemestre/<int:formsemestre_id>", methods=["GET"]) @bp.route("/formsemestre/<int:formsemestre_id>", methods=["GET"])
@token_auth.login_required @token_auth.login_required
@token_permission_required(Permission.APIView) @token_permission_required(Permission.APIView)
def formsemestre(formsemestre_id: int): def formsemestre_infos(formsemestre_id: int):
""" """
Information sur le formsemestre indiqué. Information sur le formsemestre indiqué.
@ -59,17 +65,8 @@ def formsemestre(formsemestre_id: int):
} }
""" """
formsemestre: FormSemestre = models.FormSemestre.query.filter_by( formsemestre: FormSemestre = models.FormSemestre.query.get_or_404(formsemestre_id)
id=formsemestre_id return jsonify(formsemestre.to_dict_api())
).first_or_404()
data = formsemestre.to_dict(convert_parcours=True)
# 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["annee_scolaire"] = formsemestre.annee_scolaire_str()
data["session_id"] = formsemestre.session_id()
return jsonify(data)
@bp.route("/formsemestre/apo/<string:etape_apo>", methods=["GET"]) @bp.route("/formsemestre/apo/<string:etape_apo>", methods=["GET"])
@ -92,9 +89,7 @@ def formsemestre_apo(etape_apo: str):
FormSemestreEtape.formsemestre_id == FormSemestre.id, FormSemestreEtape.formsemestre_id == FormSemestre.id,
) )
return jsonify( return jsonify([formsemestre.to_dict_api() for formsemestre in formsemestres])
[formsemestre.to_dict(convert_parcours=True) for formsemestre in formsemestres]
)
@bp.route("/formsemestre/<int:formsemestre_id>/bulletins", methods=["GET"]) @bp.route("/formsemestre/<int:formsemestre_id>/bulletins", methods=["GET"])
@ -106,183 +101,10 @@ def bulletins(formsemestre_id: int):
formsemestre_id : l'id d'un formesemestre formsemestre_id : l'id d'un formesemestre
Exemple de résultat : Exemple de résultat : liste, voir https://scodoc.org/ScoDoc9API/#bulletin
[
{
"version": "0",
"type": "BUT",
"date": "2022-04-27T07:18:16.450634Z",
"publie": true,
"etudiant": {
"civilite": "X",
"code_ine": "1",
"code_nip": "1",
"date_naissance": "",
"email": "SACHA.COSTA@example.com",
"emailperso": "",
"etudid": 1,
"nom": "COSTA",
"prenom": "SACHA",
"nomprenom": "Sacha COSTA",
"lieu_naissance": "",
"dept_naissance": "",
"nationalite": "",
"boursier": "",
"fiche_url": "/ScoDoc/TAPI/Scolarite/ficheEtud?etudid=1",
"photo_url": "/ScoDoc/TAPI/Scolarite/get_photo_image?etudid=1&size=small",
"id": 1,
"codepostaldomicile": "",
"paysdomicile": "",
"telephonemobile": "",
"typeadresse": "domicile",
"domicile": "",
"villedomicile": "",
"telephone": "",
"fax": "",
"description": ""
},
"formation": {
"id": 1,
"acronyme": "BUT R&amp;T",
"titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications",
"titre": "BUT R&amp;T"
},
"formsemestre_id": 1,
"etat_inscription": "I",
"options": {
"show_abs": true,
"show_abs_modules": false,
"show_ects": true,
"show_codemodules": false,
"show_matieres": false,
"show_rangs": true,
"show_ue_rangs": true,
"show_mod_rangs": true,
"show_moypromo": false,
"show_minmax": false,
"show_minmax_mod": false,
"show_minmax_eval": false,
"show_coef": true,
"show_ue_cap_details": false,
"show_ue_cap_current": true,
"show_temporary": true,
"temporary_txt": "Provisoire",
"show_uevalid": true,
"show_date_inscr": true
},
"ressources": {
"R101": {
"id": 1,
"titre": "Initiation aux r\u00e9seaux informatiques",
"code_apogee": null,
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=1",
"moyenne": {},
"evaluations": [
{
"id": 1,
"description": "eval1",
"date": "2022-04-20",
"heure_debut": "08:00",
"heure_fin": "09:00",
"coef": "01.00",
"poids": {
"RT1.1": 1.0,
},
"note": {
"value": "12.00",
"min": "00.00",
"max": "18.00",
"moy": "10.88"
},
"url": "/ScoDoc/TAPI/Scolarite/Notes/evaluation_listenotes?evaluation_id=1"
}
]
},
},
"saes": {
"SAE11": {
"id": 2,
"titre": "Se sensibiliser \u00e0 l&apos;hygi\u00e8ne informatique et \u00e0 la cybers\u00e9curit\u00e9",
"code_apogee": null,
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=2",
"moyenne": {},
"evaluations": []
},
},
"ues": {
"RT1.1": {
"id": 1,
"titre": "Administrer les r\u00e9seaux et l\u2019Internet",
"numero": 1,
"type": 0,
"color": "#B80004",
"competence": null,
"moyenne": {
"value": "08.50",
"min": "06.00",
"max": "16.50",
"moy": "11.31",
"rang": "12",
"total": 16
},
"bonus": "00.00",
"malus": "00.00",
"capitalise": null,
"ressources": {
"R101": {
"id": 1,
"coef": 12.0,
"moyenne": "12.00"
},
},
"saes": {
"SAE11": {
"id": 2,
"coef": 16.0,
"moyenne": "~"
},
},
"ECTS": {
"acquis": 0.0,
"total": 12.0
}
},
"semestre": {
"etapes": [],
"date_debut": "2021-09-01",
"date_fin": "2022-08-31",
"annee_universitaire": "2021 - 2022",
"numero": 1,
"inscription": "",
"groupes": [],
"absences": {
"injustifie": 1,
"total": 2
},
"ECTS": {
"acquis": 0,
"total": 30.0
},
"notes": {
"value": "10.60",
"min": "02.40",
"moy": "11.05",
"max": "17.40"
},
"rang": {
"value": "10",
"total": 16
}
}
},
...
]
""" """
formsemestre = models.FormSemestre.query.filter_by( formsemestre = models.FormSemestre.query.get_or_404(formsemestre_id)
id=formsemestre_id app.set_sco_dept(formsemestre.departement.acronym)
).first_or_404()
dept = models.Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
app.set_sco_dept(dept.acronym)
data = [] data = []
for etu in formsemestre.etuds: for etu in formsemestre.etuds:
@ -292,44 +114,6 @@ def bulletins(formsemestre_id: int):
return jsonify(data) return jsonify(data)
# XXX Attendre ScoDoc 9.3
# @bp.route("/formsemestre/<int:formsemestre_id>/jury", methods=["GET"])
# @token_auth.login_required
# @token_permission_required(Permission.APIView)
# def jury(formsemestre_id: int):
# """
# Retourne le récapitulatif des décisions jury
# formsemestre_id : l'id d'un formsemestre
# Exemple de résultat :
# """
# # Fonction utilisée : app.scodoc.sco_pvjury.formsemestre_pvjury()
# formsemestre = models.FormSemestre.query.filter_by(
# id=formsemestre_id
# ).first_or_404()
# dept = models.Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
# app.set_sco_dept(dept.acronym)
# data = formsemestre_pvjury(formsemestre_id)
# # try:
# # # Utilisation de la fonction formsemestre_pvjury
# # data = formsemestre_pvjury(formsemestre_id)
# # except AttributeError:
# # 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 jsonify(data)
@bp.route( @bp.route(
"/formsemestre/<int:formsemestre_id>/programme", "/formsemestre/<int:formsemestre_id>/programme",
methods=["GET"], methods=["GET"],
@ -401,10 +185,7 @@ def formsemestre_programme(formsemestre_id: int):
"modules" : [ ... les modules qui ne sont ni des SAEs ni des ressources ... ] "modules" : [ ... les modules qui ne sont ni des SAEs ni des ressources ... ]
} }
""" """
formsemestre: FormSemestre = models.FormSemestre.query.filter_by( formsemestre: FormSemestre = models.FormSemestre.query.get_or_404(formsemestre_id)
id=formsemestre_id
).first_or_404()
ues = formsemestre.query_ues() ues = formsemestre.query_ues()
m_list = { m_list = {
ModuleType.RESSOURCE: [], ModuleType.RESSOURCE: [],
@ -428,48 +209,37 @@ def formsemestre_programme(formsemestre_id: int):
@bp.route( @bp.route(
"/formsemestre/<int:formsemestre_id>/etudiants", "/formsemestre/<int:formsemestre_id>/etudiants",
methods=["GET"], methods=["GET"],
defaults={"etat": "I"}, defaults={"etat": scu.INSCRIT},
) )
@bp.route( @bp.route(
"/formsemestre/<int:formsemestre_id>/etudiants/demissionnaires", "/formsemestre/<int:formsemestre_id>/etudiants/demissionnaires",
methods=["GET"], methods=["GET"],
defaults={"etat": "D"}, defaults={"etat": scu.DEMISSION},
) )
@bp.route( @bp.route(
"/formsemestre/<int:formsemestre_id>/etudiants/defaillants", "/formsemestre/<int:formsemestre_id>/etudiants/defaillants",
methods=["GET"], methods=["GET"],
defaults={"etat": "DEF"}, defaults={"etat": scu.DEF},
) )
@token_auth.login_required @token_auth.login_required
@token_permission_required(Permission.APIView) @token_permission_required(Permission.APIView)
def formsemestre_etudiants(formsemestre_id: int, etat: str): def formsemestre_etudiants(formsemestre_id: int, etat: str):
""" """
Retourne la liste des étudiants d'un semestre Retourne la liste des étudiants d'un formsemestre
formsemestre_id : l'id d'un semestre formsemestre_id : l'id d'un formsemestre
""" """
# fonction to use : sco_groups.get_etud_groups formsemestre: FormSemestre = models.FormSemestre.query.filter_by(
formsemestre = models.FormSemestre.query.filter_by(
id=formsemestre_id id=formsemestre_id
).first_or_404() ).first_or_404()
# Récupération des étudiants du formsemestre inscriptions = [ins for ins in formsemestre.inscriptions if ins.etat == etat]
etuds = [etu.to_dict_short() for etu in formsemestre.etuds] etuds = [ins.etud.to_dict_short() for ins in inscriptions]
# Ajout des groupes de chaque étudiants
for etud in etuds:
etud["groups"] = get_etud_groups(etud["id"], formsemestre_id)
res = [] return jsonify(etuds)
# Trie des étudiants suivant leur état d'inscription voulu
for etu in etuds:
formsemestre_inscription = models.FormSemestreInscription.query.filter_by(
formsemestre_id=formsemestre_id, etudid=etu["id"]
).first_or_404()
if formsemestre_inscription.etat == etat:
res.append(etu)
# Ajout des groups de chaques étudiants
for etu in res:
etu["groups"] = get_etud_groups(etu["id"], formsemestre_id)
return jsonify(res)
@bp.route("/formsemestre/<int:formsemestre_id>/etat_evals", methods=["GET"]) @bp.route("/formsemestre/<int:formsemestre_id>/etat_evals", methods=["GET"])
@ -477,154 +247,103 @@ def formsemestre_etudiants(formsemestre_id: int, etat: str):
@token_permission_required(Permission.APIView) @token_permission_required(Permission.APIView)
def etat_evals(formsemestre_id: int): def etat_evals(formsemestre_id: int):
""" """
Retourne les informations sur l'état des évaluations d'un semestre donnée Informations sur l'état des évaluations d'un formsemestre.
formsemestre_id : l'id d'un semestre formsemestre_id : l'id d'un semestre
Exemple de résultat : Exemple de résultat :
{ [
"RT1.1": [ {
{ "id": 1, // moduleimpl_id
"id": 1, "titre": "Initiation aux réseaux informatiques",
"titre": "Initiation aux réseaux informatiques", "evaluations": [
"evaluations": [ {
{ "id": 1,
"id": 1, "description": null,
"description": null, "datetime_epreuve": null,
"datetime_epreuve": null, "heure_fin": "09:00:00",
"heure_fin": "09:00:00", "coefficient": "02.00"
"coefficient": "02.00" "is_complete": true,
"comptee": "oui", "nb_inscrits": 16,
"inscrits": 16, "nb_manquantes": 0,
"manquantes": 0, "ABS": 0,
"ABS": 0, "ATT": 0,
"ATT": 0, "EXC": 0,
"EXC": 0, "saisie_notes": {
"saisie_notes": { "datetime_debut": "2021-09-11T00:00:00+02:00",
"datetime_debut": "2021-09-11T00:00:00+02:00", "datetime_fin": "2022-08-25T00:00:00+02:00",
"datetime_fin": "2022-08-25T00:00:00+02:00", "datetime_mediane": "2022-03-19T00:00:00+01:00"
"datetime_mediane": "2022-03-19T00:00:00+01:00" }
} },
}, ...
{ ]
"id": 22, },
"description": null, ]
"datetime_epreuve": "Tue, 31 May 2022 00:00:00 GMT",
"heure_fin": "08:00:00",
"comptee": "oui",
"inscrits": 16,
"manquantes": 0,
"ABS": 0,
"ATT": 0,
"EXC": 0,
"saisie_notes": {
"datetime_debut": "2021-09-11T00:00:00+02:00",
"datetime_fin": "2022-08-25T00:00:00+02:00",
"datetime_mediane": "2022-03-19T00:00:00+01:00"
}
},
]
},
]
}
""" """
# Récupération du semestre
formsemestre = FormSemestre.query.get_or_404(formsemestre_id) formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
app.set_sco_dept(formsemestre.departement.acronym)
# Set du dept
dept = Departement.query.get(formsemestre.dept_id)
app.set_sco_dept(dept.acronym)
# Récupération des Ues
list_ues = formsemestre.query_ues()
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
ues = {} result = []
for modimpl_id in nt.modimpls_results:
modimpl_results: ModuleImplResults = nt.modimpls_results[modimpl_id]
modimpl = ModuleImpl.query.get_or_404(modimpl_id)
modimpl_dict = modimpl.to_dict()
for ue in list_ues: list_eval = []
modules = [] for evaluation_id in modimpl_results.evaluations_etat:
mods = ue.modules eval_etat = modimpl_results.evaluations_etat[evaluation_id]
evaluation = Evaluation.query.get_or_404(evaluation_id)
eval_dict = evaluation.to_dict()
eval_dict["etat"] = eval_etat.to_dict()
for mod in mods: eval_dict["nb_inscrits"] = modimpl_results.nb_inscrits_module
dict_module = {} eval_dict["nb_notes_manquantes"] = len(
moduleimpl = ModuleImpl.query.get_or_404(mod.id) modimpl_results.evals_etudids_sans_note[evaluation.id]
)
eval_dict["nb_notes_abs"] = sum(
modimpl_results.evals_notes[evaluation.id] == scu.NOTES_ABSENCE
)
eval_dict["nb_notes_att"] = eval_etat.nb_attente
eval_dict["nb_notes_exc"] = sum(
modimpl_results.evals_notes[evaluation.id] == scu.NOTES_NEUTRALISE
)
modimpl_results: ModuleImplResults = nt.modimpls_results[moduleimpl.id] # Récupération de toutes les notes de l'évaluation
# eval["notes"] = modimpl_results.get_eval_notes_dict(evaluation_id)
dict_module["id"] = mod.id notes = models.NotesNotes.query.filter_by(evaluation_id=evaluation.id).all()
dict_module["titre"] = mod.titre
list_eval = [] date_debut = None
for evaluation in moduleimpl.evaluations: date_fin = None
eval_etat = modimpl_results.evaluations_etat[evaluation.id] date_mediane = None
eval = {}
eval["id"] = evaluation.id # Si il y a plus d'une note saisie pour l'évaluation
eval["description"] = evaluation.description if len(notes) >= 1:
eval["datetime_epreuve"] = ( # Trie des notes en fonction de leurs dates
evaluation.jour.isoformat() if evaluation.jour is not None else None notes_sorted = sorted(notes, key=lambda note: note.date)
)
eval["heure_fin"] = evaluation.heure_fin.isoformat()
eval["coefficient"] = evaluation.coefficient
eval["comptee"] = "oui" if eval_etat.is_complete else "non"
eval["inscrits"] = modimpl_results.nb_inscrits_module
eval["manquantes"] = len(
modimpl_results.evals_etudids_sans_note[evaluation.id]
)
eval["ABS"] = sum(
modimpl_results.evals_notes[evaluation.id] == scu.NOTES_ABSENCE
)
eval["ATT"] = eval_etat.nb_attente
eval["EXC"] = sum(
modimpl_results.evals_notes[evaluation.id] == scu.NOTES_NEUTRALISE
)
# Récupération de toutes les notes de l'évaluation date_debut = notes_sorted[0].date
notes = models.NotesNotes.query.filter_by( date_fin = notes_sorted[-1].date
evaluation_id=evaluation.id
).all()
date_debut = None # Récupération de l'id de la note médiane
date_fin = None list_id_notes_sorted = [note.id for note in notes_sorted]
date_mediane = None id_mediane = list_id_notes_sorted[len(list_id_notes_sorted) // 2]
date_mediane = notes_sorted[id_mediane].date
# Si il y a plus d'une note saisie pour l'évaluation eval_dict["saisie_notes"] = {
if len(notes) >= 1: "datetime_debut": date_debut.isoformat()
# Trie des notes en fonction de leurs dates if date_debut is not None
notes_sorted = sorted(notes, key=lambda note: note.date) else None,
"datetime_fin": date_fin.isoformat() if date_fin is not None else None,
"datetime_mediane": date_mediane.isoformat()
if date_mediane is not None
else None,
}
date_debut = notes_sorted[0].date list_eval.append(eval)
date_fin = notes_sorted[-1].date
# Récupération de l'id de la note médiane modimpl_dict["evaluations"] = list_eval
list_id_notes_sorted = [note.id for note in notes_sorted] result.append(modimpl_dict)
# Ici si la longueur est paire on prend, on prend le +1 car un indice ne peux pas avoir de nombre floatant return jsonify(result)
id_mediane = list_id_notes_sorted[
int((len(list_id_notes_sorted)) / 2)
]
for n in notes_sorted:
if n.id == id_mediane:
date_mediane = n.date
eval["saisie_notes"] = {
"datetime_debut": date_debut.isoformat()
if date_debut is not None
else None,
"datetime_fin": date_fin.isoformat()
if date_fin is not None
else None,
"datetime_mediane": date_mediane.isoformat()
if date_mediane is not None
else None,
}
list_eval.append(eval)
dict_module["evaluations"] = list_eval
modules.append(dict_module)
ues[ue.acronyme] = modules
return jsonify(ues)

View File

@ -1,12 +1,21 @@
############################################### Partitions ############################################################ ##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""
ScoDoc 9 API : partitions
"""
from flask import jsonify from flask import jsonify
from app import models
from app.api import bp from app.api import bp
from app.api.errors import error_response from app.api.errors import error_response
from app.api.auth import token_auth, token_permission_required from app.api.auth import token_auth, token_permission_required
from app.scodoc.sco_groups import get_group_members, setGroups, get_partitions_list from app.models import FormSemestre, FormSemestreInscription, Identite
from app.models import GroupDescr, Partition
from app.models.groups import group_membership
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@ -41,15 +50,8 @@ def partition(formsemestre_id: int):
} }
] ]
""" """
# # Récupération de toutes les partitions formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
# partitions = models.Partition.query.filter_by(id=formsemestre_id) return jsonify([partition.to_dict() for partition in formsemestre.partitions])
#
# # Mise en forme des données
# data = [partition.to_dict() for partition in partitions]
data = get_partitions_list(formsemestre_id)
return jsonify(data)
@bp.route("/partition/group/<int:group_id>", methods=["GET"]) @bp.route("/partition/group/<int:group_id>", methods=["GET"])
@ -64,58 +66,32 @@ def etud_in_group(group_id: int, etat=None):
etat : état de l'inscription etat : état de l'inscription
Exemple de résultat : Exemple de résultat :
[ [
{ {
"etudid": 10, 'civilite': 'M',
"id": 10, 'id': 123456,
"dept_id": 1, 'ine': None,
"nom": "BOUTET", 'nip': '987654321',
"prenom": "Marguerite", 'nom': 'MARTIN',
"nom_usuel": "", 'nom_usuel': null,
"civilite": "F", 'prenom': 'JEAN'}
"date_naissance": null, },
"lieu_naissance": null, ...
"dept_naissance": null, ]
"nationalite": null,
"statut": null,
"boursier": null,
"photo_filename": null,
"code_nip": "10",
"code_ine": "10",
"scodoc7_id": null,
"email": "MARGUERITE.BOUTET@example.com",
"emailperso": null,
"domicile": null,
"codepostaldomicile": null,
"villedomicile": null,
"paysdomicile": null,
"telephone": null,
"telephonemobile": null,
"fax": null,
"typeadresse": "domicile",
"description": null,
"group_id": 1,
"etat": "I",
"civilite_str": "Mme",
"nom_disp": "BOUTET",
"nomprenom": "Mme Marguerite BOUTET",
"ne": "e",
"email_default": "MARGUERITE.BOUTET@example.com"
},
...
]
""" """
# Fonction utilisée : app.scodoc.sco_groups.get_group_members() # Fonction utilisée : app.scodoc.sco_groups.get_group_members()
group = GroupDescr.query.get_or_404(group_id)
if etat is None: if etat is None:
data = get_group_members(group_id) query = group.etuds
else: else:
data = get_group_members(group_id, etat) query = (
Identite.query.join(FormSemestreInscription)
.filter_by(formsemestre_id=group.partition.formsemestre_id, etat=etat)
.join(group_membership)
.filter_by(group_id=group_id)
)
if len(data) == 0: return jsonify([etud.to_dict_short() for etud in query])
return error_response(404, message="group_id inconnu")
return jsonify(data)
@bp.route( @bp.route(

View File

@ -1,14 +1,22 @@
##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""ScoDoc 9 API : outils
"""
from sqlalchemy import desc
from app import models from app import models
from app.api.errors import error_response from app.api.errors import error_response
from app.models import Identite from app.models import Identite, Admission
def get_last_instance_etud_from_etudid_or_nip_or_ine( def get_etud(etudid=None, nip=None, ine=None) -> models.Identite:
etudid=None, nip=None, ine=None
) -> models.Identite:
""" """
Retourne l'instance de l'etudiant la plus récente en fonction de l'etudid, code nip et code ine rentré en paramètres L'instance d'étudiant la plus récente en fonction de l'etudid,
ou du code nip ou code ine.
etudid : None ou un int etudid etudid : None ou un int etudid
nip : None ou un int code_nip nip : None ou un int code_nip
@ -17,26 +25,15 @@ def get_last_instance_etud_from_etudid_or_nip_or_ine(
Return None si étudiant inexistant. Return None si étudiant inexistant.
""" """
if etudid is not None: if etudid is not None:
etud = Identite.query.get(etudid) return Identite.query.get(etudid)
if nip is not None:
query = Identite.query.filter_by(code_nip=nip)
elif ine is not None:
query = Identite.query.filter_by(code_ine=ine)
else: else:
if nip is not None: return error_response(
query = Identite.query.filter_by(code_nip=nip) 404,
elif ine is not None: message="parametre manquant",
query = Identite.query.filter_by(code_ine=ine) )
else: return query.join(Admission).order_by(desc(Admission.annee)).first()
return error_response(
404,
message="parametre manquant",
)
if query.count() > 1: # cas rare d'un étudiant présent dans plusieurs depts
etuds = []
for e in query:
admission = e.admission.first()
etuds.append((((admission.annee or 0) if admission else 0), e))
etuds.sort()
etud = etuds[-1][1]
else:
etud = query.first()
return etud

View File

@ -33,6 +33,7 @@ Rappel: pour éviter les confusions, on appelera *poids* les coefficients d'une
évaluation dans un module, et *coefficients* ceux utilisés pour le calcul de la évaluation dans un module, et *coefficients* ceux utilisés pour le calcul de la
moyenne générale d'une UE. moyenne générale d'une UE.
""" """
import dataclasses
from dataclasses import dataclass from dataclasses import dataclass
import numpy as np import numpy as np
import pandas as pd import pandas as pd
@ -54,6 +55,10 @@ class EvaluationEtat:
nb_attente: int nb_attente: int
is_complete: bool is_complete: bool
def to_dict(self):
"convert to dict"
return dataclasses.asdict(self)
class ModuleImplResults: class ModuleImplResults:
"""Classe commune à toutes les formations (standard et APC). """Classe commune à toutes les formations (standard et APC).
@ -236,6 +241,16 @@ class ModuleImplResults:
self.evals_notes.values > scu.NOTES_ABSENCE, self.evals_notes.values, 0.0 self.evals_notes.values > scu.NOTES_ABSENCE, self.evals_notes.values, 0.0
) / [e.note_max / 20.0 for e in moduleimpl.evaluations] ) / [e.note_max / 20.0 for e in moduleimpl.evaluations]
def get_eval_notes_dict(self, evaluation_id: int) -> dict:
"""Notes d'une évaulation, brutes, sous forme d'un dict
{ etudid : valeur }
avec les valeurs float, ou "ABS" ou EXC
"""
return {
etudid: scu.fmt_note(x, keep_numeric=True)
for (etudid, x) in self.evals_notes[evaluation_id].items()
}
def get_evaluation_rattrapage(self, moduleimpl: ModuleImpl): def get_evaluation_rattrapage(self, moduleimpl: ModuleImpl):
"""L'évaluation de rattrapage de ce module, ou None s'il n'en a pas. """L'évaluation de rattrapage de ce module, ou None s'il n'en a pas.
Rattrapage: la moyenne du module est la meilleure note entre moyenne Rattrapage: la moyenne du module est la meilleure note entre moyenne

View File

@ -29,15 +29,6 @@ class Absence(db.Model):
# XXX TODO: contrainte ajoutée: vérifier suppression du module # XXX TODO: contrainte ajoutée: vérifier suppression du module
# (mettre à NULL sans supprimer) # (mettre à NULL sans supprimer)
def __init__(self, id, etudid, jour, estabs, estjust, matin, description):
self.id = id
self.etudid = etudid
self.jour = jour
self.estabs = estabs
self.estjust = estjust
self.matin = matin
self.description = description
def to_dict(self): def to_dict(self):
data = { data = {
"id": self.id, "id": self.id,

View File

@ -83,40 +83,6 @@ class ApcReferentielCompetences(db.Model, XMLModel):
) )
formations = db.relationship("Formation", backref="referentiel_competence") formations = db.relationship("Formation", backref="referentiel_competence")
def __init__(
self,
id,
dept_id,
annexe,
specialite,
specialite_long,
type_titre,
type_structure,
type_departement,
version_orebut,
_xml_attribs,
#scodoc_date_loaded,
scodoc_orig_filename,
# competences,
# parcours,
# formations,
):
self.id = id
self.dept_id = dept_id
self.annexe = annexe
self.specialite = specialite
self.specialite_long = specialite_long
self.type_titre = type_titre
self.type_structure = type_structure
self.type_departement = type_departement
self.version_orebut = version_orebut
self._xml_attribs = _xml_attribs
#self.scodoc_date_loaded = scodoc_date_loaded
self.scodoc_orig_filename = scodoc_orig_filename
# self.competences = competences
# self.parcours = parcours
# self.formations = formations
def __repr__(self): def __repr__(self):
return f"<ApcReferentielCompetences {self.id} {self.specialite!r} {self.departement!r}>" return f"<ApcReferentielCompetences {self.id} {self.specialite!r} {self.departement!r}>"
@ -225,32 +191,6 @@ class ApcCompetence(db.Model, XMLModel):
cascade="all, delete-orphan", cascade="all, delete-orphan",
) )
def __init__(
self,
id,
referentiel_id,
id_orebut,
titre,
titre_long,
couleur,
numero,
_xml_attribs,
situations,
composantes_essentielles,
niveaux,
):
self.id = id
self.referentiel_id = referentiel_id
self.id_orebut = id_orebut
self.titre = titre
self.titre_long = titre_long
self.couleur = couleur
self.numero = numero
self._xml_attribs = _xml_attribs
self.situations = situations
self.composantes_essentielles = composantes_essentielles
self.niveaux = niveaux
def __repr__(self): def __repr__(self):
return f"<ApcCompetence {self.id} {self.titre!r}>" return f"<ApcCompetence {self.id} {self.titre!r}>"
@ -288,12 +228,6 @@ class ApcSituationPro(db.Model, XMLModel):
) )
libelle = db.Column(db.Text(), nullable=False) libelle = db.Column(db.Text(), nullable=False)
# aucun attribut (le text devient le libellé) # aucun attribut (le text devient le libellé)
def __init__(self, id, competence_id, libelle):
self.id = id
self.competence_id = competence_id
self.libelle = libelle
def to_dict(self): def to_dict(self):
return {"libelle": self.libelle} return {"libelle": self.libelle}
@ -306,11 +240,6 @@ class ApcComposanteEssentielle(db.Model, XMLModel):
) )
libelle = db.Column(db.Text(), nullable=False) libelle = db.Column(db.Text(), nullable=False)
def __init__(self, id, competence_id, libelle):
self.id = id
self.competence_id = competence_id
self.libelle = libelle
def to_dict(self): def to_dict(self):
return {"libelle": self.libelle} return {"libelle": self.libelle}
@ -339,14 +268,6 @@ class ApcNiveau(db.Model, XMLModel):
) )
ues = db.relationship("UniteEns", back_populates="niveau_competence") ues = db.relationship("UniteEns", back_populates="niveau_competence")
def __init__(self, id, competence_id, libelle, annee, ordre, app_critiques):
self.id = id
self.competence_id = competence_id
self.libelle = libelle
self.annee = annee
self.ordre = ordre
self.app_critiques = app_critiques
def __repr__(self): def __repr__(self):
return f"""<{self.__class__.__name__} {self.id} ordre={self.ordre!r} annee={ return f"""<{self.__class__.__name__} {self.id} ordre={self.ordre!r} annee={
self.annee!r} {self.competence!r}>""" self.annee!r} {self.competence!r}>"""
@ -452,13 +373,6 @@ class ApcAppCritique(db.Model, XMLModel):
query = query.filter(ApcNiveau.competence == competence) query = query.filter(ApcNiveau.competence == competence)
return query return query
def __init__(self, id, niveau_id, code, libelle, modules):
self.id = id
self.niveau_id = niveau_id
self.code = code
self.libelle = libelle
self.modules = modules
def to_dict(self) -> dict: def to_dict(self) -> dict:
return {"libelle": self.libelle} return {"libelle": self.libelle}
@ -517,14 +431,6 @@ class ApcParcours(db.Model, XMLModel):
cascade="all, delete-orphan", cascade="all, delete-orphan",
) )
def __init__(self, id, referentiel_id, numero, code, libelle, annes):
self.id = id
self.referentiel_id = referentiel_id
self.numero = numero
self.code = code
self.libelle = libelle
self.annes = annes
def __repr__(self): def __repr__(self):
return f"<{self.__class__.__name__} {self.id} {self.code!r} ref={self.referentiel}>" return f"<{self.__class__.__name__} {self.id} {self.code!r} ref={self.referentiel}>"
@ -545,11 +451,6 @@ class ApcAnneeParcours(db.Model, XMLModel):
ordre = db.Column(db.Integer) ordre = db.Column(db.Integer)
"numéro de l'année: 1, 2, 3" "numéro de l'année: 1, 2, 3"
def __init__(self, id, parcours_id, ordre):
self.id = id
self.parcours_id = parcours_id
self.ordre = ordre
def __repr__(self): def __repr__(self):
return f"<{self.__class__.__name__} {self.id} ordre={self.ordre!r} parcours={self.parcours.code!r}>" return f"<{self.__class__.__name__} {self.id} ordre={self.ordre!r} parcours={self.parcours.code!r}>"

View File

@ -654,11 +654,6 @@ class FormSemestreEtape(db.Model):
# etape_apo aurait du etre not null, mais oublié # etape_apo aurait du etre not null, mais oublié
etape_apo = db.Column(db.String(APO_CODE_STR_LEN), index=True) etape_apo = db.Column(db.String(APO_CODE_STR_LEN), index=True)
def __init__(self, id, formsemestre_id, etape_apo):
self.id = id
self.formsemestre_id = formsemestre_id
self.etape_apo = etape_apo
def __bool__(self): def __bool__(self):
"Etape False if code empty" "Etape False if code empty"
return self.etape_apo is not None and (len(self.etape_apo) > 0) return self.etape_apo is not None and (len(self.etape_apo) > 0)

View File

@ -58,12 +58,9 @@ class Partition(db.Model):
def to_dict(self, with_groups=False) -> dict: def to_dict(self, with_groups=False) -> dict:
"""as a dict, with or without groups""" """as a dict, with or without groups"""
d = { d = dict(self.__dict__)
"id": self.id, d.pop("_sa_instance_state", None)
"formsemestre_id": self.partition_id,
"name": self.partition_name,
"numero": self.numero,
}
if with_groups: if with_groups:
d["groups"] = [group.to_dict(with_partition=False) for group in self.groups] d["groups"] = [group.to_dict(with_partition=False) for group in self.groups]
return d return d

View File

@ -47,24 +47,11 @@ class NotesNotes(db.Model):
date = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
uid = db.Column(db.Integer, db.ForeignKey("user.id")) uid = db.Column(db.Integer, db.ForeignKey("user.id"))
def __init__(self, etudid, evaluation_id, value, comment, date, uid): def to_dict(self) -> dict:
self.etudid = etudid "dict"
self.evaluation_id = evaluation_id d = dict(self.__dict__)
self.value = value d.pop("_sa_instance_state", None)
self.comment = comment return d
self.date = date
self.uid = uid
def to_dict(self):
return {
"id": self.id,
"etudid": self.etudid,
"evaluation_id": self.evaluation_id,
"value": self.value,
"comment": self.comment,
"date": self.date,
"uid": self.uid,
}
class NotesNotesLog(db.Model): class NotesNotesLog(db.Model):

View File

@ -136,7 +136,7 @@ def get_partition(partition_id):
return r[0] return r[0]
def get_partitions_list(formsemestre_id, with_default=True): def get_partitions_list(formsemestre_id, with_default=True) -> list[dict]:
"""Liste des partitions pour ce semestre (list of dicts)""" """Liste des partitions pour ce semestre (list of dicts)"""
partitions = ndb.SimpleDictFetch( partitions = ndb.SimpleDictFetch(
"""SELECT p.id AS partition_id, p.* """SELECT p.id AS partition_id, p.*
@ -146,9 +146,9 @@ def get_partitions_list(formsemestre_id, with_default=True):
{"formsemestre_id": formsemestre_id}, {"formsemestre_id": formsemestre_id},
) )
# Move 'all' at end of list (for menus) # Move 'all' at end of list (for menus)
R = [p for p in partitions if p["partition_name"] != None] R = [p for p in partitions if p["partition_name"] is not None]
if with_default: if with_default:
R += [p for p in partitions if p["partition_name"] == None] R += [p for p in partitions if p["partition_name"] is None]
return R return R