Merge branch 'master' of https://scodoc.org/git/viennet/ScoDoc into dev93

This commit is contained in:
Emmanuel Viennet 2022-05-09 20:43:15 +02:00
commit 91544aa254
9 changed files with 154 additions and 85 deletions

View File

@ -14,8 +14,8 @@ 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/<int:nip>", methods=["GET"]) @bp.route("/absences/nip/<string:nip>", methods=["GET"])
@bp.route("/absences/ine/<int:ine>", 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, nip: int = None, ine: int = None):

View File

@ -55,8 +55,8 @@ def etudiants_courant(long=False):
@bp.route("/etudiant/etudid/<int:etudid>", methods=["GET"]) @bp.route("/etudiant/etudid/<int:etudid>", methods=["GET"])
@bp.route("/etudiant/nip/<int:nip>", methods=["GET"]) @bp.route("/etudiant/nip/<string:nip>", methods=["GET"])
@bp.route("/etudiant/ine/<int: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: int = None, ine: int = None):
@ -109,8 +109,8 @@ def etudiant(etudid: int = None, nip: int = None, ine: int = None):
@bp.route("/etudiant/etudid/<int:etudid>/formsemestres") @bp.route("/etudiant/etudid/<int:etudid>/formsemestres")
@bp.route("/etudiant/nip/<int:nip>/formsemestres") @bp.route("/etudiant/nip/<string:nip>/formsemestres")
@bp.route("/etudiant/ine/<int:ine>/formsemestres") @bp.route("/etudiant/ine/<string:ine>/formsemestres")
@token_auth.login_required @token_auth.login_required
@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):
@ -175,12 +175,12 @@ def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None)
defaults={"version": "long"}, defaults={"version": "long"},
) )
@bp.route( @bp.route(
"/etudiant/nip/<int:nip>/formsemestre/<int:formsemestre_id>/bulletin", "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin",
methods=["GET"], methods=["GET"],
defaults={"version": "long"}, defaults={"version": "long"},
) )
@bp.route( @bp.route(
"/etudiant/ine/<int:ine>/formsemestre/<int:formsemestre_id>/bulletin", "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin",
methods=["GET"], methods=["GET"],
defaults={"version": "long"}, defaults={"version": "long"},
) )
@ -190,12 +190,12 @@ def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None)
defaults={"version": "short"}, defaults={"version": "short"},
) )
@bp.route( @bp.route(
"/etudiant/nip/<int:nip>/formsemestre/<int:formsemestre_id>/bulletin/short", "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/short",
methods=["GET"], methods=["GET"],
defaults={"version": "short"}, defaults={"version": "short"},
) )
@bp.route( @bp.route(
"/etudiant/ine/<int:ine>/formsemestre/<int:formsemestre_id>/bulletin/short", "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/short",
methods=["GET"], methods=["GET"],
defaults={"version": "short"}, defaults={"version": "short"},
) )
@ -408,10 +408,12 @@ def etudiant_bulletin_semestre(
methods=["GET"], methods=["GET"],
) )
@bp.route( @bp.route(
"/etudiant/nip/<int:nip>/formsemestre/<int:formsemestre_id>/groups", methods=["GET"] "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/groups",
methods=["GET"],
) )
@bp.route( @bp.route(
"/etudiant/ine/<int:ine>/formsemestre/<int:formsemestre_id>/groups", methods=["GET"] "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/groups",
methods=["GET"],
) )
@token_auth.login_required @token_auth.login_required
@token_permission_required(Permission.APIView) @token_permission_required(Permission.APIView)

View File

@ -413,8 +413,15 @@ def formsemestre_get_etud_groupnames(formsemestre_id, attr="group_name"):
return R return R
def etud_add_group_infos(etud, formsemestre_id, sep=" "): def etud_add_group_infos(etud, formsemestre_id, sep=" ", only_to_show=False):
"""Add informations on partitions and group memberships to etud (a dict with an etudid)""" """Add informations on partitions and group memberships to etud
(a dict with an etudid)
If only_to_show, restrict to partions such that show_in_lists is True.
etud['partitions'] = { partition_id : group + partition_name }
etud['groupes'] = "TDB, Gr2, TPB1"
etud['partitionsgroupes'] = "Groupes TD:TDB, Groupes TP:Gr2 (...)"
"""
etud[ etud[
"partitions" "partitions"
] = collections.OrderedDict() # partition_id : group + partition_name ] = collections.OrderedDict() # partition_id : group + partition_name
@ -423,11 +430,14 @@ def etud_add_group_infos(etud, formsemestre_id, sep=" "):
return etud return etud
infos = ndb.SimpleDictFetch( infos = ndb.SimpleDictFetch(
"""SELECT p.partition_name, g.*, g.id AS group_id """SELECT p.partition_name, p.show_in_lists, g.*, g.id AS group_id
FROM group_descr g, partition p, group_membership gm WHERE gm.etudid=%(etudid)s FROM group_descr g, partition p, group_membership gm WHERE gm.etudid=%(etudid)s
and gm.group_id = g.id and gm.group_id = g.id
and g.partition_id = p.id and g.partition_id = p.id
and p.formsemestre_id = %(formsemestre_id)s and p.formsemestre_id = %(formsemestre_id)s
"""
+ (" and (p.show_in_lists is True) " if only_to_show else "")
+ """
ORDER BY p.numero ORDER BY p.numero
""", """,
{"etudid": etud["etudid"], "formsemestre_id": formsemestre_id}, {"etudid": etud["etudid"], "formsemestre_id": formsemestre_id},

View File

@ -153,14 +153,14 @@ def ficheEtud(etudid=None):
try: # pour les bookmarks avec d'anciens ids... try: # pour les bookmarks avec d'anciens ids...
etudid = int(etudid) etudid = int(etudid)
except ValueError: except ValueError:
raise ScoValueError("id invalide !") raise ScoValueError("id invalide !") from ValueError
# la sidebar est differente s'il y a ou pas un etudid # la sidebar est differente s'il y a ou pas un etudid
# voir html_sidebar.sidebar() # voir html_sidebar.sidebar()
g.etudid = etudid g.etudid = etudid
args = make_etud_args(etudid=etudid) args = make_etud_args(etudid=etudid)
etuds = sco_etud.etudident_list(cnx, args) etuds = sco_etud.etudident_list(cnx, args)
if not etuds: if not etuds:
log("ficheEtud: etudid=%s request.args=%s" % (etudid, request.args)) log(f"ficheEtud: etudid={etudid!r} request.args={request.args!r}")
raise ScoValueError("Etudiant inexistant !") raise ScoValueError("Etudiant inexistant !")
etud = etuds[0] etud = etuds[0]
etudid = etud["etudid"] etudid = etud["etudid"]
@ -173,7 +173,7 @@ def ficheEtud(etudid=None):
if info["lieu_naissance"]: if info["lieu_naissance"]:
info["info_naissance"] += " à " + info["lieu_naissance"] info["info_naissance"] += " à " + info["lieu_naissance"]
if info["dept_naissance"]: if info["dept_naissance"]:
info["info_naissance"] += " (%s)" % info["dept_naissance"] info["info_naissance"] += f" ({info['dept_naissance']})"
info["etudfoto"] = sco_photos.etud_photo_html(etud) info["etudfoto"] = sco_photos.etud_photo_html(etud)
if ( if (
(not info["domicile"]) (not info["domicile"])
@ -205,7 +205,7 @@ def ficheEtud(etudid=None):
) )
else: else:
info["emaillink"] = "<em>(pas d'adresse e-mail)</em>" info["emaillink"] = "<em>(pas d'adresse e-mail)</em>"
# champs dependant des permissions # Champ dépendant des permissions:
if authuser.has_permission(Permission.ScoEtudChangeAdr): if authuser.has_permission(Permission.ScoEtudChangeAdr):
info["modifadresse"] = ( info["modifadresse"] = (
'<a class="stdlink" href="formChangeCoordonnees?etudid=%s">modifier adresse</a>' '<a class="stdlink" href="formChangeCoordonnees?etudid=%s">modifier adresse</a>'
@ -216,9 +216,10 @@ def ficheEtud(etudid=None):
# Groupes: # Groupes:
sco_groups.etud_add_group_infos( sco_groups.etud_add_group_infos(
info, info["cursem"]["formsemestre_id"] if info["cursem"] else None info,
info["cursem"]["formsemestre_id"] if info["cursem"] else None,
only_to_show=True,
) )
# Parcours de l'étudiant # Parcours de l'étudiant
if info["sems"]: if info["sems"]:
info["last_formsemestre_id"] = info["sems"][0]["formsemestre_id"] info["last_formsemestre_id"] = info["sems"][0]["formsemestre_id"]
@ -235,15 +236,28 @@ def ficheEtud(etudid=None):
) )
grlink = '<span class="fontred">%s</span>' % descr["situation"] grlink = '<span class="fontred">%s</span>' % descr["situation"]
else: else:
group = sco_groups.get_etud_main_group(etudid, sem["formsemestre_id"]) e = {"etudid": etudid}
if group["partition_name"]: sco_groups.etud_add_group_infos(
gr_name = group["group_name"] e,
sem["formsemestre_id"],
only_to_show=True,
)
grlinks = []
for partition in e["partitions"].values():
if partition["partition_name"]:
gr_name = partition["group_name"]
else: else:
gr_name = "tous" gr_name = "tous"
grlink = (
'<a class="discretelink" href="groups_view?group_ids=%s" title="Liste du groupe">groupe %s</a>' grlinks.append(
% (group["group_id"], gr_name) f"""<a class="discretelink" href="{
url_for('scolar.groups_view',
scodoc_dept=g.scodoc_dept, group_ids=partition['group_id'])
}" title="Liste du groupe {gr_name}">{gr_name}</a>
"""
) )
grlink = ", ".join(grlinks)
# infos ajoutées au semestre dans le parcours (groupe, menu) # infos ajoutées au semestre dans le parcours (groupe, menu)
menu = _menuScolarite(authuser, sem, etudid) menu = _menuScolarite(authuser, sem, etudid)
if menu: if menu:
@ -296,9 +310,9 @@ def ficheEtud(etudid=None):
if not sco_permissions_check.can_suppress_annotation(a["id"]): if not sco_permissions_check.can_suppress_annotation(a["id"]):
a["dellink"] = "" a["dellink"] = ""
else: else:
a[ a["dellink"] = (
"dellink" '<td class="annodel"><a href="doSuppressAnnotation?etudid=%s&annotation_id=%s">%s</a></td>'
] = '<td class="annodel"><a href="doSuppressAnnotation?etudid=%s&annotation_id=%s">%s</a></td>' % ( % (
etudid, etudid,
a["id"], a["id"],
scu.icontag( scu.icontag(
@ -308,6 +322,7 @@ def ficheEtud(etudid=None):
title="Supprimer cette annotation", title="Supprimer cette annotation",
), ),
) )
)
author = sco_users.user_info(a["author"]) author = sco_users.user_info(a["author"])
alist.append( alist.append(
f"""<tr><td><span class="annodate">Le {a['date']} par {author['prenomnom']} : f"""<tr><td><span class="annodate">Le {a['date']} par {author['prenomnom']} :
@ -422,9 +437,11 @@ def ficheEtud(etudid=None):
# #
if info["groupes"].strip(): if info["groupes"].strip():
info["groupes_row"] = ( info[
'<tr><td class="fichetitre2">Groupe :</td><td>%(groupes)s</td></tr>' % info "groupes_row"
) ] = f"""<tr>
<td class="fichetitre2">Groupes :</td><td>{info['groupes']}</td>
</tr>"""
else: else:
info["groupes_row"] = "" info["groupes_row"] = ""
info["menus_etud"] = menus_etud(etudid) info["menus_etud"] = menus_etud(etudid)

View File

@ -84,12 +84,15 @@ def formsemestre_recapcomplet(
selected_etudid: etudid sélectionné (pour scroller au bon endroit) selected_etudid: etudid sélectionné (pour scroller au bon endroit)
""" """
formsemestre = FormSemestre.query.get_or_404(formsemestre_id) formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
file_formats = {"csv", "json", "xls", "xlsx", "xlsall", "xml"}
supported_formats = file_formats | {"html"}
if tabformat not in supported_formats:
raise ScoValueError(f"Format non supporté: {tabformat}")
is_file = tabformat in file_formats
modejury = int(modejury) modejury = int(modejury)
xml_with_decisions = int(xml_with_decisions) xml_with_decisions = int(xml_with_decisions)
force_publishing = int(force_publishing) force_publishing = int(force_publishing)
is_file = tabformat in {"csv", "json", "xls", "xlsx", "xlsall", "xml"}
data = _do_formsemestre_recapcomplet( data = _do_formsemestre_recapcomplet(
formsemestre_id, formsemestre_id,
format=tabformat, format=tabformat,
@ -128,6 +131,7 @@ 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é"),
("xml", "Bulletins XML (obsolète)"), ("xml", "Bulletins XML (obsolète)"),
("json", "Bulletins JSON"), ("json", "Bulletins JSON"),
): ):

View File

@ -1,7 +1,7 @@
# -*- mode: python -*- # -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
SCOVERSION = "9.2.17" SCOVERSION = "9.2.18"
SCONAME = "ScoDoc" SCONAME = "ScoDoc"

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Test Logos """Test API: accès aux étudiants
Utilisation : Utilisation :
créer les variables d'environnement: (indiquer les valeurs créer les variables d'environnement: (indiquer les valeurs
@ -58,7 +58,7 @@ def test_etudiants_courant(api_headers):
def test_etudiant(api_headers): def test_etudiant(api_headers):
""" """
Route: Routes: /etudiant/etudid, /etudiant/nip, /etudiant/ine
""" """
######### Test etudid ######### ######### Test etudid #########
@ -87,7 +87,7 @@ def test_etudiant(api_headers):
######### Test code ine ######### ######### Test code ine #########
r = requests.get( r = requests.get(
API_URL + "/etudiant/ine/1", API_URL + "/etudiant/ine/INE1",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
@ -97,6 +97,39 @@ def test_etudiant(api_headers):
fields_ok = verify_fields(etud, ETUD_FIELDS) fields_ok = verify_fields(etud, ETUD_FIELDS)
assert fields_ok is True assert fields_ok is True
# Vérifie le requetage des 3 1er étudiants
for etudid in (1, 2, 3):
r = requests.get(
f"{API_URL }/etudiant/etudid/{etudid}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etud = r.json()
nip = etud["code_nip"]
ine = etud["code_ine"]
assert isinstance(etud["id"], int)
assert isinstance(nip, str)
assert isinstance(ine, str)
r = requests.get(
f"{API_URL }/etudiant/nip/{nip}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etud_nip = r.json()
# On doit avoir obtenue le même étudiant
assert etud_nip == etud
r = requests.get(
f"{API_URL }/etudiant/ine/{ine}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etud_ine = r.json()
# On doit avoir obtenue le même étudiant
assert etud_ine == etud
def test_etudiant_formsemestres(api_headers): def test_etudiant_formsemestres(api_headers):
""" """
@ -132,7 +165,7 @@ def test_etudiant_formsemestres(api_headers):
######### Test code ine ######### ######### Test code ine #########
r = requests.get( r = requests.get(
API_URL + "/etudiant/ine/1/formsemestres", API_URL + "/etudiant/ine/INE1/formsemestres",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
@ -172,7 +205,7 @@ def test_etudiant_bulletin_semestre(api_headers):
######### Test code ine ######### ######### Test code ine #########
r = requests.get( r = requests.get(
API_URL + "/etudiant/ine/1/formsemestre/1/bulletin", API_URL + "/etudiant/ine/INE1/formsemestre/1/bulletin",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
@ -235,7 +268,7 @@ def test_etudiant_groups(api_headers):
######### Test code ine ######### ######### Test code ine #########
r = requests.get( r = requests.get(
API_URL + "/etudiant/ine/1/formsemestre/1/groups", API_URL + "/etudiant/ine/INE1/formsemestre/1/groups",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Test Logos """Test API Jurys XXX TODO A ECRIRE
Utilisation : Utilisation :
créer les variables d'environnement: (indiquer les valeurs créer les variables d'environnement: (indiquer les valeurs
@ -77,30 +77,30 @@ def test_set_decision_jury(api_headers):
assert r.status_code == 200 assert r.status_code == 200
def test_annule_decision_jury(api_headers): # def test_annule_decision_jury(api_headers):
""" # """
Route: # Route:
""" # """
r = requests.get( # r = requests.get(
SCODOC_URL # SCODOC_URL
+ "/ScoDoc/api/jury/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/annule_decision", # + "/ScoDoc/api/jury/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/annule_decision",
headers=api_headers, # headers=api_headers,
verify=CHECK_CERTIFICATE, # verify=CHECK_CERTIFICATE,
) # )
assert r.status_code == 200 # assert r.status_code == 200
r = requests.get( # r = requests.get(
SCODOC_URL # SCODOC_URL
+ "/ScoDoc/api/jury/nip/<int:nip>/formsemestre/<int:formsemestre_id>/annule_decision", # + "/ScoDoc/api/jury/nip/<int:nip>/formsemestre/<int:formsemestre_id>/annule_decision",
headers=api_headers, # headers=api_headers,
verify=CHECK_CERTIFICATE, # verify=CHECK_CERTIFICATE,
) # )
assert r.status_code == 200 # assert r.status_code == 200
r = requests.get( # r = requests.get(
SCODOC_URL # SCODOC_URL
+ "/ScoDoc/api/jury/ine/<int:ine>/formsemestre/<int:formsemestre_id>/annule_decision", # + "/ScoDoc/api/jury/ine/<int:ine>/formsemestre/<int:formsemestre_id>/annule_decision",
headers=api_headers, # headers=api_headers,
verify=CHECK_CERTIFICATE, # verify=CHECK_CERTIFICATE,
) # )
assert r.status_code == 200 # assert r.status_code == 200

View File

@ -28,7 +28,7 @@ import sys
from app.auth.models import Role, User from app.auth.models import Role, User
from app import models from app import models
from app.models import Departement, Formation, FormSemestre from app.models import Departement, Formation, FormSemestre, Identite
from app import db from app import db
from app.scodoc import ( from app.scodoc import (
sco_cache, sco_cache,
@ -95,15 +95,18 @@ def create_users(dept: Departement) -> tuple:
return user, other return user, other
def create_fake_etud(dept: Departement) -> models.Identite: def create_fake_etud(dept: Departement) -> Identite:
"""Créé un faux étudiant et l'insère dans la base""" """Créé un faux étudiant et l'insère dans la base."""
civilite = random.choice(("M", "F", "X")) civilite = random.choice(("M", "F", "X"))
nom, prenom = nomprenom(civilite) nom, prenom = nomprenom(civilite)
etud = models.Identite(civilite=civilite, nom=nom, prenom=prenom, dept_id=dept.id) etud: Identite = Identite(
civilite=civilite, nom=nom, prenom=prenom, dept_id=dept.id
)
db.session.add(etud) db.session.add(etud)
db.session.commit() db.session.commit()
etud.code_nip = etud.id # créé un étudiant sur deux avec un NIP et INE alphanumérique
etud.code_ine = etud.id etud.code_nip = f"{etud.id}" if (etud.id % 2) else f"NIP{etud.id}"
etud.code_ine = f"INE{etud.id}" if (etud.id % 2) else f"{etud.id}"
db.session.add(etud) db.session.add(etud)
db.session.commit() db.session.commit()
adresse = models.Adresse( adresse = models.Adresse(