forked from ScoDoc/ScoDoc
Merge branch 'master' of https://scodoc.org/git/viennet/ScoDoc into dev93
This commit is contained in:
commit
539041fd0d
@ -5,73 +5,62 @@ 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.api.tools import get_etud_from_etudid_or_nip_or_ine
|
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_groups import get_group_members
|
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/absences/etudid/<int:etudid>", methods=["GET"])
|
@bp.route("/absences/etudid/<int:etudid>", methods=["GET"])
|
||||||
@bp.route("/absences/nip/<string:nip>", methods=["GET"])
|
|
||||||
@bp.route("/absences/ine/<string:ine>", methods=["GET"])
|
|
||||||
@token_auth.login_required
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def absences(etudid: int = None, nip: int = None, ine: int = None):
|
def absences(etudid: int = None):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des absences d'un étudiant donné
|
Retourne la liste des absences d'un étudiant donné
|
||||||
|
|
||||||
etudid : l'etudid d'un étudiant
|
etudid : l'etudid d'un étudiant
|
||||||
nip: le code nip d'un étudiant
|
|
||||||
ine : le code ine d'un étudiant
|
|
||||||
|
|
||||||
Exemple de résultat:
|
Exemple de résultat:
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"jour": "2022-04-15",
|
"jour": "2022-04-15",
|
||||||
"matin": true,
|
"matin": true,
|
||||||
"estabs": true,
|
"estabs": true,
|
||||||
"estjust": true,
|
"estjust": true,
|
||||||
"description": "",
|
"description": "",
|
||||||
"begin": "2022-04-15 08:00:00",
|
"begin": "2022-04-15 08:00:00",
|
||||||
"end": "2022-04-15 11:59:59"
|
"end": "2022-04-15 11:59:59"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"jour": "2022-04-15",
|
"jour": "2022-04-15",
|
||||||
"matin": false,
|
"matin": false,
|
||||||
"estabs": true,
|
"estabs": true,
|
||||||
"estjust": false,
|
"estjust": false,
|
||||||
"description": "",
|
"description": "",
|
||||||
"begin": "2022-04-15 12:00:00",
|
"begin": "2022-04-15 12:00:00",
|
||||||
"end": "2022-04-15 17:59:59"
|
"end": "2022-04-15 17:59:59"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
if etudid is None:
|
etud = Identite.query.get(etudid)
|
||||||
# Récupération de l'étudiant
|
if etud is None:
|
||||||
etud = get_etud_from_etudid_or_nip_or_ine(etudid, nip, ine)
|
return error_response(
|
||||||
if etud is None:
|
404,
|
||||||
return error_response(
|
message="id de l'étudiant (etudid, nip, ine) inconnu",
|
||||||
404,
|
)
|
||||||
message="id de l'étudiant (etudid, nip, ine) inconnu",
|
# Absences de l'étudiant
|
||||||
)
|
|
||||||
etudid = etud.etudid
|
|
||||||
|
|
||||||
# Récupération des absences de l'étudiant
|
|
||||||
ndb.open_db_connection()
|
ndb.open_db_connection()
|
||||||
absences = sco_abs.list_abs_date(etudid)
|
absences = sco_abs.list_abs_date(etud.id)
|
||||||
for absence in absences:
|
for absence in absences:
|
||||||
absence["jour"] = absence["jour"].isoformat()
|
absence["jour"] = absence["jour"].isoformat()
|
||||||
return jsonify(absences)
|
return jsonify(absences)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/absences/etudid/<int:etudid>/just", methods=["GET"])
|
@bp.route("/absences/etudid/<int:etudid>/just", methods=["GET"])
|
||||||
@bp.route("/absences/nip/<int:nip>/just", methods=["GET"])
|
|
||||||
@bp.route("/absences/ine/<int:ine>/just", methods=["GET"])
|
|
||||||
@token_auth.login_required
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def absences_just(etudid: int = None, nip: int = None, ine: int = None):
|
def absences_just(etudid: int = None):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des absences justifiées d'un étudiant donné
|
Retourne la liste des absences justifiées d'un étudiant donné
|
||||||
|
|
||||||
@ -80,39 +69,37 @@ def absences_just(etudid: int = None, nip: int = None, ine: int = None):
|
|||||||
ine : le code ine d'un étudiant
|
ine : le code ine d'un étudiant
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"jour": "2022-04-15",
|
"jour": "2022-04-15",
|
||||||
"matin": true,
|
"matin": true,
|
||||||
"estabs": true,
|
"estabs": true,
|
||||||
"estjust": true,
|
"estjust": true,
|
||||||
"description": "",
|
"description": "",
|
||||||
"begin": "2022-04-15 08:00:00",
|
"begin": "2022-04-15 08:00:00",
|
||||||
"end": "2022-04-15 11:59:59"
|
"end": "2022-04-15 11:59:59"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"jour": "Fri, 15 Apr 2022 00:00:00 GMT",
|
"jour": "Fri, 15 Apr 2022 00:00:00 GMT",
|
||||||
"matin": false,
|
"matin": false,
|
||||||
"estabs": true,
|
"estabs": true,
|
||||||
"estjust": true,
|
"estjust": true,
|
||||||
"description": "",
|
"description": "",
|
||||||
"begin": "2022-04-15 12:00:00",
|
"begin": "2022-04-15 12:00:00",
|
||||||
"end": "2022-04-15 17:59:59"
|
"end": "2022-04-15 17:59:59"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
if etudid is None:
|
etud = Identite.query.get(etudid)
|
||||||
etud = get_etud_from_etudid_or_nip_or_ine(etudid, nip, ine)
|
if etud is None:
|
||||||
if etud is None:
|
return error_response(
|
||||||
return error_response(
|
404,
|
||||||
404,
|
message="id de l'étudiant (etudid, nip, ine) inconnu",
|
||||||
message="id de l'étudiant (etudid, nip, ine) inconnu",
|
)
|
||||||
)
|
|
||||||
etudid = etud.etudid
|
|
||||||
|
|
||||||
# Récupération des absences justifiées de l'étudiant
|
# Absences justifiées de l'étudiant
|
||||||
abs_just = [
|
abs_just = [
|
||||||
absence for absence in sco_abs.list_abs_date(etudid) if absence["estjust"]
|
absence for absence in sco_abs.list_abs_date(etud.id) if absence["estjust"]
|
||||||
]
|
]
|
||||||
for absence in abs_just:
|
for absence in abs_just:
|
||||||
absence["jour"] = absence["jour"].isoformat()
|
absence["jour"] = absence["jour"].isoformat()
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
#################################################### Etudiants ########################################################
|
##############################################################################
|
||||||
|
# ScoDoc
|
||||||
|
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
|
||||||
|
# See LICENSE
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
API : accès aux étudiants
|
||||||
|
"""
|
||||||
|
|
||||||
from flask import jsonify
|
from flask import jsonify
|
||||||
|
|
||||||
import app
|
import app
|
||||||
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.api.tools import get_etud_from_etudid_or_nip_or_ine
|
from app.models import Departement, FormSemestreInscription, FormSemestre, Identite
|
||||||
from app.models import 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_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
@ -59,53 +65,102 @@ def etudiants_courant(long=False):
|
|||||||
@bp.route("/etudiant/ine/<string:ine>", methods=["GET"])
|
@bp.route("/etudiant/ine/<string:ine>", methods=["GET"])
|
||||||
@token_auth.login_required
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def etudiant(etudid: int = None, nip: int = None, ine: int = None):
|
def etudiant(etudid: int = None, nip: str = None, ine: str = None):
|
||||||
"""
|
"""
|
||||||
Retourne les informations de l'étudiant correspondant à l'id passé en paramètres.
|
Retourne les informations de l'étudiant correspondant, ou 404 si non trouvé.
|
||||||
|
|
||||||
etudid : l'etudid d'un étudiant
|
etudid : l'etudid de l'étudiant
|
||||||
nip : le code nip d'un étudiant
|
nip : le code nip de l'étudiant
|
||||||
ine : le code ine d'un étudiant
|
ine : le code ine de l'étudiant
|
||||||
|
|
||||||
|
Les codes INE et NIP sont uniques au sein d'un département.
|
||||||
|
Si plusieurs objets ont le même code, on ramène le plus récemment inscrit.
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
{
|
{
|
||||||
"civilite": "X",
|
"civilite": "X",
|
||||||
"code_ine": "1",
|
"code_ine": "1",
|
||||||
"code_nip": "1",
|
"code_nip": "1",
|
||||||
"date_naissance": "",
|
"date_naissance": "",
|
||||||
"email": "SACHA.COSTA@example.com",
|
"email": "SACHA.COSTA@example.com",
|
||||||
"emailperso": "",
|
"emailperso": "",
|
||||||
"etudid": 1,
|
"etudid": 1,
|
||||||
"nom": "COSTA",
|
"nom": "COSTA",
|
||||||
"prenom": "SACHA",
|
"prenom": "SACHA",
|
||||||
"nomprenom": "Sacha COSTA",
|
"nomprenom": "Sacha COSTA",
|
||||||
"lieu_naissance": "",
|
"lieu_naissance": "",
|
||||||
"dept_naissance": "",
|
"dept_naissance": "",
|
||||||
"nationalite": "",
|
"nationalite": "",
|
||||||
"boursier": "",
|
"boursier": "",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"codepostaldomicile": "",
|
"codepostaldomicile": "",
|
||||||
"paysdomicile": "",
|
"paysdomicile": "",
|
||||||
"telephonemobile": "",
|
"telephonemobile": "",
|
||||||
"typeadresse": "domicile",
|
"typeadresse": "domicile",
|
||||||
"domicile": "",
|
"domicile": "",
|
||||||
"villedomicile": "",
|
"villedomicile": "",
|
||||||
"telephone": "",
|
"telephone": "",
|
||||||
"fax": "",
|
"fax": "",
|
||||||
"description": ""
|
"description": ""
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
# Récupération de l'étudiant
|
if etudid is not None:
|
||||||
etud = get_etud_from_etudid_or_nip_or_ine(etudid, nip, ine)
|
etud = Identite.query.get(etudid)
|
||||||
|
else:
|
||||||
|
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:
|
||||||
|
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()
|
||||||
|
|
||||||
if etud is None:
|
if etud is None:
|
||||||
return error_response(
|
return error_response(
|
||||||
404,
|
404,
|
||||||
message="id de l'étudiant (etudid, nip, ine) inconnu",
|
message="étudiant inconnu",
|
||||||
)
|
)
|
||||||
# Mise en forme des données
|
|
||||||
data = etud.to_dict_bul(include_urls=False)
|
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(etud.to_dict_bul(include_urls=False))
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/etudiants/etudid/<int:etudid>", methods=["GET"])
|
||||||
|
@bp.route("/etudiants/nip/<string:nip>", methods=["GET"])
|
||||||
|
@bp.route("/etudiants/ine/<string:ine>", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
|
@token_permission_required(Permission.APIView)
|
||||||
|
def etudiants(etudid: int = None, nip: str = None, ine: str = None):
|
||||||
|
"""
|
||||||
|
Info sur le ou les étudiants correspondant. Comme /etudiant mais renvoie
|
||||||
|
toujours une liste.
|
||||||
|
Si non trouvé, liste vide, pas d'erreur.
|
||||||
|
Dans 99% des cas, la liste contient un seul étudiant, mais si l'étudiant a
|
||||||
|
été inscrit dans plusieurs départements, on a plusieurs objets (1 par dept.).
|
||||||
|
"""
|
||||||
|
if etudid is not None:
|
||||||
|
query = Identite.query.filter_by(id=etudid)
|
||||||
|
elif 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:
|
||||||
|
return error_response(
|
||||||
|
404,
|
||||||
|
message="parametre manquant",
|
||||||
|
)
|
||||||
|
|
||||||
|
return jsonify([etud.to_dict_bul(include_urls=False) for etud in query])
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/etudiant/etudid/<int:etudid>/formsemestres")
|
@bp.route("/etudiant/etudid/<int:etudid>/formsemestres")
|
||||||
@ -115,56 +170,65 @@ def etudiant(etudid: int = None, nip: int = None, ine: int = None):
|
|||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None):
|
def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des semestres qu'un étudiant a suivis, triés par ordre chronologique.
|
Liste des semestres qu'un étudiant a suivi, triés par ordre chronologique.
|
||||||
|
Accès par etudid, nip ou ine
|
||||||
etudid : l'etudid d'un étudiant
|
|
||||||
nip : le code nip d'un étudiant
|
|
||||||
ine : le code ine d'un étudiant
|
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"date_fin": "31/08/2022",
|
"date_fin": "31/08/2022",
|
||||||
"resp_can_edit": false,
|
"resp_can_edit": false,
|
||||||
"dept_id": 1,
|
"dept_id": 1,
|
||||||
"etat": true,
|
"etat": true,
|
||||||
"resp_can_change_ens": true,
|
"resp_can_change_ens": true,
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"modalite": "FI",
|
"modalite": "FI",
|
||||||
"ens_can_edit_eval": false,
|
"ens_can_edit_eval": false,
|
||||||
"formation_id": 1,
|
"formation_id": 1,
|
||||||
"gestion_compensation": false,
|
"gestion_compensation": false,
|
||||||
"elt_sem_apo": null,
|
"elt_sem_apo": null,
|
||||||
"semestre_id": 1,
|
"semestre_id": 1,
|
||||||
"bul_hide_xml": false,
|
"bul_hide_xml": false,
|
||||||
"elt_annee_apo": null,
|
"elt_annee_apo": null,
|
||||||
"titre": "Semestre test",
|
"titre": "Semestre test",
|
||||||
"block_moyennes": false,
|
"block_moyennes": false,
|
||||||
"scodoc7_id": null,
|
"scodoc7_id": null,
|
||||||
"date_debut": "01/09/2021",
|
"date_debut": "01/09/2021",
|
||||||
"gestion_semestrielle": false,
|
"gestion_semestrielle": false,
|
||||||
"bul_bgcolor": "white",
|
"bul_bgcolor": "white",
|
||||||
"formsemestre_id": 1,
|
"formsemestre_id": 1,
|
||||||
"titre_num": "Semestre test semestre 1",
|
"titre_num": "Semestre test semestre 1",
|
||||||
"date_debut_iso": "2021-09-01",
|
"date_debut_iso": "2021-09-01",
|
||||||
"date_fin_iso": "2022-08-31",
|
"date_fin_iso": "2022-08-31",
|
||||||
"responsables": []
|
"responsables": []
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
# Récupération de l'étudiant
|
if etudid is not None:
|
||||||
etud = get_etud_from_etudid_or_nip_or_ine(etudid, nip, ine)
|
query = FormSemestre.query.filter(
|
||||||
if etud is None:
|
FormSemestreInscription.etudid == etudid,
|
||||||
|
FormSemestreInscription.formsemestre_id == FormSemestre.id,
|
||||||
|
)
|
||||||
|
elif nip is not None:
|
||||||
|
query = FormSemestre.query.filter(
|
||||||
|
Identite.code_nip == nip,
|
||||||
|
FormSemestreInscription.etudid == Identite.id,
|
||||||
|
FormSemestreInscription.formsemestre_id == FormSemestre.id,
|
||||||
|
)
|
||||||
|
elif ine is not None:
|
||||||
|
query = FormSemestre.query.filter(
|
||||||
|
Identite.code_ine == ine,
|
||||||
|
FormSemestreInscription.etudid == Identite.id,
|
||||||
|
FormSemestreInscription.formsemestre_id == FormSemestre.id,
|
||||||
|
)
|
||||||
|
else:
|
||||||
return error_response(
|
return error_response(
|
||||||
404,
|
404,
|
||||||
message="id de l'étudiant (etudid, nip, ine) inconnu",
|
message="parametre manquant",
|
||||||
)
|
)
|
||||||
|
|
||||||
formsemestres = models.FormSemestre.query.filter(
|
formsemestres = query.order_by(FormSemestre.date_debut)
|
||||||
models.FormSemestreInscription.etudid == etud.id,
|
|
||||||
models.FormSemestreInscription.formsemestre_id == models.FormSemestre.id,
|
|
||||||
).order_by(models.FormSemestre.date_debut)
|
|
||||||
|
|
||||||
return jsonify([formsemestre.to_dict() for formsemestre in formsemestres])
|
return jsonify([formsemestre.to_dict() for formsemestre in formsemestres])
|
||||||
|
|
||||||
@ -204,8 +268,8 @@ def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None)
|
|||||||
def etudiant_bulletin_semestre(
|
def etudiant_bulletin_semestre(
|
||||||
formsemestre_id,
|
formsemestre_id,
|
||||||
etudid: int = None,
|
etudid: int = None,
|
||||||
nip: int = None,
|
nip: str = None,
|
||||||
ine: int = None,
|
ine: str = None,
|
||||||
version="long",
|
version="long",
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@ -216,12 +280,12 @@ def etudiant_bulletin_semestre(
|
|||||||
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 :
|
||||||
{
|
{
|
||||||
"version": "0",
|
"version": "0",
|
||||||
"type": "BUT",
|
"type": "BUT",
|
||||||
"date": "2022-04-27T07:18:16.450634Z",
|
"date": "2022-04-27T07:18:16.450634Z",
|
||||||
"publie": true,
|
"publie": true,
|
||||||
"etudiant": {
|
"etudiant": {
|
||||||
"civilite": "X",
|
"civilite": "X",
|
||||||
"code_ine": "1",
|
"code_ine": "1",
|
||||||
"code_nip": "1",
|
"code_nip": "1",
|
||||||
@ -247,17 +311,17 @@ def etudiant_bulletin_semestre(
|
|||||||
"villedomicile": "",
|
"villedomicile": "",
|
||||||
"telephone": "",
|
"telephone": "",
|
||||||
"fax": "",
|
"fax": "",
|
||||||
"description": ""
|
"description": "",
|
||||||
},
|
},
|
||||||
"formation": {
|
"formation": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"acronyme": "BUT R&T",
|
"acronyme": "BUT R&T",
|
||||||
"titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications",
|
"titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications",
|
||||||
"titre": "BUT R&T"
|
"titre": "BUT R&T",
|
||||||
},
|
},
|
||||||
"formsemestre_id": 1,
|
"formsemestre_id": 1,
|
||||||
"etat_inscription": "I",
|
"etat_inscription": "I",
|
||||||
"options": {
|
"options": {
|
||||||
"show_abs": true,
|
"show_abs": true,
|
||||||
"show_abs_modules": false,
|
"show_abs_modules": false,
|
||||||
"show_ects": true,
|
"show_ects": true,
|
||||||
@ -276,128 +340,113 @@ def etudiant_bulletin_semestre(
|
|||||||
"show_temporary": true,
|
"show_temporary": true,
|
||||||
"temporary_txt": "Provisoire",
|
"temporary_txt": "Provisoire",
|
||||||
"show_uevalid": true,
|
"show_uevalid": true,
|
||||||
"show_date_inscr": true
|
"show_date_inscr": true,
|
||||||
},
|
},
|
||||||
"ressources": {
|
"ressources": {
|
||||||
"R101": {
|
"R101": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"titre": "Initiation aux r\u00e9seaux informatiques",
|
"titre": "Initiation aux r\u00e9seaux informatiques",
|
||||||
"code_apogee": null,
|
"code_apogee": null,
|
||||||
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=1",
|
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=1",
|
||||||
"moyenne": {},
|
"moyenne": {},
|
||||||
"evaluations": [
|
"evaluations": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"description": "eval1",
|
"description": "eval1",
|
||||||
"date": "2022-04-20",
|
"date": "2022-04-20",
|
||||||
"heure_debut": "08:00",
|
"heure_debut": "08:00",
|
||||||
"heure_fin": "09:00",
|
"heure_fin": "09:00",
|
||||||
"coef": "01.00",
|
"coef": "01.00",
|
||||||
"poids": {
|
"poids": {
|
||||||
"RT1.1": 1.0,
|
"RT1.1": 1.0,
|
||||||
},
|
},
|
||||||
"note": {
|
"note": {
|
||||||
"value": "12.00",
|
"value": "12.00",
|
||||||
"min": "00.00",
|
"min": "00.00",
|
||||||
"max": "18.00",
|
"max": "18.00",
|
||||||
"moy": "10.88"
|
"moy": "10.88",
|
||||||
},
|
},
|
||||||
"url": "/ScoDoc/TAPI/Scolarite/Notes/evaluation_listenotes?evaluation_id=1"
|
"url": "/ScoDoc/TAPI/Scolarite/Notes/evaluation_listenotes?evaluation_id=1",
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"saes": {
|
"saes": {
|
||||||
"SAE11": {
|
"SAE11": {
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"titre": "Se sensibiliser \u00e0 l'hygi\u00e8ne informatique et \u00e0 la cybers\u00e9curit\u00e9",
|
"titre": "Se sensibiliser \u00e0 l'hygi\u00e8ne informatique et \u00e0 la cybers\u00e9curit\u00e9",
|
||||||
"code_apogee": null,
|
"code_apogee": null,
|
||||||
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=2",
|
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=2",
|
||||||
"moyenne": {},
|
"moyenne": {},
|
||||||
"evaluations": []
|
"evaluations": [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ues": {
|
"ues": {
|
||||||
"RT1.1": {
|
"RT1.1": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"titre": "Administrer les r\u00e9seaux et l\u2019Internet",
|
"titre": "Administrer les r\u00e9seaux et l\u2019Internet",
|
||||||
"numero": 1,
|
"numero": 1,
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"color": "#B80004",
|
"color": "#B80004",
|
||||||
"competence": null,
|
"competence": null,
|
||||||
"moyenne": {
|
"moyenne": {
|
||||||
"value": "08.50",
|
"value": "08.50",
|
||||||
"min": "06.00",
|
"min": "06.00",
|
||||||
"max": "16.50",
|
"max": "16.50",
|
||||||
"moy": "11.31",
|
"moy": "11.31",
|
||||||
"rang": "12",
|
"rang": "12",
|
||||||
"total": 16
|
"total": 16,
|
||||||
},
|
|
||||||
"bonus": "00.00",
|
|
||||||
"malus": "00.00",
|
|
||||||
"capitalise": null,
|
|
||||||
"ressources": {
|
|
||||||
"R101": {
|
|
||||||
"id": 1,
|
|
||||||
"coef": 12.0,
|
|
||||||
"moyenne": "12.00"
|
|
||||||
},
|
},
|
||||||
},
|
"bonus": "00.00",
|
||||||
"saes": {
|
"malus": "00.00",
|
||||||
"SAE11": {
|
"capitalise": null,
|
||||||
"id": 2,
|
"ressources": {
|
||||||
"coef": 16.0,
|
"R101": {"id": 1, "coef": 12.0, "moyenne": "12.00"},
|
||||||
"moyenne": "~"
|
|
||||||
},
|
},
|
||||||
},
|
"saes": {
|
||||||
"ECTS": {
|
"SAE11": {"id": 2, "coef": 16.0, "moyenne": "~"},
|
||||||
"acquis": 0.0,
|
},
|
||||||
"total": 12.0
|
"ECTS": {"acquis": 0.0, "total": 12.0},
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"semestre": {
|
"semestre": {
|
||||||
"etapes": [],
|
"etapes": [],
|
||||||
"date_debut": "2021-09-01",
|
"date_debut": "2021-09-01",
|
||||||
"date_fin": "2022-08-31",
|
"date_fin": "2022-08-31",
|
||||||
"annee_universitaire": "2021 - 2022",
|
"annee_universitaire": "2021 - 2022",
|
||||||
"numero": 1,
|
"numero": 1,
|
||||||
"inscription": "",
|
"inscription": "",
|
||||||
"groupes": [],
|
"groupes": [],
|
||||||
"absences": {
|
"absences": {"injustifie": 1, "total": 2},
|
||||||
"injustifie": 1,
|
"ECTS": {"acquis": 0, "total": 30.0},
|
||||||
"total": 2
|
"notes": {"value": "10.60", "min": "02.40", "moy": "11.05", "max": "17.40"},
|
||||||
|
"rang": {"value": "10", "total": 16},
|
||||||
},
|
},
|
||||||
"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 = FormSemestre.query.filter_by(id=formsemestre_id).first_or_404()
|
||||||
id=formsemestre_id
|
dept = Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
|
||||||
).first_or_404()
|
|
||||||
|
|
||||||
dept = models.Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
|
if etudid is not None:
|
||||||
|
query = Identite.query.filter_by(id=etudid)
|
||||||
|
elif nip is not None:
|
||||||
|
query = Identite.query.filter_by(code_nip=nip, dept_id=dept.id)
|
||||||
|
elif ine is not None:
|
||||||
|
query = Identite.query.filter_by(code_ine=ine, dept_id=dept.id)
|
||||||
|
else:
|
||||||
|
return error_response(
|
||||||
|
404,
|
||||||
|
message="parametre manquant",
|
||||||
|
)
|
||||||
|
|
||||||
app.set_sco_dept(dept.acronym)
|
etud = query.first()
|
||||||
|
|
||||||
etud = get_etud_from_etudid_or_nip_or_ine(etudid, nip, ine)
|
|
||||||
if etud is None:
|
if etud is None:
|
||||||
return error_response(
|
return error_response(
|
||||||
404,
|
404,
|
||||||
message="id de l'étudiant (etudid, nip, ine) inconnu",
|
message="id de l'étudiant (etudid, nip, ine) inconnu",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
app.set_sco_dept(dept.acronym)
|
||||||
return sco_bulletins.get_formsemestre_bulletin_etud_json(
|
return sco_bulletins.get_formsemestre_bulletin_etud_json(
|
||||||
formsemestre, etud, version
|
formsemestre, etud, version
|
||||||
)
|
)
|
||||||
@ -429,44 +478,57 @@ def etudiant_groups(
|
|||||||
ine : le code ine d'un étudiant
|
ine : le code ine d'un étudiant
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"partition_id": 1,
|
"partition_id": 1,
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"formsemestre_id": 1,
|
"formsemestre_id": 1,
|
||||||
"partition_name": null,
|
"partition_name": null,
|
||||||
"numero": 0,
|
"numero": 0,
|
||||||
"bul_show_rank": false,
|
"bul_show_rank": false,
|
||||||
"show_in_lists": true,
|
"show_in_lists": true,
|
||||||
"group_id": 1,
|
"group_id": 1,
|
||||||
"group_name": null
|
"group_name": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"partition_id": 2,
|
"partition_id": 2,
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"formsemestre_id": 1,
|
"formsemestre_id": 1,
|
||||||
"partition_name": "TD",
|
"partition_name": "TD",
|
||||||
"numero": 1,
|
"numero": 1,
|
||||||
"bul_show_rank": false,
|
"bul_show_rank": false,
|
||||||
"show_in_lists": true,
|
"show_in_lists": true,
|
||||||
"group_id": 2,
|
"group_id": 2,
|
||||||
"group_name": "A"
|
"group_name": "A"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
if etudid is None:
|
|
||||||
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",
|
|
||||||
)
|
|
||||||
etudid = etud.etudid
|
|
||||||
|
|
||||||
# Récupération du formsemestre
|
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first()
|
||||||
sem = models.FormSemestre.query.filter_by(id=formsemestre_id).first_or_404()
|
if formsemestre is None:
|
||||||
dept = models.Departement.query.get(sem.dept_id)
|
return error_response(
|
||||||
|
404,
|
||||||
|
message="formsemestre inconnu",
|
||||||
|
)
|
||||||
|
dept = Departement.query.get(formsemestre.dept_id)
|
||||||
|
if etudid is not None:
|
||||||
|
query = Identite.query.filter_by(id=etudid)
|
||||||
|
elif nip is not None:
|
||||||
|
query = Identite.query.filter_by(code_nip=nip, dept_id=dept.id)
|
||||||
|
elif ine is not None:
|
||||||
|
query = Identite.query.filter_by(code_ine=ine, dept_id=dept.id)
|
||||||
|
else:
|
||||||
|
return error_response(
|
||||||
|
404,
|
||||||
|
message="parametre manquant",
|
||||||
|
)
|
||||||
|
etud = query.first()
|
||||||
|
if etud is None:
|
||||||
|
return error_response(
|
||||||
|
404,
|
||||||
|
message="etudiant inconnu",
|
||||||
|
)
|
||||||
app.set_sco_dept(dept.acronym)
|
app.set_sco_dept(dept.acronym)
|
||||||
data = sco_groups.get_etud_groups(etudid, sem.id)
|
data = sco_groups.get_etud_groups(etud.id, formsemestre.id)
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
@ -4,6 +4,7 @@ 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
|
||||||
@ -46,7 +47,7 @@ def evaluations(moduleimpl_id: int):
|
|||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
# Récupération de toutes les évaluations
|
# Récupération de toutes les évaluations
|
||||||
evals = models.Evaluation.query.filter_by(id=moduleimpl_id)
|
evals = Evaluation.query.filter_by(id=moduleimpl_id)
|
||||||
|
|
||||||
# Mise en forme des données
|
# Mise en forme des données
|
||||||
data = [d.to_dict() for d in evals]
|
data = [d.to_dict() for d in evals]
|
||||||
|
@ -59,6 +59,7 @@ def formsemestre(formsemestre_id: int):
|
|||||||
# pour accéder aux préferences
|
# pour accéder aux préferences
|
||||||
dept = Departement.query.get(formsemestre.dept_id)
|
dept = Departement.query.get(formsemestre.dept_id)
|
||||||
app.set_sco_dept(dept.acronym)
|
app.set_sco_dept(dept.acronym)
|
||||||
|
data["annee_scolaire"] = formsemestre.annee_scolaire_str()
|
||||||
data["session_id"] = formsemestre.session_id()
|
data["session_id"] = formsemestre.session_id()
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
|
@ -348,7 +348,7 @@ class User(UserMixin, db.Model):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def get_nom_fmt(self):
|
def get_nom_fmt(self):
|
||||||
"""Nom formatté: "Martin" """
|
"""Nom formaté: "Martin" """
|
||||||
if self.nom:
|
if self.nom:
|
||||||
return sco_etud.format_nom(self.nom, uppercase=False)
|
return sco_etud.format_nom(self.nom, uppercase=False)
|
||||||
else:
|
else:
|
||||||
|
@ -14,10 +14,12 @@ from flask import url_for, g
|
|||||||
|
|
||||||
from app.comp.res_but import ResultatsSemestreBUT
|
from app.comp.res_but import ResultatsSemestreBUT
|
||||||
from app.models import FormSemestre, Identite
|
from app.models import FormSemestre, Identite
|
||||||
|
from app.models.groups import GroupDescr
|
||||||
from app.models.ues import UniteEns
|
from app.models.ues import UniteEns
|
||||||
from app.scodoc import sco_bulletins, sco_utils as scu
|
from app.scodoc import sco_bulletins, sco_utils as scu
|
||||||
from app.scodoc import sco_bulletins_json
|
from app.scodoc import sco_bulletins_json
|
||||||
from app.scodoc import sco_bulletins_pdf
|
from app.scodoc import sco_bulletins_pdf
|
||||||
|
from app.scodoc import sco_groups
|
||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
from app.scodoc.sco_codes_parcours import UE_SPORT, DEF
|
from app.scodoc.sco_codes_parcours import UE_SPORT, DEF
|
||||||
from app.scodoc.sco_utils import fmt_note
|
from app.scodoc.sco_utils import fmt_note
|
||||||
@ -64,8 +66,16 @@ class BulletinBUT:
|
|||||||
# }
|
# }
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def etud_ue_results(self, etud: Identite, ue: UniteEns, decision_ue: dict) -> dict:
|
def etud_ue_results(
|
||||||
"dict synthèse résultats UE"
|
self,
|
||||||
|
etud: Identite,
|
||||||
|
ue: UniteEns,
|
||||||
|
decision_ue: dict,
|
||||||
|
etud_groups: list[GroupDescr] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""dict synthèse résultats UE
|
||||||
|
etud_groups : liste des groupes, pour affichage du rang.
|
||||||
|
"""
|
||||||
res = self.res
|
res = self.res
|
||||||
|
|
||||||
d = {
|
d = {
|
||||||
@ -81,7 +91,7 @@ class BulletinBUT:
|
|||||||
if res.bonus_ues is not None and ue.id in res.bonus_ues
|
if res.bonus_ues is not None and ue.id in res.bonus_ues
|
||||||
else fmt_note(0.0),
|
else fmt_note(0.0),
|
||||||
"malus": fmt_note(res.malus[ue.id][etud.id]),
|
"malus": fmt_note(res.malus[ue.id][etud.id]),
|
||||||
"capitalise": None, # "AAAA-MM-JJ" TODO #sco92
|
"capitalise": None, # "AAAA-MM-JJ" TODO #sco93
|
||||||
"ressources": self.etud_ue_mod_results(etud, ue, res.ressources),
|
"ressources": self.etud_ue_mod_results(etud, ue, res.ressources),
|
||||||
"saes": self.etud_ue_mod_results(etud, ue, res.saes),
|
"saes": self.etud_ue_mod_results(etud, ue, res.saes),
|
||||||
}
|
}
|
||||||
@ -103,7 +113,18 @@ class BulletinBUT:
|
|||||||
"moy": fmt_note(res.etud_moy_ue[ue.id].mean()),
|
"moy": fmt_note(res.etud_moy_ue[ue.id].mean()),
|
||||||
"rang": rang,
|
"rang": rang,
|
||||||
"total": effectif, # nb etud avec note dans cette UE
|
"total": effectif, # nb etud avec note dans cette UE
|
||||||
|
"groupes": {},
|
||||||
}
|
}
|
||||||
|
if self.prefs["bul_show_ue_rangs"]:
|
||||||
|
for group in etud_groups:
|
||||||
|
if group.partition.bul_show_rank:
|
||||||
|
rang, effectif = self.res.get_etud_ue_rang(
|
||||||
|
ue.id, etud.id, group.id
|
||||||
|
)
|
||||||
|
d["moyenne"]["groupes"][group.id] = {
|
||||||
|
"value": rang,
|
||||||
|
"total": effectif,
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
# ceci suppose que l'on a une seule UE bonus,
|
# ceci suppose que l'on a une seule UE bonus,
|
||||||
# en tous cas elles auront la même description
|
# en tous cas elles auront la même description
|
||||||
@ -275,6 +296,9 @@ class BulletinBUT:
|
|||||||
return d
|
return d
|
||||||
|
|
||||||
nbabs, nbabsjust = formsemestre.get_abs_count(etud.id)
|
nbabs, nbabsjust = formsemestre.get_abs_count(etud.id)
|
||||||
|
etud_groups = sco_groups.get_etud_formsemestre_groups(
|
||||||
|
etud, formsemestre, only_to_show=True
|
||||||
|
)
|
||||||
semestre_infos = {
|
semestre_infos = {
|
||||||
"etapes": [str(x.etape_apo) for x in formsemestre.etapes if x.etape_apo],
|
"etapes": [str(x.etape_apo) for x in formsemestre.etapes if x.etape_apo],
|
||||||
"date_debut": formsemestre.date_debut.isoformat(),
|
"date_debut": formsemestre.date_debut.isoformat(),
|
||||||
@ -282,7 +306,7 @@ class BulletinBUT:
|
|||||||
"annee_universitaire": formsemestre.annee_scolaire_str(),
|
"annee_universitaire": formsemestre.annee_scolaire_str(),
|
||||||
"numero": formsemestre.semestre_id,
|
"numero": formsemestre.semestre_id,
|
||||||
"inscription": "", # inutilisé mais nécessaire pour le js de Seb.
|
"inscription": "", # inutilisé mais nécessaire pour le js de Seb.
|
||||||
"groupes": [], # XXX TODO
|
"groupes": [group.to_dict() for group in etud_groups],
|
||||||
}
|
}
|
||||||
if self.prefs["bul_show_abs"]:
|
if self.prefs["bul_show_abs"]:
|
||||||
semestre_infos["absences"] = {
|
semestre_infos["absences"] = {
|
||||||
@ -306,15 +330,25 @@ class BulletinBUT:
|
|||||||
"max": fmt_note(res.etud_moy_gen.max()),
|
"max": fmt_note(res.etud_moy_gen.max()),
|
||||||
}
|
}
|
||||||
if self.prefs["bul_show_rangs"] and not np.isnan(res.etud_moy_gen[etud.id]):
|
if self.prefs["bul_show_rangs"] and not np.isnan(res.etud_moy_gen[etud.id]):
|
||||||
# classement wrt moyenne général, indicatif
|
# classement wrt moyenne générale, indicatif
|
||||||
semestre_infos["rang"] = {
|
semestre_infos["rang"] = {
|
||||||
"value": res.etud_moy_gen_ranks[etud.id],
|
"value": res.etud_moy_gen_ranks[etud.id],
|
||||||
"total": nb_inscrits,
|
"total": nb_inscrits,
|
||||||
|
"groupes": {},
|
||||||
}
|
}
|
||||||
|
# Rangs par groupes
|
||||||
|
for group in etud_groups:
|
||||||
|
if group.partition.bul_show_rank:
|
||||||
|
rang, effectif = self.res.get_etud_rang_group(etud.id, group.id)
|
||||||
|
semestre_infos["rang"]["groupes"][group.id] = {
|
||||||
|
"value": rang,
|
||||||
|
"total": effectif,
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
semestre_infos["rang"] = {
|
semestre_infos["rang"] = {
|
||||||
"value": "-",
|
"value": "-",
|
||||||
"total": nb_inscrits,
|
"total": nb_inscrits,
|
||||||
|
"groupes": {},
|
||||||
}
|
}
|
||||||
d.update(
|
d.update(
|
||||||
{
|
{
|
||||||
@ -324,7 +358,10 @@ class BulletinBUT:
|
|||||||
"saes": self.etud_mods_results(etud, res.saes, version=version),
|
"saes": self.etud_mods_results(etud, res.saes, version=version),
|
||||||
"ues": {
|
"ues": {
|
||||||
ue.acronyme: self.etud_ue_results(
|
ue.acronyme: self.etud_ue_results(
|
||||||
etud, ue, decision_ue=decisions_ues.get(ue.id, {})
|
etud,
|
||||||
|
ue,
|
||||||
|
decision_ue=decisions_ues.get(ue.id, {}),
|
||||||
|
etud_groups=etud_groups,
|
||||||
)
|
)
|
||||||
for ue in res.ues
|
for ue in res.ues
|
||||||
# si l'UE comporte des modules auxquels on est inscrit:
|
# si l'UE comporte des modules auxquels on est inscrit:
|
||||||
|
@ -481,6 +481,19 @@ class BonusBezier(BonusSportAdditif):
|
|||||||
proportion_point = 0.03
|
proportion_point = 0.03
|
||||||
|
|
||||||
|
|
||||||
|
class BonusBlagnac(BonusSportAdditif):
|
||||||
|
"""Calcul bonus modules optionnels (sport, culture), règle IUT de Blagnac.
|
||||||
|
|
||||||
|
Le bonus est égal à 5% des points au dessus de 10 à appliquer sur toutes
|
||||||
|
les UE du semestre, applicable dans toutes les formations (DUT, BUT, ...).
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = "bonus_iutblagnac"
|
||||||
|
displayed_name = "IUT de Blagnac"
|
||||||
|
proportion_point = 0.05
|
||||||
|
classic_use_bonus_ues = True # toujours sur les UE
|
||||||
|
|
||||||
|
|
||||||
class BonusBordeaux1(BonusSportMultiplicatif):
|
class BonusBordeaux1(BonusSportMultiplicatif):
|
||||||
"""Calcul bonus modules optionnels (sport, culture), règle IUT Bordeaux 1,
|
"""Calcul bonus modules optionnels (sport, culture), règle IUT Bordeaux 1,
|
||||||
sur moyenne générale et UEs.
|
sur moyenne générale et UEs.
|
||||||
@ -690,6 +703,51 @@ class BonusGrenobleIUT1(BonusSportMultiplicatif):
|
|||||||
super().compute_bonus(sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan)
|
super().compute_bonus(sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan)
|
||||||
|
|
||||||
|
|
||||||
|
class BonusIUTRennes1(BonusSportAdditif):
|
||||||
|
"""Calcul bonus optionnels (sport, langue vivante, engagement étudiant),
|
||||||
|
règle IUT de l'Université de Rennes 1 (Lannion, St Malo).
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Les étudiants peuvent suivre un ou plusieurs activités optionnelles notées.
|
||||||
|
La meilleure des notes obtenue est prise en compte, si elle est supérieure à 10/20.
|
||||||
|
</li>
|
||||||
|
<li>Le vingtième des points au dessus de 10 est ajouté à la moyenne des UE.
|
||||||
|
</li>
|
||||||
|
<li> Exemple: un étudiant ayant 16/20 bénéficiera d'un bonus de (16-10)/20 = 0,3 points
|
||||||
|
sur chaque UE.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = "bonus_iut_rennes1"
|
||||||
|
displayed_name = "IUTs de Rennes 1 (Lannion, St Malo)"
|
||||||
|
seuil_moy_gen = 10.0
|
||||||
|
proportion_point = 1 / 20.0
|
||||||
|
classic_use_bonus_ues = True
|
||||||
|
# Adapté de BonusTarbes, mais s'applique aussi en classic
|
||||||
|
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
||||||
|
"""calcul du bonus"""
|
||||||
|
# Prend la note de chaque modimpl, sans considération d'UE
|
||||||
|
if len(sem_modimpl_moys_inscrits.shape) > 2: # apc
|
||||||
|
sem_modimpl_moys_inscrits = sem_modimpl_moys_inscrits[:, :, 0]
|
||||||
|
# ici sem_modimpl_moys_inscrits est nb_etuds x nb_mods_bonus, en APC et en classic
|
||||||
|
note_bonus_max = np.max(sem_modimpl_moys_inscrits, axis=1) # 1d, nb_etuds
|
||||||
|
nb_ues = self.formsemestre.query_ues(with_sport=False).count()
|
||||||
|
|
||||||
|
bonus_moy_arr = np.where(
|
||||||
|
note_bonus_max > self.seuil_moy_gen,
|
||||||
|
(note_bonus_max - self.seuil_moy_gen) * self.proportion_point,
|
||||||
|
0.0,
|
||||||
|
)
|
||||||
|
# Seuil: bonus dans [min, max] (défaut [0,20])
|
||||||
|
bonus_max = self.bonus_max or 20.0
|
||||||
|
np.clip(bonus_moy_arr, self.bonus_min, bonus_max, out=bonus_moy_arr)
|
||||||
|
if self.formsemestre.formation.is_apc():
|
||||||
|
bonus_moy_arr = np.stack([bonus_moy_arr] * nb_ues).T
|
||||||
|
|
||||||
|
self.bonus_additif(bonus_moy_arr)
|
||||||
|
|
||||||
|
|
||||||
class BonusLaRochelle(BonusSportAdditif):
|
class BonusLaRochelle(BonusSportAdditif):
|
||||||
"""Calcul bonus modules optionnels (sport, culture), règle IUT de La Rochelle.
|
"""Calcul bonus modules optionnels (sport, culture), règle IUT de La Rochelle.
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ from app.auth.models import User
|
|||||||
from app.comp.res_cache import ResultatsCache
|
from app.comp.res_cache import ResultatsCache
|
||||||
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.models import FormSemestre, FormSemestreUECoef, formsemestre
|
from app.models import FormSemestre, FormSemestreUECoef
|
||||||
from app.models import Identite
|
from app.models import Identite
|
||||||
from app.models import ModuleImpl, ModuleImplInscription
|
from app.models import ModuleImpl, ModuleImplInscription
|
||||||
from app.models.ues import UniteEns
|
from app.models.ues import UniteEns
|
||||||
@ -151,6 +151,7 @@ class ResultatsSemestre(ResultatsCache):
|
|||||||
if m.module.module_type == scu.ModuleType.SAE
|
if m.module.module_type == scu.ModuleType.SAE
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# --- JURY...
|
||||||
def get_etud_ue_validables(self, etudid: int) -> list[UniteEns]:
|
def get_etud_ue_validables(self, etudid: int) -> list[UniteEns]:
|
||||||
"""Liste des UEs du semestre qui doivent être validées
|
"""Liste des UEs du semestre qui doivent être validées
|
||||||
|
|
||||||
|
@ -35,7 +35,9 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
"malus",
|
"malus",
|
||||||
"etud_moy_gen_ranks",
|
"etud_moy_gen_ranks",
|
||||||
"etud_moy_gen_ranks_int",
|
"etud_moy_gen_ranks_int",
|
||||||
|
"moy_gen_rangs_by_group",
|
||||||
"ue_rangs",
|
"ue_rangs",
|
||||||
|
"ue_rangs_by_group",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, formsemestre: FormSemestre):
|
def __init__(self, formsemestre: FormSemestre):
|
||||||
@ -48,6 +50,8 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
self.moy_min = "NA"
|
self.moy_min = "NA"
|
||||||
self.moy_max = "NA"
|
self.moy_max = "NA"
|
||||||
self.moy_moy = "NA"
|
self.moy_moy = "NA"
|
||||||
|
self.moy_gen_rangs_by_group = {} # { group_id : (Series, Series) }
|
||||||
|
self.ue_rangs_by_group = {} # { ue_id : {group_id : (Series, Series)}}
|
||||||
self.expr_diagnostics = ""
|
self.expr_diagnostics = ""
|
||||||
self.parcours = self.formsemestre.formation.get_parcours()
|
self.parcours = self.formsemestre.formation.get_parcours()
|
||||||
|
|
||||||
@ -153,31 +157,83 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
def compute_rangs(self):
|
def compute_rangs(self):
|
||||||
"""Calcule les classements
|
"""Calcule les classements
|
||||||
Moyenne générale: etud_moy_gen_ranks
|
Moyenne générale: etud_moy_gen_ranks
|
||||||
Par UE (sauf ue bonus)
|
Par UE (sauf ue bonus): ue_rangs[ue.id]
|
||||||
|
Par groupe: classements selon moy_gen et UE:
|
||||||
|
moy_gen_rangs_by_group[group_id]
|
||||||
|
ue_rangs_by_group[group_id]
|
||||||
"""
|
"""
|
||||||
(
|
(
|
||||||
self.etud_moy_gen_ranks,
|
self.etud_moy_gen_ranks,
|
||||||
self.etud_moy_gen_ranks_int,
|
self.etud_moy_gen_ranks_int,
|
||||||
) = moy_sem.comp_ranks_series(self.etud_moy_gen)
|
) = moy_sem.comp_ranks_series(self.etud_moy_gen)
|
||||||
for ue in self.formsemestre.query_ues():
|
ues = self.formsemestre.query_ues()
|
||||||
|
for ue in ues:
|
||||||
moy_ue = self.etud_moy_ue[ue.id]
|
moy_ue = self.etud_moy_ue[ue.id]
|
||||||
self.ue_rangs[ue.id] = (
|
self.ue_rangs[ue.id] = (
|
||||||
moy_sem.comp_ranks_series(moy_ue)[0], # juste en chaine
|
moy_sem.comp_ranks_series(moy_ue)[0], # juste en chaine
|
||||||
int(moy_ue.count()),
|
int(moy_ue.count()),
|
||||||
)
|
)
|
||||||
# .count() -> nb of non NaN values
|
# .count() -> nb of non NaN values
|
||||||
|
# Rangs dans les groupes (moy. gen et par UE)
|
||||||
|
self.moy_gen_rangs_by_group = {} # { group_id : (Series, Series) }
|
||||||
|
self.ue_rangs_by_group = {}
|
||||||
|
partitions_avec_rang = self.formsemestre.partitions.filter_by(
|
||||||
|
bul_show_rank=True
|
||||||
|
)
|
||||||
|
for partition in partitions_avec_rang:
|
||||||
|
for group in partition.groups:
|
||||||
|
# on prend l'intersection car les groupes peuvent inclure des étudiants désinscrits
|
||||||
|
group_members = list(
|
||||||
|
{etud.id for etud in group.etuds}.intersection(
|
||||||
|
self.etud_moy_gen.index
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# list() car pandas veut une sequence pour take()
|
||||||
|
# Rangs / moyenne générale:
|
||||||
|
group_moys_gen = self.etud_moy_gen[group_members]
|
||||||
|
self.moy_gen_rangs_by_group[group.id] = moy_sem.comp_ranks_series(
|
||||||
|
group_moys_gen
|
||||||
|
)
|
||||||
|
# Rangs / UEs:
|
||||||
|
for ue in ues:
|
||||||
|
group_moys_ue = self.etud_moy_ue[ue.id][group_members]
|
||||||
|
self.ue_rangs_by_group.setdefault(ue.id, {})[
|
||||||
|
group.id
|
||||||
|
] = moy_sem.comp_ranks_series(group_moys_ue)
|
||||||
|
|
||||||
def get_etud_ue_rang(self, ue_id, etudid) -> tuple[str, int]:
|
def get_etud_rang(self, etudid: int) -> str:
|
||||||
|
"""Le rang (classement) de l'étudiant dans le semestre.
|
||||||
|
Result: "13" ou "12 ex"
|
||||||
|
"""
|
||||||
|
return self.etud_moy_gen_ranks.get(etudid, 99999)
|
||||||
|
|
||||||
|
def get_etud_ue_rang(self, ue_id, etudid, group_id=None) -> tuple[str, int]:
|
||||||
"""Le rang de l'étudiant dans cette ue
|
"""Le rang de l'étudiant dans cette ue
|
||||||
|
Si le group_id est spécifié, rang au sein de ce groupe, sinon global.
|
||||||
Result: rang:str, effectif:str
|
Result: rang:str, effectif:str
|
||||||
"""
|
"""
|
||||||
rangs, effectif = self.ue_rangs[ue_id]
|
if group_id is None:
|
||||||
if rangs is not None:
|
rangs, effectif = self.ue_rangs[ue_id]
|
||||||
rang = rangs[etudid]
|
if rangs is not None:
|
||||||
|
rang = rangs[etudid]
|
||||||
|
else:
|
||||||
|
return "", ""
|
||||||
else:
|
else:
|
||||||
return "", ""
|
rangs = self.ue_rangs_by_group[ue_id][group_id][0]
|
||||||
|
rang = rangs[etudid]
|
||||||
|
effectif = len(rangs)
|
||||||
return rang, effectif
|
return rang, effectif
|
||||||
|
|
||||||
|
def get_etud_rang_group(self, etudid: int, group_id: int) -> tuple[str, int]:
|
||||||
|
"""Rang de l'étudiant (selon moy gen) et effectif dans ce groupe.
|
||||||
|
Si le groupe n'a pas de rang (partition avec bul_show_rank faux), ramène "", 0
|
||||||
|
"""
|
||||||
|
if group_id in self.moy_gen_rangs_by_group:
|
||||||
|
r = self.moy_gen_rangs_by_group[group_id][0] # version en str
|
||||||
|
return (r[etudid], len(r))
|
||||||
|
else:
|
||||||
|
return "", 0
|
||||||
|
|
||||||
def etud_check_conditions_ues(self, etudid):
|
def etud_check_conditions_ues(self, etudid):
|
||||||
"""Vrai si les conditions sur les UE sont remplies.
|
"""Vrai si les conditions sur les UE sont remplies.
|
||||||
Ne considère que les UE ayant des notes (moyenne calculée).
|
Ne considère que les UE ayant des notes (moyenne calculée).
|
||||||
@ -298,16 +354,6 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
"ects_pot_fond": 0.0, # not implemented (anciennemment pour école ingé)
|
"ects_pot_fond": 0.0, # not implemented (anciennemment pour école ingé)
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_etud_rang(self, etudid: int) -> str:
|
|
||||||
"""Le rang (classement) de l'étudiant dans le semestre.
|
|
||||||
Result: "13" ou "12 ex"
|
|
||||||
"""
|
|
||||||
return self.etud_moy_gen_ranks.get(etudid, 99999)
|
|
||||||
|
|
||||||
def get_etud_rang_group(self, etudid: int, group_id: int):
|
|
||||||
"Le rang de l'étudiant dans ce groupe (NON IMPLEMENTE)"
|
|
||||||
return (None, 0) # XXX unimplemented TODO
|
|
||||||
|
|
||||||
def get_evals_in_mod(self, moduleimpl_id: int) -> list[dict]:
|
def get_evals_in_mod(self, moduleimpl_id: int) -> list[dict]:
|
||||||
"""Liste d'informations (compat NotesTable) sur évaluations completes
|
"""Liste d'informations (compat NotesTable) sur évaluations completes
|
||||||
de ce module.
|
de ce module.
|
||||||
|
@ -82,7 +82,9 @@ def configuration():
|
|||||||
form_bonus.data["bonus_sport_func_name"]
|
form_bonus.data["bonus_sport_func_name"]
|
||||||
)
|
)
|
||||||
app.clear_scodoc_cache()
|
app.clear_scodoc_cache()
|
||||||
flash(f"Fonction bonus sport&culture configurée.")
|
flash("""Fonction bonus sport&culture configurée.""")
|
||||||
|
else:
|
||||||
|
flash("Fonction bonus inchangée.")
|
||||||
return redirect(url_for("scodoc.index"))
|
return redirect(url_for("scodoc.index"))
|
||||||
elif form_scodoc.submit_scodoc.data and form_scodoc.validate():
|
elif form_scodoc.submit_scodoc.data and form_scodoc.validate():
|
||||||
if ScoDocSiteConfig.enable_entreprises(
|
if ScoDocSiteConfig.enable_entreprises(
|
||||||
|
@ -56,11 +56,13 @@ class Identite(db.Model):
|
|||||||
#
|
#
|
||||||
adresses = db.relationship("Adresse", lazy="dynamic", backref="etud")
|
adresses = db.relationship("Adresse", lazy="dynamic", backref="etud")
|
||||||
billets = db.relationship("BilletAbsence", backref="etudiant", lazy="dynamic")
|
billets = db.relationship("BilletAbsence", backref="etudiant", lazy="dynamic")
|
||||||
# one-to-one relation:
|
#
|
||||||
admission = db.relationship("Admission", backref="identite", lazy="dynamic")
|
admission = db.relationship("Admission", backref="identite", lazy="dynamic")
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<Etud {self.id} {self.nom!r} {self.prenom!r}>"
|
return (
|
||||||
|
f"<Etud {self.id}/{self.departement.acronym} {self.nom!r} {self.prenom!r}>"
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_request(cls, etudid=None, code_nip=None):
|
def from_request(cls, etudid=None, code_nip=None):
|
||||||
@ -178,6 +180,8 @@ class Identite(db.Model):
|
|||||||
"date_naissance": self.date_naissance.strftime("%d/%m/%Y")
|
"date_naissance": self.date_naissance.strftime("%d/%m/%Y")
|
||||||
if self.date_naissance
|
if self.date_naissance
|
||||||
else "",
|
else "",
|
||||||
|
"dept_id": self.dept_id,
|
||||||
|
"dept_acronym": self.departement.acronym,
|
||||||
"email": self.get_first_email() or "",
|
"email": self.get_first_email() or "",
|
||||||
"emailperso": self.get_first_email("emailperso"),
|
"emailperso": self.get_first_email("emailperso"),
|
||||||
"etudid": self.id,
|
"etudid": self.id,
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.models import formsemestre
|
|
||||||
from app.models.formsemestre import FormSemestre
|
|
||||||
from app.models.moduleimpls import ModuleImpl
|
from app.models.moduleimpls import ModuleImpl
|
||||||
from app.models.ues import UniteEns
|
from app.models.ues import UniteEns
|
||||||
|
|
||||||
@ -48,13 +46,25 @@ class Evaluation(db.Model):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"""<Evaluation {self.id} {self.jour.isoformat() if self.jour else ''} "{self.description[:16] if self.description else ''}">"""
|
return f"""<Evaluation {self.id} {self.jour.isoformat() if self.jour else ''} "{self.description[:16] if self.description else ''}">"""
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self) -> dict:
|
||||||
|
"Représentation dict, pour json"
|
||||||
e = dict(self.__dict__)
|
e = dict(self.__dict__)
|
||||||
e.pop("_sa_instance_state", None)
|
e.pop("_sa_instance_state", None)
|
||||||
# ScoDoc7 output_formators
|
# ScoDoc7 output_formators
|
||||||
e["evaluation_id"] = self.id
|
e["evaluation_id"] = self.id
|
||||||
e["jour"] = ndb.DateISOtoDMY(e["jour"])
|
e["jour"] = e["jour"].strftime("%d/%m/%Y") if e["jour"] else ""
|
||||||
|
if self.jour is None:
|
||||||
|
e["date_debut"] = None
|
||||||
|
e["date_fin"] = None
|
||||||
|
else:
|
||||||
|
e["date_debut"] = datetime.datetime.combine(
|
||||||
|
self.jour, self.heure_debut or datetime.time(0, 0)
|
||||||
|
).isoformat()
|
||||||
|
e["date_fin"] = datetime.datetime.combine(
|
||||||
|
self.jour, self.heure_fin or datetime.time(0, 0)
|
||||||
|
).isoformat()
|
||||||
e["numero"] = ndb.int_null_is_zero(e["numero"])
|
e["numero"] = ndb.int_null_is_zero(e["numero"])
|
||||||
|
e["poids"] = self.get_ue_poids_dict() # { ue_id : poids }
|
||||||
return evaluation_enrich_dict(e)
|
return evaluation_enrich_dict(e)
|
||||||
|
|
||||||
def from_dict(self, data):
|
def from_dict(self, data):
|
||||||
@ -153,7 +163,7 @@ class EvaluationUEPoids(db.Model):
|
|||||||
|
|
||||||
# Fonction héritée de ScoDoc7 à refactorer
|
# Fonction héritée de ScoDoc7 à refactorer
|
||||||
def evaluation_enrich_dict(e):
|
def evaluation_enrich_dict(e):
|
||||||
"""add or convert some fileds in an evaluation dict"""
|
"""add or convert some fields in an evaluation dict"""
|
||||||
# For ScoDoc7 compat
|
# For ScoDoc7 compat
|
||||||
heure_debut_dt = e["heure_debut"] or datetime.time(
|
heure_debut_dt = e["heure_debut"] or datetime.time(
|
||||||
8, 00
|
8, 00
|
||||||
|
@ -287,7 +287,7 @@ class FormSemestre(db.Model):
|
|||||||
"""
|
"""
|
||||||
if not self.etapes:
|
if not self.etapes:
|
||||||
return ""
|
return ""
|
||||||
return ", ".join(sorted([str(x.etape_apo) for x in self.etapes]))
|
return ", ".join(sorted([etape.etape_apo for etape in self.etapes if etape]))
|
||||||
|
|
||||||
def responsables_str(self, abbrev_prenom=True) -> str:
|
def responsables_str(self, abbrev_prenom=True) -> str:
|
||||||
"""chaîne "J. Dupond, X. Martin"
|
"""chaîne "J. Dupond, X. Martin"
|
||||||
@ -449,10 +449,15 @@ class FormSemestreEtape(db.Model):
|
|||||||
db.Integer,
|
db.Integer,
|
||||||
db.ForeignKey("notes_formsemestre.id"),
|
db.ForeignKey("notes_formsemestre.id"),
|
||||||
)
|
)
|
||||||
|
# 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 __bool__(self):
|
||||||
|
"Etape False if code empty"
|
||||||
|
return self.etape_apo is not None and (len(self.etape_apo) > 0)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<Etape {self.id} apo={self.etape_apo}>"
|
return f"<Etape {self.id} apo={self.etape_apo!r}>"
|
||||||
|
|
||||||
def as_apovdi(self):
|
def as_apovdi(self):
|
||||||
return ApoEtapeVDI(self.etape_apo)
|
return ApoEtapeVDI(self.etape_apo)
|
||||||
|
@ -25,9 +25,11 @@ class Partition(db.Model):
|
|||||||
partition_name = db.Column(db.String(SHORT_STR_LEN))
|
partition_name = db.Column(db.String(SHORT_STR_LEN))
|
||||||
# numero = ordre de presentation)
|
# numero = ordre de presentation)
|
||||||
numero = db.Column(db.Integer)
|
numero = db.Column(db.Integer)
|
||||||
|
# Calculer le rang ?
|
||||||
bul_show_rank = db.Column(
|
bul_show_rank = db.Column(
|
||||||
db.Boolean(), nullable=False, default=False, server_default="false"
|
db.Boolean(), nullable=False, default=False, server_default="false"
|
||||||
)
|
)
|
||||||
|
# Montrer quand on indique les groupes de l'étudiant ?
|
||||||
show_in_lists = db.Column(
|
show_in_lists = db.Column(
|
||||||
db.Boolean(), nullable=False, default=True, server_default="true"
|
db.Boolean(), nullable=False, default=True, server_default="true"
|
||||||
)
|
)
|
||||||
@ -50,6 +52,18 @@ class Partition(db.Model):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"""<{self.__class__.__name__} {self.id} "{self.partition_name or '(default)'}">"""
|
return f"""<{self.__class__.__name__} {self.id} "{self.partition_name or '(default)'}">"""
|
||||||
|
|
||||||
|
def to_dict(self, with_groups=False) -> dict:
|
||||||
|
"""as a dict, with or without groups"""
|
||||||
|
d = {
|
||||||
|
"id": self.id,
|
||||||
|
"formsemestre_id": self.partition_id,
|
||||||
|
"name": self.partition_name,
|
||||||
|
"numero": self.numero,
|
||||||
|
}
|
||||||
|
if with_groups:
|
||||||
|
d["groups"] = [group.to_dict(with_partition=False) for group in self.groups]
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
class GroupDescr(db.Model):
|
class GroupDescr(db.Model):
|
||||||
"""Description d'un groupe d'une partition"""
|
"""Description d'un groupe d'une partition"""
|
||||||
@ -78,6 +92,17 @@ class GroupDescr(db.Model):
|
|||||||
"Nom avec partition: 'TD A'"
|
"Nom avec partition: 'TD A'"
|
||||||
return f"{self.partition.partition_name or ''} {self.group_name or '-'}"
|
return f"{self.partition.partition_name or ''} {self.group_name or '-'}"
|
||||||
|
|
||||||
|
def to_dict(self, with_partition=True) -> dict:
|
||||||
|
"""as a dict, with or without partition"""
|
||||||
|
d = {
|
||||||
|
"id": self.id,
|
||||||
|
"partition_id": self.partition_id,
|
||||||
|
"name": self.group_name,
|
||||||
|
}
|
||||||
|
if with_partition:
|
||||||
|
d["partition"] = self.partition.to_dict(with_groups=False)
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
group_membership = db.Table(
|
group_membership = db.Table(
|
||||||
"group_membership",
|
"group_membership",
|
||||||
@ -85,3 +110,11 @@ group_membership = db.Table(
|
|||||||
db.Column("group_id", db.Integer, db.ForeignKey("group_descr.id")),
|
db.Column("group_id", db.Integer, db.ForeignKey("group_descr.id")),
|
||||||
db.UniqueConstraint("etudid", "group_id"),
|
db.UniqueConstraint("etudid", "group_id"),
|
||||||
)
|
)
|
||||||
|
# class GroupMembership(db.Model):
|
||||||
|
# """Association groupe / étudiant"""
|
||||||
|
|
||||||
|
# __tablename__ = "group_membership"
|
||||||
|
# __table_args__ = (db.UniqueConstraint("etudid", "group_id"),)
|
||||||
|
# id = db.Column(db.Integer, primary_key=True)
|
||||||
|
# etudid = db.Column(db.Integer, db.ForeignKey("identite.id"))
|
||||||
|
# group_id = db.Column(db.Integer, db.ForeignKey("group_descr.id"))
|
||||||
|
@ -97,7 +97,7 @@ class SetTag(pe_tagtable.TableTag):
|
|||||||
"""Mémorise les semtag nécessaires au jury."""
|
"""Mémorise les semtag nécessaires au jury."""
|
||||||
self.SemTagDict = {fid: SemTagDict[fid] for fid in self.get_Fids_in_settag()}
|
self.SemTagDict = {fid: SemTagDict[fid] for fid in self.get_Fids_in_settag()}
|
||||||
if PE_DEBUG >= 1:
|
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):
|
def comp_data_settag(self):
|
||||||
@ -210,7 +210,7 @@ class SetTagInterClasse(pe_tagtable.TableTag):
|
|||||||
# -------------------------------------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------------------------------------
|
||||||
def __init__(self, nom_combinaison, diplome):
|
def __init__(self, nom_combinaison, diplome):
|
||||||
|
|
||||||
pe_tagtable.TableTag.__init__(self, nom=nom_combinaison + "_%d" % diplome)
|
pe_tagtable.TableTag.__init__(self, nom=f"{nom_combinaison}_{diplome or ''}")
|
||||||
self.combinaison = nom_combinaison
|
self.combinaison = nom_combinaison
|
||||||
self.parcoursDict = {}
|
self.parcoursDict = {}
|
||||||
|
|
||||||
@ -243,7 +243,7 @@ class SetTagInterClasse(pe_tagtable.TableTag):
|
|||||||
fid: SetTagDict[fid] for fid in self.get_Fids_in_settag() if fid != None
|
fid: SetTagDict[fid] for fid in self.get_Fids_in_settag() if fid != None
|
||||||
}
|
}
|
||||||
if PE_DEBUG >= 1:
|
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):
|
def comp_data_settag(self):
|
||||||
|
@ -983,7 +983,8 @@ def _tables_abs_etud(
|
|||||||
)[0]
|
)[0]
|
||||||
if format == "html":
|
if format == "html":
|
||||||
ex.append(
|
ex.append(
|
||||||
f"""<a href="{url_for('notes.moduleimpl_status',
|
f"""<a title="{mod['module']['titre']}"
|
||||||
|
href="{url_for('notes.moduleimpl_status',
|
||||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])}
|
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])}
|
||||||
">{mod["module"]["code"] or '(module sans code)'}</a>"""
|
">{mod["module"]["code"] or '(module sans code)'}</a>"""
|
||||||
)
|
)
|
||||||
|
@ -251,7 +251,7 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
|||||||
rang = ""
|
rang = ""
|
||||||
|
|
||||||
rang_gr, ninscrits_gr, gr_name = get_etud_rangs_groups(
|
rang_gr, ninscrits_gr, gr_name = get_etud_rangs_groups(
|
||||||
etudid, formsemestre_id, partitions, partitions_etud_groups, nt
|
etudid, partitions, partitions_etud_groups, nt
|
||||||
)
|
)
|
||||||
|
|
||||||
if nt.get_moduleimpls_attente():
|
if nt.get_moduleimpls_attente():
|
||||||
@ -651,7 +651,7 @@ def _ue_mod_bulletin(
|
|||||||
|
|
||||||
|
|
||||||
def get_etud_rangs_groups(
|
def get_etud_rangs_groups(
|
||||||
etudid, formsemestre_id, partitions, partitions_etud_groups, nt
|
etudid: int, partitions, partitions_etud_groups, nt: NotesTableCompat
|
||||||
):
|
):
|
||||||
"""Ramene rang et nb inscrits dans chaque partition"""
|
"""Ramene rang et nb inscrits dans chaque partition"""
|
||||||
rang_gr, ninscrits_gr, gr_name = {}, {}, {}
|
rang_gr, ninscrits_gr, gr_name = {}, {}, {}
|
||||||
|
@ -165,7 +165,7 @@ def formsemestre_bulletinetud_published_dict(
|
|||||||
else:
|
else:
|
||||||
rang = str(nt.get_etud_rang(etudid))
|
rang = str(nt.get_etud_rang(etudid))
|
||||||
rang_gr, ninscrits_gr, gr_name = sco_bulletins.get_etud_rangs_groups(
|
rang_gr, ninscrits_gr, gr_name = sco_bulletins.get_etud_rangs_groups(
|
||||||
etudid, formsemestre_id, partitions, partitions_etud_groups, nt
|
etudid, partitions, partitions_etud_groups, nt
|
||||||
)
|
)
|
||||||
|
|
||||||
d["note"] = dict(
|
d["note"] = dict(
|
||||||
|
@ -172,7 +172,7 @@ def make_xml_formsemestre_bulletinetud(
|
|||||||
else:
|
else:
|
||||||
rang = str(nt.get_etud_rang(etudid))
|
rang = str(nt.get_etud_rang(etudid))
|
||||||
rang_gr, ninscrits_gr, gr_name = sco_bulletins.get_etud_rangs_groups(
|
rang_gr, ninscrits_gr, gr_name = sco_bulletins.get_etud_rangs_groups(
|
||||||
etudid, formsemestre_id, partitions, partitions_etud_groups, nt
|
etudid, partitions, partitions_etud_groups, nt
|
||||||
)
|
)
|
||||||
|
|
||||||
doc.append(
|
doc.append(
|
||||||
|
@ -204,13 +204,14 @@ def module_delete(module_id=None):
|
|||||||
|
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(page_title="Suppression d'un module"),
|
html_sco_header.sco_header(page_title="Suppression d'un module"),
|
||||||
"""<h2>Suppression du module %(titre)s (%(code)s)</h2>""" % mod,
|
f"""<h2>Suppression du module {module.titre} ({module.code})</h2>""",
|
||||||
]
|
]
|
||||||
|
|
||||||
dest_url = url_for(
|
dest_url = url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=str(mod["formation_id"]),
|
formation_id=module.formation_id,
|
||||||
|
semestre_idx=module.ue.semestre_idx,
|
||||||
)
|
)
|
||||||
tf = TrivialFormulator(
|
tf = TrivialFormulator(
|
||||||
request.base_url,
|
request.base_url,
|
||||||
|
@ -157,6 +157,8 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
|
|||||||
from app.scodoc import sco_parcours_dut
|
from app.scodoc import sco_parcours_dut
|
||||||
|
|
||||||
ue = UniteEns.query.get_or_404(ue_id)
|
ue = UniteEns.query.get_or_404(ue_id)
|
||||||
|
formation_id = ue.formation_id
|
||||||
|
semestre_idx = ue.semestre_idx
|
||||||
if not can_delete_ue(ue):
|
if not can_delete_ue(ue):
|
||||||
raise ScoNonEmptyFormationObject(
|
raise ScoNonEmptyFormationObject(
|
||||||
"UE",
|
"UE",
|
||||||
@ -164,8 +166,8 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
|
|||||||
dest_url=url_for(
|
dest_url=url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=ue.formation_id,
|
formation_id=formation_id,
|
||||||
semestre_idx=ue.semestre_idx,
|
semestre_idx=semestre_idx,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -188,13 +190,13 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
|
|||||||
cancel_url=url_for(
|
cancel_url=url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=ue.formation_id,
|
formation_id=formation_id,
|
||||||
semestre_idx=ue.semestre_idx,
|
semestre_idx=semestre_idx,
|
||||||
),
|
),
|
||||||
parameters={"ue_id": ue.id, "dialog_confirmed": 1},
|
parameters={"ue_id": ue.id, "dialog_confirmed": 1},
|
||||||
)
|
)
|
||||||
if delete_validations:
|
if delete_validations:
|
||||||
log("deleting all validations of UE %s" % ue.id)
|
log(f"deleting all validations of UE {ue.id}")
|
||||||
ndb.SimpleQuery(
|
ndb.SimpleQuery(
|
||||||
"DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s",
|
"DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s",
|
||||||
{"ue_id": ue.id},
|
{"ue_id": ue.id},
|
||||||
@ -216,10 +218,10 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
|
|||||||
# utilisé: acceptable de tout invalider):
|
# utilisé: acceptable de tout invalider):
|
||||||
sco_cache.invalidate_formsemestre()
|
sco_cache.invalidate_formsemestre()
|
||||||
# news
|
# news
|
||||||
F = sco_formations.formation_list(args={"formation_id": ue.formation_id})[0]
|
F = sco_formations.formation_list(args={"formation_id": formation_id})[0]
|
||||||
ScolarNews.add(
|
ScolarNews.add(
|
||||||
typ=ScolarNews.NEWS_FORM,
|
typ=ScolarNews.NEWS_FORM,
|
||||||
obj=ue.formation_id,
|
obj=formation_id,
|
||||||
text=f"Modification de la formation {F['acronyme']}",
|
text=f"Modification de la formation {F['acronyme']}",
|
||||||
max_frequency=10 * 60,
|
max_frequency=10 * 60,
|
||||||
)
|
)
|
||||||
@ -229,8 +231,8 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
|
|||||||
url_for(
|
url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=ue.formation_id,
|
formation_id=formation_id,
|
||||||
semestre_idx=ue.semestre_idx,
|
semestre_idx=semestre_idx,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
@ -208,25 +208,29 @@ def _build_results_list(dpv_by_sem, etuds_infos):
|
|||||||
return rows, titles, columns_ids
|
return rows, titles, columns_ids
|
||||||
|
|
||||||
|
|
||||||
def get_set_formsemestre_id_dates(start_date, end_date):
|
def get_set_formsemestre_id_dates(start_date, end_date) -> set:
|
||||||
"""Ensemble des formsemestre_id entre ces dates"""
|
"""Ensemble des formsemestre_id entre ces dates"""
|
||||||
s = ndb.SimpleDictFetch(
|
s = ndb.SimpleDictFetch(
|
||||||
"""SELECT id
|
"""SELECT id
|
||||||
FROM notes_formsemestre
|
FROM notes_formsemestre
|
||||||
WHERE date_debut >= %(start_date)s AND date_fin <= %(end_date)s
|
WHERE date_debut >= %(start_date)s
|
||||||
|
AND date_fin <= %(end_date)s
|
||||||
|
AND dept_id = %(dept_id)s
|
||||||
""",
|
""",
|
||||||
{"start_date": start_date, "end_date": end_date},
|
{"start_date": start_date, "end_date": end_date, "dept_id": g.scodoc_dept_id},
|
||||||
)
|
)
|
||||||
return {x["id"] for x in s}
|
return {x["id"] for x in s}
|
||||||
|
|
||||||
|
|
||||||
def scodoc_table_results(start_date="", end_date="", types_parcours=[], format="html"):
|
def scodoc_table_results(
|
||||||
|
start_date="", end_date="", types_parcours: list = None, format="html"
|
||||||
|
):
|
||||||
"""Page affichant la table des résultats
|
"""Page affichant la table des résultats
|
||||||
Les dates sont en dd/mm/yyyy (datepicker javascript)
|
Les dates sont en dd/mm/yyyy (datepicker javascript)
|
||||||
types_parcours est la liste des types de parcours à afficher
|
types_parcours est la liste des types de parcours à afficher
|
||||||
(liste de chaines, eg ['100', '210'] )
|
(liste de chaines, eg ['100', '210'] )
|
||||||
"""
|
"""
|
||||||
log("scodoc_table_results: start_date=%s" % (start_date,)) # XXX
|
log(f"scodoc_table_results: start_date={start_date!r}")
|
||||||
if not types_parcours:
|
if not types_parcours:
|
||||||
types_parcours = []
|
types_parcours = []
|
||||||
if not isinstance(types_parcours, list):
|
if not isinstance(types_parcours, list):
|
||||||
|
@ -256,6 +256,8 @@ def formation_import_xml(doc: str, import_tags=True):
|
|||||||
mod_info[1]["formation_id"] = formation_id
|
mod_info[1]["formation_id"] = formation_id
|
||||||
mod_info[1]["matiere_id"] = mat_id
|
mod_info[1]["matiere_id"] = mat_id
|
||||||
mod_info[1]["ue_id"] = ue_id
|
mod_info[1]["ue_id"] = ue_id
|
||||||
|
if not "module_type" in mod_info[1]:
|
||||||
|
mod_info[1]["module_type"] = scu.ModuleType.STANDARD
|
||||||
mod_id = sco_edit_module.do_module_create(mod_info[1])
|
mod_id = sco_edit_module.do_module_create(mod_info[1])
|
||||||
if xml_module_id:
|
if xml_module_id:
|
||||||
modules_old2new[int(xml_module_id)] = mod_id
|
modules_old2new[int(xml_module_id)] = mod_id
|
||||||
|
@ -262,7 +262,7 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _make_page(etud, sem, tf, message=""):
|
def _make_page(etud: dict, sem, tf, message="") -> list:
|
||||||
formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"])
|
formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"])
|
||||||
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||||
moy_gen = nt.get_etud_moy_gen(etud["etudid"])
|
moy_gen = nt.get_etud_moy_gen(etud["etudid"])
|
||||||
@ -277,21 +277,20 @@ def _make_page(etud, sem, tf, message=""):
|
|||||||
</p>
|
</p>
|
||||||
"""
|
"""
|
||||||
% etud,
|
% etud,
|
||||||
"""<p>La moyenne de ce semestre serait:
|
f"""<p>La moyenne de ce semestre serait:
|
||||||
<span class="ext_sem_moy"><span class="ext_sem_moy_val">%s</span> / 20</span>
|
<span class="ext_sem_moy"><span class="ext_sem_moy_val">{moy_gen}</span> / 20</span>
|
||||||
</p>
|
</p>
|
||||||
"""
|
""",
|
||||||
% moy_gen,
|
|
||||||
'<div id="formsemestre_ext_edit_ue_validations">',
|
'<div id="formsemestre_ext_edit_ue_validations">',
|
||||||
tf[1],
|
tf[1],
|
||||||
"</div>",
|
"</div>",
|
||||||
"""<div>
|
f"""<div>
|
||||||
<a class="stdlink"
|
<a class="stdlink"
|
||||||
href="formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s">
|
href="{url_for("notes.formsemestre_bulletinetud", scodoc_dept=g.scodoc_dept,
|
||||||
retour au bulletin de notes
|
formsemestre_id=formsemestre.id, etudid=etud['etudid']
|
||||||
</a></div>
|
)}">retour au bulletin de notes</a>
|
||||||
"""
|
</div>
|
||||||
% (sem["formsemestre_id"], etud["etudid"]),
|
""",
|
||||||
html_sco_header.sco_footer(),
|
html_sco_header.sco_footer(),
|
||||||
]
|
]
|
||||||
return H
|
return H
|
||||||
|
@ -43,13 +43,14 @@ from xml.etree.ElementTree import Element
|
|||||||
import flask
|
import flask
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask import url_for, make_response
|
from flask import url_for, make_response
|
||||||
|
from sqlalchemy.sql import text
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.comp import res_sem
|
from app.comp import res_sem
|
||||||
from app.comp.res_compat import NotesTableCompat
|
from app.comp.res_compat import NotesTableCompat
|
||||||
from app.models import FormSemestre, formsemestre
|
from app.models import FormSemestre, Identite
|
||||||
from app.models import GROUPNAME_STR_LEN, SHORT_STR_LEN
|
from app.models import GROUPNAME_STR_LEN, SHORT_STR_LEN
|
||||||
from app.models.groups import Partition
|
from app.models.groups import GroupDescr, Partition
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
from app import log, cache
|
from app import log, cache
|
||||||
@ -61,7 +62,6 @@ from app.scodoc import sco_etud
|
|||||||
from app.scodoc import sco_permissions_check
|
from app.scodoc import sco_permissions_check
|
||||||
from app.scodoc import sco_xml
|
from app.scodoc import sco_xml
|
||||||
from app.scodoc.sco_exceptions import ScoException, AccessDenied, ScoValueError
|
from app.scodoc.sco_exceptions import ScoException, AccessDenied, ScoValueError
|
||||||
from app.scodoc.sco_permissions import Permission
|
|
||||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||||
|
|
||||||
|
|
||||||
@ -413,6 +413,34 @@ def formsemestre_get_etud_groupnames(formsemestre_id, attr="group_name"):
|
|||||||
return R
|
return R
|
||||||
|
|
||||||
|
|
||||||
|
def get_etud_formsemestre_groups(
|
||||||
|
etud: Identite, formsemestre: FormSemestre, only_to_show=True
|
||||||
|
) -> list[GroupDescr]:
|
||||||
|
"""Liste les groupes auxquels est inscrit"""
|
||||||
|
# Note: je n'ai pas réussi à cosntruire une requete SQLAlechemy avec
|
||||||
|
# la Table d'association group_membership
|
||||||
|
cursor = db.session.execute(
|
||||||
|
text(
|
||||||
|
"""
|
||||||
|
SELECT g.id
|
||||||
|
FROM group_descr g, group_membership gm, partition p
|
||||||
|
WHERE gm.etudid = :etudid
|
||||||
|
AND gm.group_id = g.id
|
||||||
|
AND g.partition_id = p.id
|
||||||
|
AND p.formsemestre_id = :formsemestre_id
|
||||||
|
AND p.partition_name is not NULL
|
||||||
|
"""
|
||||||
|
+ (" and (p.show_in_lists is True) " if only_to_show else "")
|
||||||
|
+ """
|
||||||
|
ORDER BY p.numero
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
{"etudid": etud.id, "formsemestre_id": formsemestre.id},
|
||||||
|
)
|
||||||
|
return [GroupDescr.query.get(group_id) for group_id in cursor]
|
||||||
|
|
||||||
|
|
||||||
|
# Ancienne fonction:
|
||||||
def etud_add_group_infos(etud, formsemestre_id, sep=" ", only_to_show=False):
|
def etud_add_group_infos(etud, formsemestre_id, sep=" ", only_to_show=False):
|
||||||
"""Add informations on partitions and group memberships to etud
|
"""Add informations on partitions and group memberships to etud
|
||||||
(a dict with an etudid)
|
(a dict with an etudid)
|
||||||
@ -453,7 +481,7 @@ def etud_add_group_infos(etud, formsemestre_id, sep=" ", only_to_show=False):
|
|||||||
)
|
)
|
||||||
etud["partitionsgroupes"] = sep.join(
|
etud["partitionsgroupes"] = sep.join(
|
||||||
[
|
[
|
||||||
gr["partition_name"] + ":" + gr["group_name"]
|
(gr["partition_name"] or "") + ":" + gr["group_name"]
|
||||||
for gr in infos
|
for gr in infos
|
||||||
if gr["group_name"] is not None
|
if gr["group_name"] is not None
|
||||||
]
|
]
|
||||||
|
@ -50,10 +50,10 @@ _SCO_PERMISSIONS = (
|
|||||||
(1 << 27, "RelationsEntreprisesCorrespondants", "Voir les correspondants"),
|
(1 << 27, "RelationsEntreprisesCorrespondants", "Voir les correspondants"),
|
||||||
# 27 à 39 ... réservé pour "entreprises"
|
# 27 à 39 ... réservé pour "entreprises"
|
||||||
# Api scodoc9
|
# Api scodoc9
|
||||||
(1 << 40, "APIView", "Voir"),
|
(1 << 40, "APIView", "API: Lecture"),
|
||||||
(1 << 41, "APIEtudChangeGroups", "Modifier les groupes"),
|
(1 << 41, "APIEtudChangeGroups", "API: Modifier les groupes"),
|
||||||
(1 << 42, "APIEditAllNotes", "Modifier toutes les notes"),
|
(1 << 42, "APIEditAllNotes", "API: Modifier toutes les notes"),
|
||||||
(1 << 43, "APIAbsChange", "Saisir des absences"),
|
(1 << 43, "APIAbsChange", "API: Saisir des absences"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ import time
|
|||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask import url_for
|
from flask import abort, url_for
|
||||||
|
|
||||||
from app import log
|
from app import log
|
||||||
from app.but import bulletin_but
|
from app.but import bulletin_but
|
||||||
@ -83,9 +83,11 @@ def formsemestre_recapcomplet(
|
|||||||
force_publishing: publie les xml et json même si bulletins non publiés
|
force_publishing: publie les xml et json même si bulletins non publiés
|
||||||
selected_etudid: etudid sélectionné (pour scroller au bon endroit)
|
selected_etudid: etudid sélectionné (pour scroller au bon endroit)
|
||||||
"""
|
"""
|
||||||
|
if not isinstance(formsemestre_id, int):
|
||||||
|
abort(404)
|
||||||
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||||
file_formats = {"csv", "json", "xls", "xlsx", "xlsall", "xml"}
|
file_formats = {"csv", "json", "xls", "xlsx", "xlsall", "xml"}
|
||||||
supported_formats = file_formats | {"html"}
|
supported_formats = file_formats | {"html", "evals"}
|
||||||
if tabformat not in supported_formats:
|
if tabformat not in supported_formats:
|
||||||
raise ScoValueError(f"Format non supporté: {tabformat}")
|
raise ScoValueError(f"Format non supporté: {tabformat}")
|
||||||
is_file = tabformat in file_formats
|
is_file = tabformat in file_formats
|
||||||
@ -131,7 +133,8 @@ def formsemestre_recapcomplet(
|
|||||||
for (format, label) in (
|
for (format, label) in (
|
||||||
("html", "Tableau"),
|
("html", "Tableau"),
|
||||||
("evals", "Avec toutes les évaluations"),
|
("evals", "Avec toutes les évaluations"),
|
||||||
("xlsx", "Excel non formatté"),
|
("xlsx", "Excel (non formaté)"),
|
||||||
|
("xlsall", "Excel avec évaluations"),
|
||||||
("xml", "Bulletins XML (obsolète)"),
|
("xml", "Bulletins XML (obsolète)"),
|
||||||
("json", "Bulletins JSON"),
|
("json", "Bulletins JSON"),
|
||||||
):
|
):
|
||||||
|
@ -191,7 +191,7 @@ def fmt_note(val, note_max=None, keep_numeric=False):
|
|||||||
return "EXC" # excuse, note neutralise
|
return "EXC" # excuse, note neutralise
|
||||||
if val == NOTES_ATTENTE:
|
if val == NOTES_ATTENTE:
|
||||||
return "ATT" # attente, note neutralisee
|
return "ATT" # attente, note neutralisee
|
||||||
if isinstance(val, float) or isinstance(val, int):
|
if not isinstance(val, str):
|
||||||
if np.isnan(val):
|
if np.isnan(val):
|
||||||
return "~"
|
return "~"
|
||||||
if (note_max is not None) and note_max > 0:
|
if (note_max is not None) and note_max > 0:
|
||||||
|
@ -100,7 +100,7 @@ $(function () {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Elimine les 0 à gauche pour les exports excel et les "copy"
|
// Elimine les 0 à gauche pour les exports excel et les "copy"
|
||||||
targets: ["col_mod", "col_moy_gen", "col_ue", "col_res", "col_sae"],
|
targets: ["col_mod", "col_moy_gen", "col_ue", "col_res", "col_sae", "evaluation"],
|
||||||
render: function (data, type, row) {
|
render: function (data, type, row) {
|
||||||
return type === 'export' ? data.replace(/0(\d\..*)/, '$1') : data;
|
return type === 'export' ? data.replace(/0(\d\..*)/, '$1') : data;
|
||||||
}
|
}
|
||||||
|
@ -292,7 +292,7 @@ def formsemestre_bulletinetud(
|
|||||||
format = format or "html"
|
format = format or "html"
|
||||||
|
|
||||||
if not isinstance(formsemestre_id, int):
|
if not isinstance(formsemestre_id, int):
|
||||||
raise ValueError("formsemestre_id must be an integer !")
|
abort(404, description="formsemestre_id must be an integer !")
|
||||||
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||||
if etudid:
|
if etudid:
|
||||||
etud = models.Identite.query.get_or_404(etudid)
|
etud = models.Identite.query.get_or_404(etudid)
|
||||||
@ -662,17 +662,6 @@ def formation_export(formation_id, export_ids=False, format=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/formation_import_xml")
|
|
||||||
@scodoc
|
|
||||||
@permission_required(Permission.ScoChangeFormation)
|
|
||||||
@scodoc7func
|
|
||||||
def formation_import_xml(file):
|
|
||||||
"import d'une formation en XML"
|
|
||||||
log("formation_import_xml")
|
|
||||||
doc = file.read()
|
|
||||||
return sco_formations.formation_import_xml(doc)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/formation_import_xml_form", methods=["GET", "POST"])
|
@bp.route("/formation_import_xml_form", methods=["GET", "POST"])
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoChangeFormation)
|
@permission_required(Permission.ScoChangeFormation)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
SCOVERSION = "9.2.18"
|
SCOVERSION = "9.2.22"
|
||||||
|
|
||||||
SCONAME = "ScoDoc"
|
SCONAME = "ScoDoc"
|
||||||
|
|
||||||
|
@ -26,13 +26,17 @@ import urllib3
|
|||||||
from pprint import pprint as pp
|
from pprint import pprint as pp
|
||||||
|
|
||||||
# --- Lecture configuration (variables d'env ou .env)
|
# --- Lecture configuration (variables d'env ou .env)
|
||||||
BASEDIR = os.path.abspath(os.path.dirname(__file__))
|
try:
|
||||||
|
BASEDIR = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
except NameError:
|
||||||
|
BASEDIR = "."
|
||||||
|
|
||||||
load_dotenv(os.path.join(BASEDIR, ".env"))
|
load_dotenv(os.path.join(BASEDIR, ".env"))
|
||||||
CHK_CERT = bool(int(os.environ.get("CHECK_CERTIFICATE", False)))
|
CHK_CERT = bool(int(os.environ.get("CHECK_CERTIFICATE", False)))
|
||||||
SCODOC_URL = os.environ["SCODOC_URL"]
|
SCODOC_URL = os.environ["SCODOC_URL"] or "http://localhost:5000"
|
||||||
API_URL = SCODOC_URL + "/ScoDoc/api"
|
API_URL = SCODOC_URL + "/ScoDoc/api"
|
||||||
SCODOC_USER = os.environ["SCODOC_USER"]
|
SCODOC_USER = os.environ["SCODOC_USER"]
|
||||||
SCODOC_PASSWORD = os.environ["SCODOC_PASSWD"]
|
SCODOC_PASSWORD = os.environ["SCODOC_PASSWORD"]
|
||||||
print(f"SCODOC_URL={SCODOC_URL}")
|
print(f"SCODOC_URL={SCODOC_URL}")
|
||||||
print(f"API URL={API_URL}")
|
print(f"API URL={API_URL}")
|
||||||
|
|
||||||
@ -90,6 +94,25 @@ formsemestre_id = 1028 # A adapter
|
|||||||
etudid = 14721
|
etudid = 14721
|
||||||
bul_dut = GET(f"/etudiant/etudid/{etudid}/formsemestre/{formsemestre_id}/bulletin")
|
bul_dut = GET(f"/etudiant/etudid/{etudid}/formsemestre/{formsemestre_id}/bulletin")
|
||||||
|
|
||||||
|
|
||||||
|
# Infos sur un étudiant
|
||||||
|
etudid = 3561
|
||||||
|
code_nip = "11303314"
|
||||||
|
etud = GET(f"/etudiant/etudid/{etudid}")
|
||||||
|
print(etud)
|
||||||
|
|
||||||
|
etud = GET(f"/etudiant/nip/{code_nip}")
|
||||||
|
print(etud)
|
||||||
|
|
||||||
|
sems = GET(f"/etudiant/etudid/{etudid}/formsemestres")
|
||||||
|
print("\n".join([s["titre_num"] for s in sems]))
|
||||||
|
|
||||||
|
sems = GET(f"/etudiant/nip/{code_nip}/formsemestres")
|
||||||
|
print("\n".join([s["titre_num"] for s in sems]))
|
||||||
|
|
||||||
|
# Evaluation
|
||||||
|
evals = GET("/evaluations/1")
|
||||||
|
|
||||||
# # --- Recupere la liste de tous les semestres:
|
# # --- Recupere la liste de tous les semestres:
|
||||||
# sems = GET(s, "Notes/formsemestre_list?format=json", "Aucun semestre !")
|
# sems = GET(s, "Notes/formsemestre_list?format=json", "Aucun semestre !")
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ SCODOC_URL = os.environ["SCODOC_URL"]
|
|||||||
API_URL = SCODOC_URL + "/ScoDoc/api"
|
API_URL = SCODOC_URL + "/ScoDoc/api"
|
||||||
API_USER = os.environ.get("API_USER", "test")
|
API_USER = os.environ.get("API_USER", "test")
|
||||||
API_PASSWORD = os.environ.get("API_PASSWD", "test")
|
API_PASSWORD = os.environ.get("API_PASSWD", "test")
|
||||||
|
DEPT_ACRONYM = "TAPI"
|
||||||
print(f"SCODOC_URL={SCODOC_URL}")
|
print(f"SCODOC_URL={SCODOC_URL}")
|
||||||
print(f"API URL={API_URL}")
|
print(f"API URL={API_URL}")
|
||||||
|
|
||||||
|
@ -22,8 +22,7 @@ from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
|
|||||||
|
|
||||||
# Etudiant pour les tests
|
# Etudiant pour les tests
|
||||||
ETUDID = 1
|
ETUDID = 1
|
||||||
INE = "1"
|
|
||||||
NIP = "1"
|
|
||||||
|
|
||||||
# absences
|
# absences
|
||||||
def test_absences(api_headers):
|
def test_absences(api_headers):
|
||||||
@ -37,20 +36,6 @@ def test_absences(api_headers):
|
|||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
|
||||||
r = requests.get(
|
|
||||||
f"{API_URL}/absences/nip/{NIP}",
|
|
||||||
headers=api_headers,
|
|
||||||
verify=CHECK_CERTIFICATE,
|
|
||||||
)
|
|
||||||
assert r.status_code == 200
|
|
||||||
|
|
||||||
r = requests.get(
|
|
||||||
f"{API_URL}/absences/ine/{INE}",
|
|
||||||
headers=api_headers,
|
|
||||||
verify=CHECK_CERTIFICATE,
|
|
||||||
)
|
|
||||||
assert r.status_code == 200
|
|
||||||
|
|
||||||
|
|
||||||
# absences_justify
|
# absences_justify
|
||||||
def test_absences_justify(api_headers):
|
def test_absences_justify(api_headers):
|
||||||
@ -65,22 +50,6 @@ def test_absences_justify(api_headers):
|
|||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
# TODO vérifier résultat
|
# TODO vérifier résultat
|
||||||
|
|
||||||
r = requests.get(
|
|
||||||
API_URL + f"/absences/nip/{NIP}/just",
|
|
||||||
headers=api_headers,
|
|
||||||
verify=CHECK_CERTIFICATE,
|
|
||||||
)
|
|
||||||
assert r.status_code == 200
|
|
||||||
# TODO vérifier résultat
|
|
||||||
|
|
||||||
r = requests.get(
|
|
||||||
API_URL + f"/absences/ine/{INE}/just",
|
|
||||||
headers=api_headers,
|
|
||||||
verify=CHECK_CERTIFICATE,
|
|
||||||
)
|
|
||||||
assert r.status_code == 200
|
|
||||||
# TODO vérifier résultat
|
|
||||||
|
|
||||||
|
|
||||||
# XXX TODO
|
# XXX TODO
|
||||||
# def test_abs_groupe_etat(api_headers):
|
# def test_abs_groupe_etat(api_headers):
|
||||||
|
@ -19,7 +19,12 @@ Utilisation :
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
|
from tests.api.setup_test_api import (
|
||||||
|
API_URL,
|
||||||
|
CHECK_CERTIFICATE,
|
||||||
|
DEPT_ACRONYM,
|
||||||
|
api_headers,
|
||||||
|
)
|
||||||
from tests.api.tools_test_api import verify_fields
|
from tests.api.tools_test_api import verify_fields
|
||||||
|
|
||||||
DEPARTEMENT_FIELDS = [
|
DEPARTEMENT_FIELDS = [
|
||||||
@ -86,7 +91,7 @@ def test_list_etudiants(api_headers):
|
|||||||
fields = {"id", "nip", "ine", "nom", "nom_usuel", "prenom", "civilite"}
|
fields = {"id", "nip", "ine", "nom", "nom_usuel", "prenom", "civilite"}
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
API_URL + "/departement/TAPI/etudiants",
|
f"{API_URL}/departement/{DEPT_ACRONYM}/etudiants",
|
||||||
headers=api_headers,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
|
@ -19,7 +19,12 @@ Utilisation :
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
|
from tests.api.setup_test_api import (
|
||||||
|
API_URL,
|
||||||
|
CHECK_CERTIFICATE,
|
||||||
|
DEPT_ACRONYM,
|
||||||
|
api_headers,
|
||||||
|
)
|
||||||
from tests.api.tools_test_api import verify_fields
|
from tests.api.tools_test_api import verify_fields
|
||||||
from tests.api.tools_test_api import ETUD_FIELDS, FSEM_FIELDS
|
from tests.api.tools_test_api import ETUD_FIELDS, FSEM_FIELDS
|
||||||
|
|
||||||
@ -83,7 +88,7 @@ def test_etudiant(api_headers):
|
|||||||
etud = r.json()
|
etud = r.json()
|
||||||
fields_ok = verify_fields(etud, ETUD_FIELDS)
|
fields_ok = verify_fields(etud, ETUD_FIELDS)
|
||||||
assert fields_ok is True
|
assert fields_ok is True
|
||||||
|
assert etud["dept_acronym"] == DEPT_ACRONYM
|
||||||
######### Test code ine #########
|
######### Test code ine #########
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
@ -93,7 +98,7 @@ def test_etudiant(api_headers):
|
|||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
etud = r.json()
|
etud = r.json()
|
||||||
assert len(etud) == 24
|
assert len(etud) == 25
|
||||||
fields_ok = verify_fields(etud, ETUD_FIELDS)
|
fields_ok = verify_fields(etud, ETUD_FIELDS)
|
||||||
assert fields_ok is True
|
assert fields_ok is True
|
||||||
|
|
||||||
|
@ -24,6 +24,11 @@ from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
|
|||||||
from tests.api.tools_test_api import MODIMPL_FIELDS, 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
|
from tests.api.tools_test_api import FSEM_FIELDS, UE_FIELDS, MODULE_FIELDS
|
||||||
|
|
||||||
|
# Etudiant pour les tests
|
||||||
|
ETUDID = 1
|
||||||
|
NIP = "1"
|
||||||
|
INE = "INE1"
|
||||||
|
|
||||||
|
|
||||||
def test_formsemestre(api_headers):
|
def test_formsemestre(api_headers):
|
||||||
"""
|
"""
|
||||||
@ -53,7 +58,7 @@ def test_etudiant_bulletin(api_headers):
|
|||||||
bull_a = r.json()
|
bull_a = r.json()
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
f"{API_URL}/etudiant/nip/1/formsemestre/{formsemestre_id}/bulletin",
|
f"{API_URL}/etudiant/nip/{NIP}/formsemestre/{formsemestre_id}/bulletin",
|
||||||
headers=api_headers,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
@ -61,7 +66,7 @@ def test_etudiant_bulletin(api_headers):
|
|||||||
bull_b = r.json()
|
bull_b = r.json()
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
f"{API_URL}/etudiant/ine/1/formsemestre/{formsemestre_id}/bulletin",
|
f"{API_URL}/etudiant/ine/{INE}/formsemestre/{formsemestre_id}/bulletin",
|
||||||
headers=api_headers,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
|
@ -21,6 +21,8 @@ ETUD_FIELDS = {
|
|||||||
"code_nip",
|
"code_nip",
|
||||||
"codepostaldomicile",
|
"codepostaldomicile",
|
||||||
"date_naissance",
|
"date_naissance",
|
||||||
|
"dept_acronym",
|
||||||
|
"dept_id",
|
||||||
"dept_naissance",
|
"dept_naissance",
|
||||||
"description",
|
"description",
|
||||||
"domicile",
|
"domicile",
|
||||||
|
Loading…
Reference in New Issue
Block a user