Génère JSON avec Flask-JSON. Abandonne jsonify.

This commit is contained in:
Emmanuel Viennet 2023-04-06 16:10:32 +02:00 committed by iziram
parent 0948734ff2
commit 36a0784897
29 changed files with 269 additions and 692 deletions

View File

@ -18,7 +18,7 @@ import warnings
from flask import current_app, g, request
from flask import Flask
from flask import abort, flash, has_request_context, jsonify
from flask import abort, flash, has_request_context
from flask import render_template
# from flask.json import JSONEncoder
@ -26,6 +26,7 @@ from flask.logging import default_handler
from flask_bootstrap import Bootstrap
from flask_caching import Cache
from flask_json import FlaskJSON, json_response
from flask_login import LoginManager, current_user
from flask_mail import Mail
from flask_migrate import Migrate
@ -140,12 +141,14 @@ def _async_dump(app, request_url: str):
def handle_invalid_usage(error):
response = jsonify(error.to_dict())
response = json_response(data_=error.to_dict())
response.status_code = error.status_code
return response
# JSON ENCODING
# used by some internal finctions
# the API is now using flask_son, NOT THIS ENCODER
class ScoDocJSONEncoder(json.JSONEncoder):
def default(self, o): # pylint: disable=E0202
if isinstance(o, (datetime.date, datetime.datetime)):
@ -252,13 +255,13 @@ class ReverseProxied(object):
def create_app(config_class=DevConfig):
app = Flask(__name__, static_url_path="/ScoDoc/static", static_folder="static")
app.config.from_object(config_class)
from app.auth import cas
CAS(app, url_prefix="/cas", configuration_function=cas.set_cas_configuration)
app.wsgi_app = ReverseProxied(app.wsgi_app)
app.json_provider_class = ScoDocJSONEncoder
FlaskJSON(app)
app.config.from_object(config_class)
# Pour conserver l'ordre des objets dans les JSON:
# e.g. l'ordre des UE dans les bulletins
app.json.sort_keys = False

View File

@ -6,7 +6,7 @@
"""ScoDoc 9 API : Absences
"""
from flask import jsonify
from flask_json import as_json
from app.api import api_bp as bp, API_CLIENT_ERROR
from app.scodoc.sco_utils import json_error
@ -19,10 +19,12 @@ from app.scodoc import sco_abs
from app.scodoc.sco_groups import get_group_members
from app.scodoc.sco_permissions import Permission
# TODO XXX revoir routes web API et calcul des droits
@bp.route("/absences/etudid/<int:etudid>", methods=["GET"])
@scodoc
@permission_required(Permission.ScoView)
@as_json
def absences(etudid: int = None):
"""
Liste des absences de cet étudiant
@ -57,12 +59,13 @@ def absences(etudid: int = None):
abs_list = sco_abs.list_abs_date(etud.id)
for absence in abs_list:
absence["jour"] = absence["jour"].isoformat()
return jsonify(abs_list)
return abs_list
@bp.route("/absences/etudid/<int:etudid>/just", methods=["GET"])
@scodoc
@permission_required(Permission.ScoView)
@as_json
def absences_just(etudid: int = None):
"""
Retourne la liste des absences justifiées d'un étudiant donné
@ -103,7 +106,7 @@ def absences_just(etudid: int = None):
]
for absence in abs_just:
absence["jour"] = absence["jour"].isoformat()
return jsonify(abs_just)
return abs_just
@bp.route(
@ -116,6 +119,7 @@ def absences_just(etudid: int = None):
)
@scodoc
@permission_required(Permission.ScoView)
@as_json
def abs_groupe_etat(group_id: int, date_debut=None, date_fin=None):
"""
Liste des absences d'un groupe (possibilité de choisir entre deux dates)
@ -167,7 +171,7 @@ def abs_groupe_etat(group_id: int, date_debut=None, date_fin=None):
}
data.append(absence)
return jsonify(data)
return data
# XXX TODO EV: A REVOIR (data json dans le POST + modifier les routes)

View File

@ -8,7 +8,8 @@
API : billets d'absences
"""
from flask import g, jsonify, request
from flask import g, request
from flask_json import as_json
from flask_login import login_required
from app import db
@ -26,10 +27,11 @@ from app.scodoc.sco_permissions import Permission
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def billets_absence_etudiant(etudid: int):
"""Liste des billets d'absence pour cet étudiant"""
billets = sco_abs_billets.query_billets_etud(etudid)
return jsonify([billet.to_dict() for billet in billets])
return [billet.to_dict() for billet in billets]
@bp.route("/billets_absence/create", methods=["POST"])
@ -37,6 +39,7 @@ def billets_absence_etudiant(etudid: int):
@login_required
@scodoc
@permission_required(Permission.ScoAbsAddBillet)
@as_json
def billets_absence_create():
"""Ajout d'un billet d'absence"""
data = request.get_json(force=True) # may raise 400 Bad Request
@ -60,7 +63,7 @@ def billets_absence_create():
)
db.session.add(billet)
db.session.commit()
return jsonify(billet.to_dict())
return billet.to_dict()
@bp.route("/billets_absence/<int:billet_id>/delete", methods=["POST"])
@ -68,6 +71,7 @@ def billets_absence_create():
@login_required
@scodoc
@permission_required(Permission.ScoAbsAddBillet)
@as_json
def billets_absence_delete(billet_id: int):
"""Suppression d'un billet d'absence"""
query = BilletAbsence.query.filter_by(id=billet_id)
@ -77,4 +81,4 @@ def billets_absence_delete(billet_id: int):
billet = query.first_or_404()
db.session.delete(billet)
db.session.commit()
return jsonify({"OK": True})
return {"OK": True}

View File

@ -12,7 +12,8 @@
"""
from datetime import datetime
from flask import jsonify, request
from flask import request
from flask_json import as_json
from flask_login import login_required
import app
@ -41,24 +42,27 @@ def get_departement(dept_ident: str) -> Departement:
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def departements_list():
"""Liste les départements"""
return jsonify([dept.to_dict(with_dept_name=True) for dept in Departement.query])
return [dept.to_dict(with_dept_name=True) for dept in Departement.query]
@bp.route("/departements_ids")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def departements_ids():
"""Liste des ids de départements"""
return jsonify([dept.id for dept in Departement.query])
return [dept.id for dept in Departement.query]
@bp.route("/departement/<string:acronym>")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def departement(acronym: str):
"""
Info sur un département. Accès par acronyme.
@ -74,25 +78,27 @@ def departement(acronym: str):
}
"""
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
return jsonify(dept.to_dict(with_dept_name=True))
return dept.to_dict(with_dept_name=True)
@bp.route("/departement/id/<int:dept_id>")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def departement_by_id(dept_id: int):
"""
Info sur un département. Accès par id.
"""
dept = Departement.query.get_or_404(dept_id)
return jsonify(dept.to_dict())
return dept.to_dict()
@bp.route("/departement/create", methods=["POST"])
@login_required
@scodoc
@permission_required(Permission.ScoSuperAdmin)
@as_json
def departement_create():
"""
Création d'un département.
@ -111,13 +117,14 @@ def departement_create():
dept = departements.create_dept(acronym, visible=visible)
except ScoValueError as exc:
return json_error(500, exc.args[0] if exc.args else "")
return jsonify(dept.to_dict())
return dept.to_dict()
@bp.route("/departement/<string:acronym>/edit", methods=["POST"])
@login_required
@scodoc
@permission_required(Permission.ScoSuperAdmin)
@as_json
def departement_edit(acronym):
"""
Edition d'un département: seul visible peut être modifié
@ -135,7 +142,7 @@ def departement_edit(acronym):
dept.visible = visible
db.session.add(dept)
db.session.commit()
return jsonify(dept.to_dict())
return dept.to_dict()
@bp.route("/departement/<string:acronym>/delete", methods=["POST"])
@ -149,13 +156,14 @@ def departement_delete(acronym):
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
db.session.delete(dept)
db.session.commit()
return jsonify({"OK": True})
return {"OK": True}
@bp.route("/departement/<string:acronym>/etudiants", methods=["GET"])
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def dept_etudiants(acronym: str):
"""
Retourne la liste des étudiants d'un département
@ -179,45 +187,49 @@ def dept_etudiants(acronym: str):
]
"""
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
return jsonify([etud.to_dict_short() for etud in dept.etudiants])
return [etud.to_dict_short() for etud in dept.etudiants]
@bp.route("/departement/id/<int:dept_id>/etudiants")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def dept_etudiants_by_id(dept_id: int):
"""
Retourne la liste des étudiants d'un département d'id donné.
"""
dept = Departement.query.get_or_404(dept_id)
return jsonify([etud.to_dict_short() for etud in dept.etudiants])
return [etud.to_dict_short() for etud in dept.etudiants]
@bp.route("/departement/<string:acronym>/formsemestres_ids")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def dept_formsemestres_ids(acronym: str):
"""liste des ids formsemestre du département"""
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
return jsonify([formsemestre.id for formsemestre in dept.formsemestres])
return [formsemestre.id for formsemestre in dept.formsemestres]
@bp.route("/departement/id/<int:dept_id>/formsemestres_ids")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def dept_formsemestres_ids_by_id(dept_id: int):
"""liste des ids formsemestre du département"""
dept = Departement.query.get_or_404(dept_id)
return jsonify([formsemestre.id for formsemestre in dept.formsemestres])
return [formsemestre.id for formsemestre in dept.formsemestres]
@bp.route("/departement/<string:acronym>/formsemestres_courants")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def dept_formsemestres_courants(acronym: str):
"""
Liste des semestres actifs d'un département d'acronyme donné
@ -269,13 +281,14 @@ def dept_formsemestres_courants(acronym: str):
FormSemestre.date_debut <= test_date,
FormSemestre.date_fin >= test_date,
)
return jsonify([d.to_dict_api() for d in formsemestres])
return [d.to_dict_api() for d in formsemestres]
@bp.route("/departement/id/<int:dept_id>/formsemestres_courants")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def dept_formsemestres_courants_by_id(dept_id: int):
"""
Liste des semestres actifs d'un département d'id donné
@ -294,4 +307,4 @@ def dept_formsemestres_courants_by_id(dept_id: int):
FormSemestre.date_fin >= test_date,
)
return jsonify([d.to_dict_api() for d in formsemestres])
return [d.to_dict_api() for d in formsemestres]

View File

@ -9,7 +9,8 @@
"""
from datetime import datetime
from flask import abort, g, jsonify, request
from flask import g, request
from flask_json import as_json
from flask_login import current_user
from flask_login import login_required
from sqlalchemy import desc, or_
@ -38,11 +39,11 @@ import app.scodoc.sco_photos as sco_photos
# @login_required
# @scodoc
# @permission_required(Permission.ScoView)
# @as_json
# def api_function(arg: int):
# """Une fonction quelconque de l'API"""
# return jsonify(
# {"current_user": current_user.to_dict(), "arg": arg, "dept": g.scodoc_dept}
# )
# return {"current_user": current_user.to_dict(), "arg": arg, "dept": g.scodoc_dept}
#
@bp.route("/etudiants/courants", defaults={"long": False})
@ -52,6 +53,7 @@ import app.scodoc.sco_photos as sco_photos
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def etudiants_courants(long=False):
"""
La liste des étudiants des semestres "courants" (tous départements)
@ -97,7 +99,7 @@ def etudiants_courants(long=False):
data = [etud.to_dict_api() for etud in etuds]
else:
data = [etud.to_dict_short() for etud in etuds]
return jsonify(data)
return data
@bp.route("/etudiant/etudid/<int:etudid>")
@ -109,6 +111,7 @@ def etudiants_courants(long=False):
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def etudiant(etudid: int = None, nip: str = None, ine: str = None):
"""
Retourne les informations de l'étudiant correspondant, ou 404 si non trouvé.
@ -128,7 +131,7 @@ def etudiant(etudid: int = None, nip: str = None, ine: str = None):
message="étudiant inconnu",
)
return jsonify(etud.to_dict_api())
return etud.to_dict_api()
@api_web_bp.route("/etudiant/etudid/<int:etudid>/photo")
@ -175,6 +178,7 @@ def get_photo_image(etudid: int = None, nip: str = None, ine: str = None):
@api_web_bp.route("/etudiants/ine/<string:ine>", methods=["GET"])
@scodoc
@permission_required(Permission.ScoView)
@as_json
def etudiants(etudid: int = None, nip: str = None, ine: str = None):
"""
Info sur le ou les étudiants correspondant. Comme /etudiant mais renvoie
@ -200,7 +204,7 @@ def etudiants(etudid: int = None, nip: str = None, ine: str = None):
etuds = etuds.join(Departement).filter(
or_(Departement.acronym == acronym for acronym in allowed_depts)
)
return jsonify([etud.to_dict_api() for etud in query])
return [etud.to_dict_api() for etud in query]
@bp.route("/etudiant/etudid/<int:etudid>/formsemestres")
@ -211,6 +215,7 @@ def etudiants(etudid: int = None, nip: str = None, ine: str = None):
@api_web_bp.route("/etudiant/ine/<string:ine>/formsemestres")
@scodoc
@permission_required(Permission.ScoView)
@as_json
def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None):
"""
Liste des semestres qu'un étudiant a suivi, triés par ordre chronologique.
@ -243,7 +248,7 @@ def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None)
formsemestres = query.order_by(FormSemestre.date_debut)
return jsonify([formsemestre.to_dict_api() for formsemestre in formsemestres])
return [formsemestre.to_dict_api() for formsemestre in formsemestres]
@bp.route(
@ -302,7 +307,7 @@ def bulletin(
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first_or_404()
dept = Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
if g.scodoc_dept and dept.acronym != g.scodoc_dept:
return json_error(404, "formsemestre inexistant")
return json_error(404, "formsemestre inexistant", as_response=True)
app.set_sco_dept(dept.acronym)
if code_type == "nip":
@ -340,6 +345,7 @@ def bulletin(
)
@scodoc
@permission_required(Permission.ScoView)
@as_json
def etudiant_groups(formsemestre_id: int, etudid: int = None):
"""
Retourne la liste des groupes auxquels appartient l'étudiant dans le formsemestre indiqué
@ -389,4 +395,4 @@ def etudiant_groups(formsemestre_id: int, etudid: int = None):
app.set_sco_dept(dept.acronym)
data = sco_groups.get_etud_groups(etud.id, formsemestre.id)
return jsonify(data)
return data

View File

@ -8,7 +8,8 @@
ScoDoc 9 API : accès aux évaluations
"""
from flask import g, jsonify
from flask import g
from flask_json import as_json
from flask_login import login_required
import app
@ -26,7 +27,8 @@ import app.scodoc.sco_utils as scu
@login_required
@scodoc
@permission_required(Permission.ScoView)
def evaluation(evaluation_id: int):
@as_json
def the_eval(evaluation_id: int):
"""Description d'une évaluation.
{
@ -56,7 +58,7 @@ def evaluation(evaluation_id: int):
.filter_by(dept_id=g.scodoc_dept_id)
)
e = query.first_or_404()
return jsonify(e.to_dict_api())
return e.to_dict_api()
@bp.route("/moduleimpl/<int:moduleimpl_id>/evaluations")
@ -64,6 +66,7 @@ def evaluation(evaluation_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def evaluations(moduleimpl_id: int):
"""
Retourne la liste des évaluations d'un moduleimpl
@ -79,7 +82,7 @@ def evaluations(moduleimpl_id: int):
.join(FormSemestre)
.filter_by(dept_id=g.scodoc_dept_id)
)
return jsonify([e.to_dict_api() for e in query])
return [e.to_dict_api() for e in query]
@bp.route("/evaluation/<int:evaluation_id>/notes")
@ -87,6 +90,7 @@ def evaluations(moduleimpl_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def evaluation_notes(evaluation_id: int):
"""
Retourne la liste des notes à partir de l'id d'une évaluation donnée
@ -124,8 +128,8 @@ def evaluation_notes(evaluation_id: int):
.filter_by(dept_id=g.scodoc_dept_id)
)
evaluation = query.first_or_404()
dept = evaluation.moduleimpl.formsemestre.departement
the_eval = query.first_or_404()
dept = the_eval.moduleimpl.formsemestre.departement
app.set_sco_dept(dept.acronym)
notes = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
@ -133,7 +137,7 @@ def evaluation_notes(evaluation_id: int):
# "ABS", "EXC", etc mais laisse les notes sur le barème de l'éval.
note = notes[etudid]
note["value"] = scu.fmt_note(note["value"], keep_numeric=True)
note["note_max"] = evaluation.note_max
note["note_max"] = the_eval.note_max
del note["id"]
return jsonify(notes)
return notes

View File

@ -8,7 +8,8 @@
ScoDoc 9 API : accès aux formations
"""
from flask import g, jsonify, request
from flask import g, request
from flask_json import as_json
from flask_login import login_required
import app
@ -18,7 +19,6 @@ from app.scodoc.sco_utils import json_error
from app.decorators import scodoc, permission_required
from app.models import ApcParcours, Formation, FormSemestre, ModuleImpl, UniteEns
from app.scodoc import sco_formations
from app.scodoc.sco_exceptions import ScoFormationConflict
from app.scodoc.sco_permissions import Permission
@ -27,6 +27,7 @@ from app.scodoc.sco_permissions import Permission
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def formations():
"""
Retourne la liste de toutes les formations (tous départements)
@ -35,7 +36,7 @@ def formations():
if g.scodoc_dept:
query = query.filter_by(dept_id=g.scodoc_dept_id)
return jsonify([d.to_dict() for d in query])
return [d.to_dict() for d in query]
@bp.route("/formations_ids")
@ -43,6 +44,7 @@ def formations():
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def formations_ids():
"""
Retourne la liste de toutes les id de formations (tous départements)
@ -52,7 +54,7 @@ def formations_ids():
query = Formation.query
if g.scodoc_dept:
query = query.filter_by(dept_id=g.scodoc_dept_id)
return jsonify([d.id for d in query])
return [d.id for d in query]
@bp.route("/formation/<int:formation_id>")
@ -60,6 +62,7 @@ def formations_ids():
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def formation_by_id(formation_id: int):
"""
La formation d'id donné
@ -84,7 +87,7 @@ def formation_by_id(formation_id: int):
query = Formation.query.filter_by(id=formation_id)
if g.scodoc_dept:
query = query.filter_by(dept_id=g.scodoc_dept_id)
return jsonify(query.first_or_404().to_dict())
return query.first_or_404().to_dict()
@bp.route(
@ -106,6 +109,7 @@ def formation_by_id(formation_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def formation_export_by_formation_id(formation_id: int, export_ids=False):
"""
Retourne la formation, avec UE, matières, modules
@ -212,7 +216,7 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
except ValueError:
return json_error(500, message="Erreur inconnue")
return jsonify(data)
return data
@bp.route("/formation/<int:formation_id>/referentiel_competences")
@ -220,6 +224,7 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def referentiel_competences(formation_id: int):
"""
Retourne le référentiel de compétences
@ -233,8 +238,8 @@ def referentiel_competences(formation_id: int):
query = query.filter_by(dept_id=g.scodoc_dept_id)
formation = query.first_or_404(formation_id)
if formation.referentiel_competence is None:
return jsonify(None)
return jsonify(formation.referentiel_competence.to_dict())
return None
return formation.referentiel_competence.to_dict()
@bp.route("/moduleimpl/<int:moduleimpl_id>")
@ -242,6 +247,7 @@ def referentiel_competences(formation_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def moduleimpl(moduleimpl_id: int):
"""
Retourne un moduleimpl en fonction de son id
@ -281,7 +287,7 @@ def moduleimpl(moduleimpl_id: int):
if g.scodoc_dept:
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
modimpl: ModuleImpl = query.first_or_404()
return jsonify(modimpl.to_dict(convert_objects=True))
return modimpl.to_dict(convert_objects=True)
@bp.route("/set_ue_parcours/<int:ue_id>", methods=["POST"])
@ -289,6 +295,7 @@ def moduleimpl(moduleimpl_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoChangeFormation)
@as_json
def set_ue_parcours(ue_id: int):
"""Associe UE et parcours BUT.
La liste des ids de parcours est passée en argument JSON.
@ -307,4 +314,4 @@ def set_ue_parcours(ue_id: int):
]
log(f"set_ue_parcours: ue_id={ue.id} parcours_ids={parcours_ids}")
ok, error_message = ue.set_parcours(parcours)
return jsonify({"status": ok, "message": error_message})
return {"status": ok, "message": error_message}

View File

@ -9,7 +9,8 @@
"""
from operator import attrgetter, itemgetter
from flask import g, jsonify, request
from flask import g, request
from flask_json import as_json
from flask_login import login_required
import app
@ -42,6 +43,7 @@ from app.tables.recap import TableRecap
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def formsemestre_infos(formsemestre_id: int):
"""
Information sur le formsemestre indiqué.
@ -83,7 +85,7 @@ def formsemestre_infos(formsemestre_id: int):
if g.scodoc_dept:
query = query.filter_by(dept_id=g.scodoc_dept_id)
formsemestre: FormSemestre = query.first_or_404(formsemestre_id)
return jsonify(formsemestre.to_dict_api())
return formsemestre.to_dict_api()
@bp.route("/formsemestres/query")
@ -91,6 +93,7 @@ def formsemestre_infos(formsemestre_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def formsemestres_query():
"""
Retourne les formsemestres filtrés par
@ -146,7 +149,7 @@ def formsemestres_query():
formsemestres = formsemestres.join(FormSemestreInscription).join(Identite)
formsemestres = formsemestres.filter_by(code_ine=ine)
return jsonify([formsemestre.to_dict_api() for formsemestre in formsemestres])
return [formsemestre.to_dict_api() for formsemestre in formsemestres]
@bp.route("/formsemestre/<int:formsemestre_id>/bulletins")
@ -156,6 +159,7 @@ def formsemestres_query():
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def bulletins(formsemestre_id: int, version: str = "long"):
"""
Retourne les bulletins d'un formsemestre donné
@ -179,7 +183,7 @@ def bulletins(formsemestre_id: int, version: str = "long"):
)
data.append(bul_etu.json)
return jsonify(data)
return data
@bp.route("/formsemestre/<int:formsemestre_id>/programme")
@ -187,6 +191,7 @@ def bulletins(formsemestre_id: int, version: str = "long"):
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def formsemestre_programme(formsemestre_id: int):
"""
Retourne la liste des Ues, ressources et SAE d'un semestre
@ -266,15 +271,13 @@ def formsemestre_programme(formsemestre_id: int):
for modimpl in formsemestre.modimpls_sorted:
d = modimpl.to_dict(convert_objects=True)
m_list[modimpl.module.module_type].append(d)
return jsonify(
{
"ues": [ue.to_dict(convert_objects=True) for ue in ues],
"ressources": m_list[ModuleType.RESSOURCE],
"saes": m_list[ModuleType.SAE],
"modules": m_list[ModuleType.STANDARD],
"malus": m_list[ModuleType.MALUS],
}
)
return {
"ues": [ue.to_dict(convert_objects=True) for ue in ues],
"ressources": m_list[ModuleType.RESSOURCE],
"saes": m_list[ModuleType.SAE],
"modules": m_list[ModuleType.STANDARD],
"malus": m_list[ModuleType.MALUS],
}
@bp.route(
@ -312,6 +315,7 @@ def formsemestre_programme(formsemestre_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def formsemestre_etudiants(
formsemestre_id: int, with_query: bool = False, long: bool = False
):
@ -347,7 +351,7 @@ def formsemestre_etudiants(
etud["id"], formsemestre_id, exclude_default=True
)
return jsonify(sorted(etuds, key=itemgetter("sort_key")))
return sorted(etuds, key=itemgetter("sort_key"))
@bp.route("/formsemestre/<int:formsemestre_id>/etat_evals")
@ -355,6 +359,7 @@ def formsemestre_etudiants(
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def etat_evals(formsemestre_id: int):
"""
Informations sur l'état des évaluations d'un formsemestre.
@ -456,7 +461,7 @@ def etat_evals(formsemestre_id: int):
modimpl_dict["evaluations"] = list_eval
result.append(modimpl_dict)
return jsonify(result)
return result
@bp.route("/formsemestre/<int:formsemestre_id>/resultats")
@ -464,6 +469,7 @@ def etat_evals(formsemestre_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def formsemestre_resultat(formsemestre_id: int):
"""Tableau récapitulatif des résultats
Pour chaque étudiant, son état, ses groupes, ses moyennes d'UE et de modules.
@ -489,4 +495,4 @@ def formsemestre_resultat(formsemestre_id: int):
for row in rows:
row["partitions"] = etud_groups.get(row["etudid"], {})
return jsonify(rows)
return rows

View File

@ -8,7 +8,7 @@
ScoDoc 9 API : jury WIP
"""
from flask import jsonify
from flask_json import as_json
from flask_login import login_required
import app
@ -25,6 +25,7 @@ from app.scodoc.sco_permissions import Permission
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def decisions_jury(formsemestre_id: int):
"""Décisions du jury des étudiants du formsemestre."""
# APC, pair:
@ -32,6 +33,6 @@ def decisions_jury(formsemestre_id: int):
if formsemestre.formation.is_apc():
app.set_sco_dept(formsemestre.departement.acronym)
rows = jury_but_results.get_jury_but_results(formsemestre)
return jsonify(rows)
return rows
else:
raise ScoException("non implemente")

View File

@ -30,11 +30,10 @@ Contrib @jmp
"""
from datetime import datetime
from flask import jsonify, g, send_file
from flask_login import login_required
from flask import Response, send_file
from flask_json import as_json
from app.api import api_bp as bp, api_web_bp
from app.api import requested_format
from app.api import api_bp as bp
from app.scodoc.sco_utils import json_error
from app.models import Departement
from app.scodoc.sco_logos import list_logos, find_logo
@ -47,10 +46,11 @@ from app.scodoc.sco_permissions import Permission
@bp.route("/logos")
@scodoc
@permission_required(Permission.ScoSuperAdmin)
@as_json
def api_get_glob_logos():
"""Liste tous les logos"""
logos = list_logos()[None]
return jsonify(list(logos.keys()))
return list(logos.keys())
@bp.route("/logo/<string:logoname>")
@ -68,27 +68,29 @@ def api_get_glob_logo(logoname):
)
def core_get_logos(dept_id):
def _core_get_logos(dept_id) -> list:
logos = list_logos().get(dept_id, dict())
return jsonify(list(logos.keys()))
return list(logos.keys())
@bp.route("/departement/<string:departement>/logos")
@scodoc
@permission_required(Permission.ScoSuperAdmin)
@as_json
def api_get_local_logos_by_acronym(departement):
dept_id = Departement.from_acronym(departement).id
return core_get_logos(dept_id)
return _core_get_logos(dept_id)
@bp.route("/departement/id/<int:dept_id>/logos")
@scodoc
@permission_required(Permission.ScoSuperAdmin)
@as_json
def api_get_local_logos_by_id(dept_id):
return core_get_logos(dept_id)
return _core_get_logos(dept_id)
def core_get_logo(dept_id, logoname):
def _core_get_logo(dept_id, logoname) -> Response:
logo = find_logo(logoname=logoname, dept_id=dept_id)
if logo is None:
return json_error(404, message="logo not found")
@ -105,11 +107,11 @@ def core_get_logo(dept_id, logoname):
@permission_required(Permission.ScoSuperAdmin)
def api_get_local_logo_dept_by_acronym(departement, logoname):
dept_id = Departement.from_acronym(departement).id
return core_get_logo(dept_id, logoname)
return _core_get_logo(dept_id, logoname)
@bp.route("/departement/id/<int:dept_id>/logo/<string:logoname>")
@scodoc
@permission_required(Permission.ScoSuperAdmin)
def api_get_local_logo_dept_by_id(dept_id, logoname):
return core_get_logo(dept_id, logoname)
return _core_get_logo(dept_id, logoname)

View File

@ -9,7 +9,8 @@
"""
from operator import attrgetter
from flask import g, jsonify, request
from flask import g, request
from flask_json import as_json
from flask_login import login_required
import app
@ -31,6 +32,7 @@ from app.scodoc import sco_utils as scu
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def partition_info(partition_id: int):
"""Info sur une partition.
@ -55,7 +57,7 @@ def partition_info(partition_id: int):
if g.scodoc_dept:
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
partition = query.first_or_404()
return jsonify(partition.to_dict(with_groups=True))
return partition.to_dict(with_groups=True)
@bp.route("/formsemestre/<int:formsemestre_id>/partitions")
@ -63,6 +65,7 @@ def partition_info(partition_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def formsemestre_partitions(formsemestre_id: int):
"""Liste de toutes les partitions d'un formsemestre
@ -88,13 +91,11 @@ def formsemestre_partitions(formsemestre_id: int):
query = query.filter_by(dept_id=g.scodoc_dept_id)
formsemestre: FormSemestre = query.first_or_404(formsemestre_id)
partitions = sorted(formsemestre.partitions, key=attrgetter("numero"))
return jsonify(
{
partition.id: partition.to_dict(with_groups=True)
for partition in partitions
if partition.partition_name is not None
}
)
return {
partition.id: partition.to_dict(with_groups=True)
for partition in partitions
if partition.partition_name is not None
}
@bp.route("/group/<int:group_id>/etudiants")
@ -102,6 +103,7 @@ def formsemestre_partitions(formsemestre_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def etud_in_group(group_id: int):
"""
Retourne la liste des étudiants dans un groupe
@ -128,7 +130,7 @@ def etud_in_group(group_id: int):
query.join(Partition).join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
)
group = query.first_or_404()
return jsonify([etud.to_dict_short() for etud in group.etuds])
return [etud.to_dict_short() for etud in group.etuds]
@bp.route("/group/<int:group_id>/etudiants/query")
@ -136,6 +138,7 @@ def etud_in_group(group_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def etud_in_group_query(group_id: int):
"""Étudiants du groupe, filtrés par état"""
etat = request.args.get("etat")
@ -156,7 +159,7 @@ def etud_in_group_query(group_id: int):
query = query.join(group_membership).filter_by(group_id=group_id)
return jsonify([etud.to_dict_short() for etud in query])
return [etud.to_dict_short() for etud in query]
@bp.route("/group/<int:group_id>/set_etudiant/<int:etudid>", methods=["POST"])
@ -164,6 +167,7 @@ def etud_in_group_query(group_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoEtudChangeGroups)
@as_json
def set_etud_group(etudid: int, group_id: int):
"""Affecte l'étudiant au groupe indiqué"""
etud = Identite.query.get_or_404(etudid)
@ -182,7 +186,7 @@ def set_etud_group(etudid: int, group_id: int):
etudid, group_id, group.partition.to_dict()
)
return jsonify({"group_id": group_id, "etudid": etudid})
return {"group_id": group_id, "etudid": etudid}
@bp.route("/group/<int:group_id>/remove_etudiant/<int:etudid>", methods=["POST"])
@ -192,6 +196,7 @@ def set_etud_group(etudid: int, group_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoEtudChangeGroups)
@as_json
def group_remove_etud(group_id: int, etudid: int):
"""Retire l'étudiant de ce groupe. S'il n'y est pas, ne fait rien."""
etud = Identite.query.get_or_404(etudid)
@ -215,7 +220,7 @@ def group_remove_etud(group_id: int, etudid: int):
# Update parcours
group.partition.formsemestre.update_inscriptions_parcours_from_groups()
sco_cache.invalidate_formsemestre(group.partition.formsemestre_id)
return jsonify({"group_id": group_id, "etudid": etudid})
return {"group_id": group_id, "etudid": etudid}
@bp.route(
@ -227,6 +232,7 @@ def group_remove_etud(group_id: int, etudid: int):
@login_required
@scodoc
@permission_required(Permission.ScoEtudChangeGroups)
@as_json
def partition_remove_etud(partition_id: int, etudid: int):
"""Enlève l'étudiant de tous les groupes de cette partition
(NB: en principe, un étudiant ne doit être que dans 0 ou 1 groupe d'une partition)
@ -256,7 +262,7 @@ def partition_remove_etud(partition_id: int, etudid: int):
partition.formsemestre.update_inscriptions_parcours_from_groups()
app.set_sco_dept(partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
return jsonify({"partition_id": partition_id, "etudid": etudid})
return {"partition_id": partition_id, "etudid": etudid}
@bp.route("/partition/<int:partition_id>/group/create", methods=["POST"])
@ -264,6 +270,7 @@ def partition_remove_etud(partition_id: int, etudid: int):
@login_required
@scodoc
@permission_required(Permission.ScoEtudChangeGroups)
@as_json
def group_create(partition_id: int):
"""Création d'un groupe dans une partition
@ -294,7 +301,7 @@ def group_create(partition_id: int):
log(f"created group {group}")
app.set_sco_dept(partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
return jsonify(group.to_dict(with_partition=True))
return group.to_dict(with_partition=True)
@bp.route("/group/<int:group_id>/delete", methods=["POST"])
@ -302,6 +309,7 @@ def group_create(partition_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoEtudChangeGroups)
@as_json
def group_delete(group_id: int):
"""Suppression d'un groupe"""
query = GroupDescr.query.filter_by(id=group_id)
@ -320,7 +328,7 @@ def group_delete(group_id: int):
db.session.commit()
app.set_sco_dept(group.partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(formsemestre_id)
return jsonify({"OK": True})
return {"OK": True}
@bp.route("/group/<int:group_id>/edit", methods=["POST"])
@ -328,6 +336,7 @@ def group_delete(group_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoEtudChangeGroups)
@as_json
def group_edit(group_id: int):
"""Edit a group"""
query = GroupDescr.query.filter_by(id=group_id)
@ -352,7 +361,7 @@ def group_edit(group_id: int):
log(f"modified {group}")
app.set_sco_dept(group.partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(group.partition.formsemestre_id)
return jsonify(group.to_dict(with_partition=True))
return group.to_dict(with_partition=True)
@bp.route("/formsemestre/<int:formsemestre_id>/partition/create", methods=["POST"])
@ -362,6 +371,7 @@ def group_edit(group_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoEtudChangeGroups)
@as_json
def partition_create(formsemestre_id: int):
"""Création d'une partition dans un semestre
@ -414,7 +424,7 @@ def partition_create(formsemestre_id: int):
log(f"created partition {partition}")
app.set_sco_dept(formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(formsemestre_id)
return jsonify(partition.to_dict(with_groups=True))
return partition.to_dict(with_groups=True)
@bp.route("/formsemestre/<int:formsemestre_id>/partitions/order", methods=["POST"])
@ -424,6 +434,7 @@ def partition_create(formsemestre_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoEtudChangeGroups)
@as_json
def formsemestre_order_partitions(formsemestre_id: int):
"""Modifie l'ordre des partitions du formsemestre
JSON args: [partition_id1, partition_id2, ...]
@ -449,13 +460,11 @@ def formsemestre_order_partitions(formsemestre_id: int):
db.session.commit()
app.set_sco_dept(formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(formsemestre_id)
return jsonify(
[
partition.to_dict()
for partition in formsemestre.partitions.order_by(Partition.numero)
if partition.partition_name is not None
]
)
return [
partition.to_dict()
for partition in formsemestre.partitions.order_by(Partition.numero)
if partition.partition_name is not None
]
@bp.route("/partition/<int:partition_id>/groups/order", methods=["POST"])
@ -463,6 +472,7 @@ def formsemestre_order_partitions(formsemestre_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoEtudChangeGroups)
@as_json
def partition_order_groups(partition_id: int):
"""Modifie l'ordre des groupes de la partition
JSON args: [group_id1, group_id2, ...]
@ -489,7 +499,7 @@ def partition_order_groups(partition_id: int):
app.set_sco_dept(partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
log(f"partition_order_groups: {partition} : {group_ids}")
return jsonify(partition.to_dict(with_groups=True))
return partition.to_dict(with_groups=True)
@bp.route("/partition/<int:partition_id>/edit", methods=["POST"])
@ -497,6 +507,7 @@ def partition_order_groups(partition_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoEtudChangeGroups)
@as_json
def partition_edit(partition_id: int):
"""Modification d'une partition dans un semestre
@ -558,7 +569,7 @@ def partition_edit(partition_id: int):
app.set_sco_dept(partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
return jsonify(partition.to_dict(with_groups=True))
return partition.to_dict(with_groups=True)
@bp.route("/partition/<int:partition_id>/delete", methods=["POST"])
@ -566,6 +577,7 @@ def partition_edit(partition_id: int):
@login_required
@scodoc
@permission_required(Permission.ScoEtudChangeGroups)
@as_json
def partition_delete(partition_id: int):
"""Suppression d'une partition (et de tous ses groupes).
@ -593,4 +605,4 @@ def partition_delete(partition_id: int):
sco_cache.invalidate_formsemestre(formsemestre.id)
if is_parcours:
formsemestre.update_inscriptions_parcours_from_groups()
return jsonify({"OK": True})
return {"OK": True}

View File

@ -1,4 +1,4 @@
from flask import jsonify
from flask_json import as_json
from app import db, log
from app.api import api_bp as bp
from app.auth.logic import basic_auth, token_auth
@ -6,12 +6,13 @@ from app.auth.logic import basic_auth, token_auth
@bp.route("/tokens", methods=["POST"])
@basic_auth.login_required
@as_json
def get_token():
"renvoie un jeton jwt pour l'utilisateur courant"
token = basic_auth.current_user().get_token()
log(f"API: giving token to {basic_auth.current_user()}")
db.session.commit()
return jsonify({"token": token})
return {"token": token}
@bp.route("/tokens", methods=["DELETE"])

View File

@ -9,7 +9,8 @@
"""
from flask import g, jsonify, request
from flask import g, request
from flask_json import as_json
from flask_login import current_user, login_required
from app import db
@ -29,6 +30,7 @@ from app.scodoc import sco_utils as scu
@login_required
@scodoc
@permission_required(Permission.ScoUsersView)
@as_json
def user_info(uid: int):
"""
Info sur un compte utilisateur scodoc
@ -41,7 +43,7 @@ def user_info(uid: int):
if (None not in allowed_depts) and (user.dept not in allowed_depts):
return json_error(404, "user not found")
return jsonify(user.to_dict())
return user.to_dict()
@bp.route("/users/query")
@ -49,6 +51,7 @@ def user_info(uid: int):
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def users_info_query():
"""Utilisateurs, filtrés par dept, active ou début nom
/users/query?departement=dept_acronym&active=1&starts_with=<string:nom>
@ -79,7 +82,7 @@ def users_info_query():
)
query = query.order_by(User.user_name)
return jsonify([user.to_dict() for user in query])
return [user.to_dict() for user in query]
@bp.route("/user/create", methods=["POST"])
@ -87,6 +90,7 @@ def users_info_query():
@login_required
@scodoc
@permission_required(Permission.ScoUsersAdmin)
@as_json
def user_create():
"""Création d'un utilisateur
The request content type should be "application/json":
@ -121,7 +125,7 @@ def user_create():
user = User(user_name=user_name, active=active, dept=dept, nom=nom, prenom=prenom)
db.session.add(user)
db.session.commit()
return jsonify(user.to_dict())
return user.to_dict()
@bp.route("/user/<int:uid>/edit", methods=["POST"])
@ -129,6 +133,7 @@ def user_create():
@login_required
@scodoc
@permission_required(Permission.ScoUsersAdmin)
@as_json
def user_edit(uid: int):
"""Modification d'un utilisateur
Champs modifiables:
@ -165,7 +170,7 @@ def user_edit(uid: int):
db.session.add(user)
db.session.commit()
return jsonify(user.to_dict())
return user.to_dict()
@bp.route("/user/<int:uid>/password", methods=["POST"])
@ -173,6 +178,7 @@ def user_edit(uid: int):
@login_required
@scodoc
@permission_required(Permission.ScoUsersAdmin)
@as_json
def user_password(uid: int):
"""Modification du mot de passe d'un utilisateur
Champs modifiables:
@ -194,7 +200,7 @@ def user_password(uid: int):
user.set_password(password)
db.session.add(user)
db.session.commit()
return jsonify(user.to_dict())
return user.to_dict()
@bp.route("/user/<int:uid>/role/<string:role_name>/add", methods=["POST"])
@ -210,6 +216,7 @@ def user_password(uid: int):
@login_required
@scodoc
@permission_required(Permission.ScoSuperAdmin)
@as_json
def user_role_add(uid: int, role_name: str, dept: str = None):
"""Add a role to the user"""
user: User = User.query.get_or_404(uid)
@ -222,7 +229,7 @@ def user_role_add(uid: int, role_name: str, dept: str = None):
user.add_role(role, dept)
db.session.add(user)
db.session.commit()
return jsonify(user.to_dict())
return user.to_dict()
@bp.route("/user/<int:uid>/role/<string:role_name>/remove", methods=["POST"])
@ -238,6 +245,7 @@ def user_role_add(uid: int, role_name: str, dept: str = None):
@login_required
@scodoc
@permission_required(Permission.ScoSuperAdmin)
@as_json
def user_role_remove(uid: int, role_name: str, dept: str = None):
"""Remove the role from the user"""
user: User = User.query.get_or_404(uid)
@ -256,7 +264,7 @@ def user_role_remove(uid: int, role_name: str, dept: str = None):
db.session.delete(user_role)
db.session.add(user)
db.session.commit()
return jsonify(user.to_dict())
return user.to_dict()
@bp.route("/permissions")
@ -264,9 +272,10 @@ def user_role_remove(uid: int, role_name: str, dept: str = None):
@login_required
@scodoc
@permission_required(Permission.ScoUsersView)
@as_json
def list_permissions():
"""Liste des noms de permissions définies"""
return jsonify(list(Permission.permission_by_name.keys()))
return list(Permission.permission_by_name.keys())
@bp.route("/role/<string:role_name>")
@ -274,9 +283,10 @@ def list_permissions():
@login_required
@scodoc
@permission_required(Permission.ScoUsersView)
@as_json
def list_role(role_name: str):
"""Un rôle"""
return jsonify(Role.query.filter_by(name=role_name).first_or_404().to_dict())
return Role.query.filter_by(name=role_name).first_or_404().to_dict()
@bp.route("/roles")
@ -284,9 +294,10 @@ def list_role(role_name: str):
@login_required
@scodoc
@permission_required(Permission.ScoUsersView)
@as_json
def list_roles():
"""Tous les rôles définis"""
return jsonify([role.to_dict() for role in Role.query])
return [role.to_dict() for role in Role.query]
@bp.route(
@ -300,6 +311,7 @@ def list_roles():
@login_required
@scodoc
@permission_required(Permission.ScoSuperAdmin)
@as_json
def role_permission_add(role_name: str, perm_name: str):
"""Add permission to role"""
role: Role = Role.query.filter_by(name=role_name).first_or_404()
@ -309,7 +321,7 @@ def role_permission_add(role_name: str, perm_name: str):
role.add_permission(permission)
db.session.add(role)
db.session.commit()
return jsonify(role.to_dict())
return role.to_dict()
@bp.route(
@ -323,6 +335,7 @@ def role_permission_add(role_name: str, perm_name: str):
@login_required
@scodoc
@permission_required(Permission.ScoSuperAdmin)
@as_json
def role_permission_remove(role_name: str, perm_name: str):
"""Remove permission from role"""
role: Role = Role.query.filter_by(name=role_name).first_or_404()
@ -332,7 +345,7 @@ def role_permission_remove(role_name: str, perm_name: str):
role.remove_permission(permission)
db.session.add(role)
db.session.commit()
return jsonify(role.to_dict())
return role.to_dict()
@bp.route("/role/create/<string:role_name>", methods=["POST"])
@ -340,6 +353,7 @@ def role_permission_remove(role_name: str, perm_name: str):
@login_required
@scodoc
@permission_required(Permission.ScoSuperAdmin)
@as_json
def role_create(role_name: str):
"""Create a new role with permissions.
{
@ -359,7 +373,7 @@ def role_create(role_name: str):
return json_error(404, "role_create: invalid permissions")
db.session.add(role)
db.session.commit()
return jsonify(role.to_dict())
return role.to_dict()
@bp.route("/role/<string:role_name>/edit", methods=["POST"])
@ -367,6 +381,7 @@ def role_create(role_name: str):
@login_required
@scodoc
@permission_required(Permission.ScoSuperAdmin)
@as_json
def role_edit(role_name: str):
"""Edit a role. On peut spécifier un nom et/ou des permissions.
{
@ -390,7 +405,7 @@ def role_edit(role_name: str):
role.name = role_name
db.session.add(role)
db.session.commit()
return jsonify(role.to_dict())
return role.to_dict()
@bp.route("/role/<string:role_name>/delete", methods=["POST"])
@ -398,9 +413,10 @@ def role_edit(role_name: str):
@login_required
@scodoc
@permission_required(Permission.ScoSuperAdmin)
@as_json
def role_delete(role_name: str):
"""Delete a role"""
role: Role = Role.query.filter_by(name=role_name).first_or_404()
db.session.delete(role)
db.session.commit()
return jsonify({"OK": True})
return {"OK": True}

View File

@ -1,12 +1,13 @@
import os
from config import Config
from datetime import datetime, date
from datetime import datetime
import glob
import shutil
from flask import render_template, redirect, url_for, request, flash, send_file, abort
from flask.json import jsonify
from flask_json import as_json
from flask_login import current_user
from sqlalchemy import text, sql
from werkzeug.utils import secure_filename
from app.decorators import permission_required
@ -58,8 +59,7 @@ from app.scodoc import sco_etud, sco_excel
import app.scodoc.sco_utils as scu
from app import db
from sqlalchemy import text, sql
from werkzeug.utils import secure_filename
from config import Config
@bp.route("/", methods=["GET", "POST"])
@ -1698,6 +1698,7 @@ def envoyer_offre(entreprise_id, offre_id):
@bp.route("/etudiants")
@permission_required(Permission.RelationsEntreprisesChange)
@as_json
def json_etudiants():
"""
Permet de récuperer un JSON avec tous les étudiants
@ -1723,7 +1724,7 @@ def json_etudiants():
"info": f"Département {are.get_dept_acronym_by_id(etudiant.dept_id)}",
}
list.append(content)
return jsonify(results=list)
return list
@bp.route("/responsables")
@ -1749,7 +1750,7 @@ def json_responsables():
value = f"{responsable.get_nomplogin()}"
content = {"id": f"{responsable.id}", "value": value}
list.append(content)
return jsonify(results=list)
return list
@bp.route("/export_donnees")
@ -1843,7 +1844,7 @@ def import_donnees():
db.session.add(correspondant)
correspondants.append(correspondant)
db.session.commit()
flash(f"Importation réussie")
flash("Importation réussie")
return render_template(
"entreprises/import_donnees.j2",
title="Importation données",

View File

@ -85,6 +85,7 @@ class ApcReferentielCompetences(db.Model, XMLModel):
backref="referentiel",
lazy="dynamic",
cascade="all, delete-orphan",
order_by="ApcParcours.numero, ApcParcours.code",
)
formations = db.relationship(
"Formation",

View File

@ -275,6 +275,7 @@ DEVENIRS_NEXT2 = {NEXT_OR_NEXT2: 1, NEXT2: 1}
NO_SEMESTRE_ID = -1 # code semestre si pas de semestres
# Règles gestion cursus
class DUTRule(object):
def __init__(self, rule_id, premise, conclusion):
@ -298,7 +299,7 @@ class DUTRule(object):
# Types de cursus
DEFAULT_TYPE_CURSUS = 100 # pour le menu de creation nouvelle formation
DEFAULT_TYPE_CURSUS = 700 # (BUT) pour le menu de creation nouvelle formation
class TypeCursus:

View File

@ -33,8 +33,9 @@ import email
import time
import numpy as np
from flask import g, request
from flask import flash, jsonify, render_template, url_for
from flask import g, request, Response
from flask import flash, render_template, url_for
from flask_json import json_response
from flask_login import current_user
from app import email
@ -79,14 +80,14 @@ def get_formsemestre_bulletin_etud_json(
etud: Identite,
force_publishing=False,
version="long",
) -> str:
) -> Response:
"""Le JSON du bulletin d'un étudiant, quel que soit le type de formation."""
if formsemestre.formation.is_apc():
bulletins_sem = bulletin_but.BulletinBUT(formsemestre)
if not etud.id in bulletins_sem.res.identdict:
return json_error(404, "get_formsemestre_bulletin_etud_json: invalid etud")
return jsonify(
bulletins_sem.bulletin_etud(
return json_response(
data_=bulletins_sem.bulletin_etud(
etud,
formsemestre,
force_publishing=force_publishing,

View File

@ -56,8 +56,9 @@ from pytz import timezone
import dateutil.parser as dtparser
import flask
from flask import g, request
from flask import flash, url_for, make_response, jsonify
from flask import g, request, Response
from flask import flash, url_for, make_response
from flask_json import json_response
from werkzeug.http import HTTP_STATUS_CODES
from config import Config
@ -966,24 +967,26 @@ def get_request_args():
return vals
def json_error(status_code, message=None):
"""Simple JSON response, for errors"""
def json_error(status_code, message=None) -> Response:
"""Simple JSON for errors.
If as-response, returns Flask's Response. Otherwise returns a dict.
"""
payload = {
"error": HTTP_STATUS_CODES.get(status_code, "Unknown error"),
"status": status_code,
}
if message:
payload["message"] = message
response = jsonify(payload)
response = json_response(status_=status_code, data_=payload)
response.status_code = status_code
log(f"Error: {response}")
return response
def json_ok_response(status_code=200, payload=None):
def json_ok_response(status_code=200, payload=None) -> Response:
"""Simple JSON respons for "success" """
payload = payload or {"OK": True}
response = jsonify(payload)
response = json_response(status_=status_code, data_=payload)
response.status_code = status_code
return response

View File

@ -33,7 +33,7 @@ from app.scodoc.sco_exceptions import ScoValueError
class ApoEtapeVDI(object):
_ETAPE_VDI_SEP = "!"
def __init__(self, etape_vdi=None, etape="", vdi=""):
def __init__(self, etape_vdi: str = None, etape: str = "", vdi: str = ""):
"""Build from string representation, e.g. 'V1RT!111'"""
if etape_vdi:
self.etape_vdi = etape_vdi
@ -52,6 +52,10 @@ class ApoEtapeVDI(object):
def __str__(self):
return self.etape_vdi
def __json__(self) -> str:
"json repr for flask_json"
return str(self)
def _cmp(self, other):
"""Test égalité de deux codes étapes.
Si le VDI des deux est spécifié, on l'utilise. Sinon, seul le code étape est pris en compte.

View File

@ -28,6 +28,8 @@
<li><a class="stdlink" href="{{
url_for('notes.ue_table', scodoc_dept=g.scodoc_dept, formation_id=formation.id )
}}">{{ formation.get_titre_version() }}</a></li>
{% else %}
<li><em>aucune</em></li>
{% endfor %}
</ul>
</li>

View File

@ -32,10 +32,10 @@ Emmanuel Viennet, 2021
"""
from flask import url_for
from flask import jsonify
from flask import g, request
from flask.templating import render_template
from flask_json import as_json
from flask_login import current_user
from flask.templating import render_template
from app.scodoc.codes_cursus import UE_SPORT
@ -58,6 +58,7 @@ from app.scodoc.sco_permissions import Permission
)
@scodoc
@permission_required(Permission.ScoView)
@as_json
def table_modules_ue_coefs(formation_id, semestre_idx=None, parcours_id: int = None):
"""Description JSON de la table des coefs modules/UE dans une formation
@ -111,10 +112,10 @@ def table_modules_ue_coefs(formation_id, semestre_idx=None, parcours_id: int = N
]
# Les champs de saisie
cells = []
for (row, mod) in enumerate(modules, start=2):
for row, mod in enumerate(modules, start=2):
style = "champs champs_" + scu.ModuleType(mod.module_type).name
mod_parcours_ids = {p.id for p in mod.parcours}
for (col, ue) in enumerate(ues, start=2):
for col, ue in enumerate(ues, start=2):
# met en gris les coefs qui devraient être nuls
# car le module n'est pas dans le parcours de l'UE:
if (
@ -136,7 +137,7 @@ def table_modules_ue_coefs(formation_id, semestre_idx=None, parcours_id: int = N
"ue_id": ue.id,
}
)
return jsonify(col_titres_mods + row_titres_ue + cells)
return col_titres_mods + row_titres_ue + cells
@bp.route("/set_module_ue_coef", methods=["POST"])

View File

@ -6,10 +6,11 @@ Emmanuel Viennet, 2021
from pathlib import Path
import re
from flask import jsonify, flash, url_for
from flask import flash, url_for
from flask import Markup
from flask import current_app, g, request
from flask.templating import render_template
from flask_json import as_json
from flask_login import current_user
from werkzeug.utils import redirect
from werkzeug.utils import secure_filename
@ -33,12 +34,13 @@ from app.views import ScoData
@bp.route("/referentiel/comp/get/<int:refcomp_id>")
@scodoc
@permission_required(Permission.ScoView)
@as_json
def refcomp(refcomp_id):
"""Le référentiel de compétences, en JSON."""
ref: ApcReferentielCompetences = ApcReferentielCompetences.query.get_or_404(
refcomp_id
)
return jsonify(ref.to_dict())
return ref.to_dict()
@bp.route("/referentiel/comp/show/<int:refcomp_id>")

View File

@ -35,8 +35,9 @@ import requests
import time
import flask
from flask import jsonify, url_for, flash, render_template, make_response
from flask import url_for, flash, render_template, make_response
from flask import g, request
from flask_json import as_json
from flask_login import current_user
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileAllowed
@ -501,10 +502,11 @@ sco_publish(
@bp.route("/Notes/search_etud_by_name") # for JS apis
@scodoc
@permission_required(Permission.ScoView)
@as_json
def search_etud_by_name():
term = request.args["term"]
data = sco_find_etud.search_etud_by_name(term)
return jsonify(data)
return data
# XMLgetEtudInfos était le nom dans l'ancienne API ScoDoc 6

View File

@ -38,6 +38,9 @@ class Config:
SCODOC_ERR_FILE = os.path.join(SCODOC_VAR_DIR, "log", "scodoc_exc.log")
#
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # Flask uploads (16Mo, en ligne avec nginx)
# flask_json:
JSON_ADD_STATUS = False
JSON_USE_ENCODE_METHODS = True
class ProdConfig(Config):

View File

@ -26,6 +26,7 @@ flask-babel==3.0.1
Flask-Bootstrap==3.3.7.1
Flask-Caching==2.0.2
Flask-HTTPAuth==4.7.0
Flask-JSON==0.3.5
Flask-Login==0.6.2
Flask-Mail==0.9.1
Flask-Migrate==4.0.4

View File

@ -17,7 +17,6 @@ import os
import requests
from dotenv import load_dotenv
import pytest
from app.scodoc import sco_utils as scu
# --- Lecture configuration (variables d'env ou .env)
try:
@ -34,6 +33,7 @@ API_PASSWORD = os.environ.get("API_PASSWORD", os.environ.get("API_PASSWD", "test
API_USER_ADMIN = os.environ.get("API_USER_ADMIN", "admin_api")
API_PASSWORD_ADMIN = os.environ.get("API_PASSWD_ADMIN", "admin_api")
DEPT_ACRONYM = "TAPI"
SCO_TEST_API_TIMEOUT = 5
print(f"SCODOC_URL={SCODOC_URL}")
print(f"API URL={API_URL}")
@ -78,7 +78,7 @@ def GET(path: str, headers: dict = None, errmsg=None, dept=None):
url,
headers=headers or {},
verify=CHECK_CERTIFICATE,
timeout=scu.SCO_TEST_API_TIMEOUT,
timeout=SCO_TEST_API_TIMEOUT,
)
if reply.status_code != 200:
raise APIError(
@ -111,7 +111,7 @@ def POST_JSON(path: str, data: dict = {}, headers: dict = None, errmsg=None, dep
json=data,
headers=headers or {},
verify=CHECK_CERTIFICATE,
timeout=10,
timeout=SCO_TEST_API_TIMEOUT,
)
if r.status_code != 200:
raise APIError(errmsg or f"erreur status={r.status_code} !", r.json())

View File

@ -91,7 +91,9 @@ def test_permissions(api_headers):
assert r.status_code == 401
# Demande un jeton pour "other"
r = requests.post(API_URL + "/tokens", auth=("other", "other"), timeout=10)
r = requests.post(
API_URL + "/tokens", auth=("other", "other"), timeout=scu.SCO_TEST_API_TIMEOUT
)
assert r.status_code == 200
token = r.json()["token"]
headers = {"Authorization": f"Bearer {token}"}

View File

@ -1,526 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<formation acronyme="BUT INFO" titre_officiel="Bachelor Universitaire de Technologie" version="1" formation_code="FCOD49" code_specialite="" titre="BUT INFORMATIQUE" commentaire="" type_parcours="700" referentiel_competence_id="4" refcomp_version_orebut="2021-12-11 00:00:00" refcomp_specialite="INFO" refcomp_type_titre="B.U.T.">
<ue is_external="0" acronyme="UE11" code_apogee="V1INFU11" numero="0" coefficient="0.0" titre="Compétence 1 : Réaliser un développement d'application" coef_rcue="1.0" semestre_idx="1" color="#b80004" type="0" ue_code="UCOD62" ects="5.0" apc_niveau_libelle="Développer des applications informatiques simples " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="1639">
<matiere titre="Portfolio" numero="0">
<module titre="Initiation au développement" abbrev="Initiation au dev." code="R1.01-A" heures_cours="0.0" heures_td="24.0" heures_tp="30.0" coefficient="66.0" ects="" semestre_id="1" numero="10" code_apogee="VINFR101" module_type="2">
<coefficients ue_reference="1638" coef="12.0"/>
<coefficients ue_reference="1639" coef="21.0"/>
</module>
<module titre="Implémentation d'un besoin client" abbrev="Implémentation" code="S1.01" heures_cours="0.0" heures_td="2.0" heures_tp="2.0" coefficient="40.0" ects="" semestre_id="1" numero="10" code_apogee="VINFS101" module_type="3">
<coefficients ue_reference="1639" coef="40.0"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE12" code_apogee="V1INFU12" numero="1" coefficient="0.0" titre="Compétence 2 : Optimiser des applications informatiques" coef_rcue="1.0" semestre_idx="1" color="#f97b3d" type="0" ue_code="UCOD61" ects="5.0" apc_niveau_libelle="Appréhender et construire des algorithmes " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="1638">
<matiere titre="Ressource" numero="2">
<module titre="Initiation au développement" abbrev="Initiation au dev." code="R1.01-B" heures_cours="0.0" heures_td="16.0" heures_tp="24.0" coefficient="66.0" ects="" semestre_id="1" numero="20" code_apogee="VINFR101" module_type="2">
<coefficients ue_reference="1638" coef="12.0"/>
<coefficients ue_reference="1639" coef="21.0"/>
</module>
<module titre="Comparaison d'approches algorithmiques" abbrev="Comparaison d'algo." code="S1.02" heures_cours="0.0" heures_td="2.0" heures_tp="2.0" coefficient="40.0" ects="" semestre_id="1" numero="20" code_apogee="VINFS102" module_type="3">
<coefficients ue_reference="1638" coef="40.0"/>
</module>
<module titre="Développement d'interfaces web" abbrev="Dev. interfaces web" code="R1.02" heures_cours="0.0" heures_td="5.0" heures_tp="14.0" coefficient="35.0" ects="" semestre_id="1" numero="30" code_apogee="VINFR102" module_type="2">
<coefficients ue_reference="1639" coef="12.0"/>
<coefficients ue_reference="1900" coef="18.0"/>
<coefficients ue_reference="1901" coef="5.0"/>
</module>
<module titre="Mathématiques discrètes" abbrev="Maths discrètes" code="R1.06" heures_cours="0.0" heures_td="30.0" heures_tp="10.0" coefficient="33.0" ects="" semestre_id="1" numero="70" code_apogee="VINFR106" module_type="2">
<coefficients ue_reference="1638" coef="15.0"/>
<coefficients ue_reference="1885" coef="18.0"/>
</module>
<module titre="Outils mathématiques fondamentaux" abbrev="Outils fondamentaux" code="R1.07" heures_cours="0.0" heures_td="14.0" heures_tp="10.0" coefficient="15.0" ects="" semestre_id="1" numero="80" code_apogee="VINFR107" module_type="2">
<coefficients ue_reference="1638" coef="15.0"/>
</module>
<module titre="Gestion de projet &amp; des organisations" abbrev="Gestion proj. orga." code="R1.08" heures_cours="0.0" heures_td="21.0" heures_tp="10.0" coefficient="38.0" ects="" semestre_id="1" numero="90" code_apogee="VINFR108" module_type="2">
<coefficients ue_reference="1900" coef="27.0"/>
<coefficients ue_reference="1901" coef="11.0"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE13" code_apogee="V1INFU13" numero="2" coefficient="0.0" titre="Compétence 3 : Administrer des systèmes informatiques communicants" coef_rcue="1.0" semestre_idx="1" color="#feb40b" type="0" ue_code="UCOD50" ects="5.0" apc_niveau_libelle="Installer et configurer un poste de travail " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="1637">
<matiere titre="SAE" numero="1">
<module titre="Installation d'un poste pour le développement" abbrev="Installation poste" code="S1.03" heures_cours="0.0" heures_td="5.0" heures_tp="4.0" coefficient="40.0" ects="" semestre_id="1" numero="30" code_apogee="VINFS103" module_type="3">
<coefficients ue_reference="1637" coef="40.0"/>
</module>
<module titre="Introduction à l'architecture des ordinateurs" abbrev="Intro. archi." code="R1.03" heures_cours="0.0" heures_td="12.0" heures_tp="8.0" coefficient="24.0" ects="" semestre_id="1" numero="40" code_apogee="VINFR103" module_type="2">
<coefficients ue_reference="1637" coef="21.0"/>
<coefficients ue_reference="1638" coef="6.0"/>
</module>
<module titre="Introduction aux systèmes dexploitation et à leur fonctionnement" abbrev="Intro. systèmes" code="R1.04" heures_cours="0.0" heures_td="5.0" heures_tp="20.0" coefficient="24.0" ects="" semestre_id="1" numero="50" code_apogee="VINFR104" module_type="2">
<coefficients ue_reference="1637" coef="21.0"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE14" code_apogee="V1INFU14" numero="3" coefficient="0.0" titre="Compétence 4 : Gérer des données de l'information" coef_rcue="1.0" semestre_idx="1" color="#80cb3f" type="0" ue_code="UCOD10" ects="5.0" apc_niveau_libelle="Concevoir et mettre en place une base de données à partir dun cahier des charges client " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="1885">
<matiere titre="Ressources" numero="0">
<module titre="Création d'une base de données" abbrev="Création BD" code="S1.04" heures_cours="0.0" heures_td="4.0" heures_tp="2.0" coefficient="40.0" ects="" semestre_id="1" numero="40" code_apogee="VINFS104" module_type="3">
<coefficients ue_reference="1885" coef="40.0"/>
</module>
<module titre="Introduction aux bases de données et SQL" abbrev="Introduction BD" code="R1.05" heures_cours="12.0" heures_td="9.0" heures_tp="24.0" coefficient="36.0" ects="" semestre_id="1" numero="60" code_apogee="VINFR105" module_type="2">
<coefficients ue_reference="1885" coef="36.0"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE15" code_apogee="V1INFU15" numero="4" coefficient="0.0" titre="Compétence 5 : Conduire un projet" coef_rcue="1.0" semestre_idx="1" color="#05162e" type="0" ue_code="UCOD15" ects="5.0" apc_niveau_libelle="Identifier les besoins métiers des clients et des utilisateurs " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="1900">
<matiere titre="Compétence 5 : Conduire un projet" numero="1">
<module titre="Recueil de besoins" abbrev="Recueil de besoins" code="S1.05" heures_cours="0.0" heures_td="4.0" heures_tp="2.0" coefficient="40.0" ects="" semestre_id="1" numero="50" code_apogee="VINFS105" module_type="3">
<coefficients ue_reference="1900" coef="40.0"/>
</module>
<module titre="Économie durable et numérique" abbrev="Économie" code="R1.09" heures_cours="0.0" heures_td="18.0" heures_tp="5.0" coefficient="17.0" ects="" semestre_id="1" numero="100" code_apogee="VINFR109" module_type="2">
<coefficients ue_reference="1885" coef="6.0"/>
<coefficients ue_reference="1901" coef="11.0"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE16" code_apogee="V1INFU16" numero="5" coefficient="0.0" titre="Compétence 6 : Travailler dans une équipe informatique" coef_rcue="1.0" semestre_idx="1" color="#548687" type="0" ue_code="UCOD16" ects="5.0" apc_niveau_libelle="Identifier ses aptitudes pour travailler dans une équipe " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="1901">
<matiere titre="Compétence 6 : Travailler dans une équipe informatique" numero="1">
<module titre="Portfolio" abbrev="Portfolio" code="P1.01" heures_cours="0.0" heures_td="0.0" heures_tp="6.0" coefficient="0.0" ects="" semestre_id="1" numero="1" code_apogee="VINFPOR1" module_type="0"/>
<module titre="Découverte de l'environnement économique et écologique" abbrev="Environnement éco." code="S1.06" heures_cours="0.0" heures_td="0.0" heures_tp="6.0" coefficient="40.0" ects="" semestre_id="1" numero="60" code_apogee="VINFS106" module_type="3">
<coefficients ue_reference="1901" coef="40.0"/>
</module>
<module titre="Anglais technique" abbrev="Anglais technique" code="R1.10" heures_cours="0.0" heures_td="15.0" heures_tp="9.0" coefficient="29.0" ects="" semestre_id="1" numero="110" code_apogee="VINFR110" module_type="2">
<coefficients ue_reference="1637" coef="12.0"/>
<coefficients ue_reference="1639" coef="6.0"/>
<coefficients ue_reference="1901" coef="11.0"/>
</module>
<module titre="Bases de la communication" abbrev="Bases de la comm" code="R1.11" heures_cours="0.0" heures_td="15.0" heures_tp="9.0" coefficient="32.0" ects="" semestre_id="1" numero="120" code_apogee="VINFR111" module_type="2">
<coefficients ue_reference="1637" coef="6.0"/>
<coefficients ue_reference="1900" coef="15.0"/>
<coefficients ue_reference="1901" coef="11.0"/>
</module>
<module titre="Projet professionnel et personnel" abbrev="PPP" code="R1.12" heures_cours="0.0" heures_td="8.0" heures_tp="2.0" coefficient="11.0" ects="" semestre_id="1" numero="130" code_apogee="VINFR112" module_type="2">
<coefficients ue_reference="1901" coef="11.0"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE17" code_apogee="" numero="6" coefficient="0.0" titre="Sport/Culture" coef_rcue="1.0" semestre_idx="1" color="#444054" type="1" ue_code="X7.2" ects="0.0" reference="2472">
<matiere titre="Sport/Culture" numero="1">
<module titre="Sport / Culture" abbrev="Sport" code="X1.01" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="1" numero="520" code_apogee="" module_type="0"/>
</matiere>
</ue>
<ue is_external="0" acronyme="UE21" code_apogee="V1INFU21" numero="7" coefficient="0.0" titre="Compétence 1: Réaliser un développement d'application" coef_rcue="1.0" semestre_idx="2" color="#b80004" type="0" ue_code="UCOD17" ects="5.0" apc_niveau_libelle="Développer des applications informatiques simples " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="1902">
<matiere titre="Compétence 1: Réaliser un développement d'application" numero="1">
<module titre="Développement orienté objets" abbrev="Développement orienté objets" code="R2.01" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="140" code_apogee="VINFR201" module_type="2">
<coefficients ue_reference="1902" coef="21.0"/>
<coefficients ue_reference="1903" coef="15.0"/>
</module>
<module titre="Développement d'applications avec IHM" abbrev="Développement d'applications avec IHM" code="R2.02" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="150" code_apogee="VINFR202" module_type="2">
<coefficients ue_reference="1902" coef="21.0"/>
<coefficients ue_reference="1906" coef="3.0"/>
<coefficients ue_reference="1907" coef="4.0"/>
</module>
<module titre="Qualité de développement" abbrev="Qualité de développement" code="R2.03" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="160" code_apogee="VINFR203" module_type="2">
<coefficients ue_reference="1902" coef="12.0"/>
<coefficients ue_reference="1906" coef="6.0"/>
</module>
<module titre="Développement d'une application" abbrev="Développement d'une application" code="S2.01" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="280" code_apogee="VINFS201" module_type="3">
<coefficients ue_reference="1902" coef="40.0"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE22" code_apogee="V1INFU22" numero="8" coefficient="0.0" titre="Compétence 2 : Optimiser des applications informatiques" coef_rcue="1.0" semestre_idx="2" color="#f97b3d" type="0" ue_code="UCOD18" ects="5.0" apc_niveau_libelle="Appréhender et construire des algorithmes " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="1903">
<matiere titre="Compétence 2 : Optimiser des applications informatiques" numero="1">
<module titre="Graphes" abbrev="Graphes" code="R2.07" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="210" code_apogee="VINFR207" module_type="2">
<coefficients ue_reference="1903" coef="21.0"/>
<coefficients ue_reference="1906" coef="6.0"/>
</module>
<module titre="Méthodes numériques" abbrev="Méthodes numériques" code="R2.09" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="230" code_apogee="VINFR209" module_type="2">
<coefficients ue_reference="1903" coef="12.0"/>
</module>
<module titre="Exploration algorithmique d'un problème" abbrev="Exploration algorithmique d'un problème" code="S2.02" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="290" code_apogee="VINFS202" module_type="3">
<coefficients ue_reference="1903" coef="40.0"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE23" code_apogee="V1INFU23" numero="9" coefficient="0.0" titre="Compétence 3 : Administrer des systèmes informatiques communicants" coef_rcue="1.0" semestre_idx="2" color="#feb40b" type="0" ue_code="UCOD19" ects="5.0" apc_niveau_libelle="Installer et configurer un poste de travail " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="1904">
<matiere titre="Compétence 3 : Administrer des systèmes informatiques communicants" numero="1">
<module titre="Communication et fonctionnement bas niveau" abbrev="Communication et fonctionnement bas niveau" code="R2.04-A" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="170" code_apogee="VINFR204" module_type="2">
<coefficients ue_reference="1903" coef="6.0"/>
<coefficients ue_reference="1904" coef="18.0"/>
</module>
<module titre="Communication et fonctionnement bas niveau" abbrev="Communication et fonctionnement bas niveau" code="R2.04-B" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="180" code_apogee="VINFR204" module_type="2">
<coefficients ue_reference="1903" coef="6.0"/>
<coefficients ue_reference="1904" coef="18.0"/>
</module>
<module titre="Introduction aux services réseaux" abbrev="Introduction aux services réseaux" code="R2.05" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="190" code_apogee="VINFR205" module_type="2">
<coefficients ue_reference="1904" coef="15.0"/>
</module>
<module titre="Installation de services réseau" abbrev="Installation de services réseau" code="S2.03" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="300" code_apogee="VINFS203" module_type="3">
<coefficients ue_reference="1904" coef="40.0"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE24" code_apogee="V1INFU24" numero="10" coefficient="0.0" titre="Compétence 4 : Gérer des données de l'information" coef_rcue="1.0" semestre_idx="2" color="#80cb3f" type="0" ue_code="UCOD20" ects="5.0" apc_niveau_libelle="Concevoir et mettre en place une base de données à partir dun cahier des charges client " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="1905">
<matiere titre="Compétence 4 : Gérer des données de l'information" numero="1">
<module titre="Exploitation d'une base de données" abbrev="Exploitation d'une base de données" code="R2.06" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="200" code_apogee="VINFR206" module_type="2">
<coefficients ue_reference="1905" coef="30.0"/>
</module>
<module titre="Outils numériques pour les statistiques descriptives" abbrev="Outils numériques pour les statistiques descriptives" code="R2.08" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="220" code_apogee="VINFR208" module_type="2">
<coefficients ue_reference="1905" coef="12.0"/>
</module>
<module titre="Exploitation d'une base de données" abbrev="Exploitation d'une base de données" code="S2.04" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="310" code_apogee="VINFS204" module_type="3">
<coefficients ue_reference="1905" coef="40.0"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE25" code_apogee="V1INFU25" numero="11" coefficient="0.0" titre="Compétence 5 : Conduire un projet" coef_rcue="1.0" semestre_idx="2" color="#05162e" type="0" ue_code="UCOD21" ects="5.0" apc_niveau_libelle="Identifier les besoins métiers des clients et des utilisateurs " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="1906">
<matiere titre="Compétence 5 : Conduire un projet" numero="1">
<module titre="Gestion de projet &amp; des organisations" abbrev="Gestion de projet &amp; des organisations" code="R2.10" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="240" code_apogee="VINFR210" module_type="2">
<coefficients ue_reference="1905" coef="12.0"/>
<coefficients ue_reference="1906" coef="30.0"/>
</module>
<module titre="Anglais d'entreprise" abbrev="Anglais d'entreprise" code="R2.12" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="260" code_apogee="VINFR212" module_type="2">
<coefficients ue_reference="1904" coef="6.0"/>
<coefficients ue_reference="1905" coef="6.0"/>
<coefficients ue_reference="1906" coef="6.0"/>
<coefficients ue_reference="1907" coef="17.0"/>
</module>
<module titre="Gestion d'un projet" abbrev="Gestion d'un projet" code="S2.05" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="320" code_apogee="VINFS205" module_type="3">
<coefficients ue_reference="1906" coef="40.0"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE26" code_apogee="V1INFU26" numero="12" coefficient="0.0" titre="Compétence 6 : Travailler dans une équipe informatique" coef_rcue="1.0" semestre_idx="2" color="#548687" type="0" ue_code="UCOD22" ects="5.0" apc_niveau_libelle="Identifier ses aptitudes pour travailler dans une équipe " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="1907">
<matiere titre="Compétence 6 : Travailler dans une équipe informatique" numero="1">
<module titre="Droit des contrats et du numérique" abbrev="Droit des contrats et du numérique" code="R2.11" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="250" code_apogee="VINFR211" module_type="2">
<coefficients ue_reference="1907" coef="17.0"/>
</module>
<module titre="Communication avec le milieu professionnel" abbrev="Communication avec le milieu professionnel" code="R2.13" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="270" code_apogee="VINFR213" module_type="2">
<coefficients ue_reference="1902" coef="6.0"/>
<coefficients ue_reference="1904" coef="3.0"/>
<coefficients ue_reference="1906" coef="9.0"/>
<coefficients ue_reference="1907" coef="11.0"/>
</module>
<module titre="Organisation d'un travail d'équipe" abbrev="Organisation d'un travail d'équipe" code="S2.06" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="330" code_apogee="VINFS206" module_type="3">
<coefficients ue_reference="1907" coef="40.0"/>
</module>
<module titre="Portfolio" abbrev="Portfolio" code="P2.01" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="340" code_apogee="VINFPOR2" module_type="3"/>
<module titre="Projet professionnel et personnel : métiers de l'informatique" abbrev="PPP" code="R2.14" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="2" numero="350" code_apogee="VINFR214" module_type="2">
<coefficients ue_reference="1907" coef="11.0"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE31" code_apogee="" numero="13" coefficient="0.0" titre="Compétence 1 : Réaliser un développement d'application" coef_rcue="1.0" semestre_idx="3" color="#b80004" type="0" ue_code="UCOD71" ects="5.0" apc_niveau_libelle="Partir des exigences et aller jusquà une application complète " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="1986">
<matiere titre="Compétence 1 : Réaliser un développement d'application" numero="1"/>
</ue>
<ue is_external="0" acronyme="UE32" code_apogee="" numero="14" coefficient="0.0" titre="Compétence 2 : Optimiser des applications informatiques" coef_rcue="1.0" semestre_idx="3" color="#f97b3d" type="0" ue_code="UCOD79" ects="5.0" apc_niveau_libelle="Sélectionner les algorithmes adéquats pour répondre à un problème donné " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="1987">
<matiere titre="Compétence 2 : Optimiser des applications informatiques" numero="1"/>
</ue>
<ue is_external="0" acronyme="UE33" code_apogee="" numero="15" coefficient="0.0" titre="Compétence 3 : Administrer des systèmes informatiques communicants" coef_rcue="1.0" semestre_idx="3" color="#feb40b" type="0" ue_code="UCOD102" ects="5.0" apc_niveau_libelle="Déployer des services dans une architecture réseau" apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="1988">
<matiere titre="Compétence 3 : Administrer des systèmes informatiques communicants" numero="1"/>
</ue>
<ue is_external="0" acronyme="UE34" code_apogee="" numero="16" coefficient="0.0" titre="Compétence 4 : Gérer des données de l'information" coef_rcue="1.0" semestre_idx="3" color="#80cb3f" type="0" ue_code="UCOD103" ects="5.0" apc_niveau_libelle="Optimiser une base de données, interagir avec une application et mettre en œuvre la sécurité " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="1989">
<matiere titre="Compétence 4 : Gérer des données de l'information" numero="1"/>
</ue>
<ue is_external="0" acronyme="UE35" code_apogee="" numero="17" coefficient="0.0" titre="Compétence 5 : Conduire un projet" coef_rcue="1.0" semestre_idx="3" color="#05162e" type="0" ue_code="UCOD104" ects="5.0" apc_niveau_libelle="Appliquer une démarche de suivi de projet en fonction des besoins métiers des clients et des utilisateurs " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="1990">
<matiere titre="Compétence 5 : Conduire un projet" numero="1"/>
</ue>
<ue is_external="0" acronyme="UE36" code_apogee="" numero="18" coefficient="0.0" titre="Compétence 6 : Travailler dans une équipe informatique" coef_rcue="1.0" semestre_idx="3" color="#548687" type="0" ue_code="UCOD105" ects="5.0" apc_niveau_libelle="Situer son rôle et ses missions au sein dune équipe informatique " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="1991">
<matiere titre="Compétence 6 : Travailler dans une équipe informatique" numero="1">
<module titre="Développement web" abbrev="Développement web" code="R3.01" heures_cours="0.0" heures_td="8.0" heures_tp="25.0" coefficient="0.0" ects="" semestre_id="3" numero="360" code_apogee="VINFR301" module_type="2">
<coefficients ue_reference="1986" coef="15.0"/>
<coefficients ue_reference="1987" coef="5.0"/>
<coefficients ue_reference="1988" coef="5.0"/>
<coefficients ue_reference="1989" coef="10.0"/>
</module>
<module titre="Développement efficace" abbrev="Développement efficace" code="R3.02" heures_cours="0.0" heures_td="8.0" heures_tp="8.0" coefficient="0.0" ects="" semestre_id="3" numero="370" code_apogee="VINFR302" module_type="2">
<coefficients ue_reference="1986" coef="10.0"/>
<coefficients ue_reference="1987" coef="13.0"/>
</module>
<module titre="Analyse" abbrev="Analyse" code="R3.03" heures_cours="0.0" heures_td="8.0" heures_tp="8.0" coefficient="0.0" ects="" semestre_id="3" numero="380" code_apogee="VINFR303" module_type="2">
<coefficients ue_reference="1986" coef="12.0"/>
<coefficients ue_reference="1987" coef="5.0"/>
<coefficients ue_reference="1990" coef="10.0"/>
</module>
<module titre="Qualité de développement" abbrev="Qualité de développement" code="R3.04" heures_cours="0.0" heures_td="16.0" heures_tp="24.0" coefficient="0.0" ects="" semestre_id="3" numero="390" code_apogee="VINFR304" module_type="2">
<coefficients ue_reference="1986" coef="15.0"/>
<coefficients ue_reference="1990" coef="8.0"/>
<coefficients ue_reference="1991" coef="5.0"/>
</module>
<module titre="Programmation système" abbrev="Programmation système" code="R3.05" heures_cours="0.0" heures_td="12.0" heures_tp="12.0" coefficient="0.0" ects="" semestre_id="3" numero="400" code_apogee="VINFR305" module_type="2">
<coefficients ue_reference="1988" coef="22.0"/>
</module>
<module titre="Architecture des réseaux" abbrev="Architecture des réseaux" code="R3.06" heures_cours="0.0" heures_td="8.0" heures_tp="8.0" coefficient="0.0" ects="" semestre_id="3" numero="410" code_apogee="VINFR306" module_type="2">
<coefficients ue_reference="1987" coef="5.0"/>
<coefficients ue_reference="1988" coef="18.0"/>
</module>
<module titre="SQL dans un langage de programmation" abbrev="SQL dans un langage de programmation" code="R3.07" heures_cours="0.0" heures_td="12.0" heures_tp="16.0" coefficient="0.0" ects="" semestre_id="3" numero="420" code_apogee="VINFR307" module_type="2">
<coefficients ue_reference="1989" coef="25.0"/>
</module>
<module titre="Probabilités" abbrev="Probabilités" code="R3.08" heures_cours="0.0" heures_td="22.0" heures_tp="7.0" coefficient="0.0" ects="" semestre_id="3" numero="430" code_apogee="VINFR308" module_type="2">
<coefficients ue_reference="1987" coef="17.0"/>
<coefficients ue_reference="1989" coef="5.0"/>
</module>
<module titre="Cryptographie et sécurité" abbrev="Cryptographie et sécurité" code="R3.09" heures_cours="0.0" heures_td="8.0" heures_tp="12.0" coefficient="0.0" ects="" semestre_id="3" numero="440" code_apogee="VINFR309" module_type="2">
<coefficients ue_reference="1987" coef="10.0"/>
<coefficients ue_reference="1988" coef="10.0"/>
<coefficients ue_reference="1989" coef="5.0"/>
</module>
<module titre="Management des systèmes d'information" abbrev="Management des systèmes d'information" code="R3.10" heures_cours="0.0" heures_td="24.0" heures_tp="8.0" coefficient="0.0" ects="" semestre_id="3" numero="450" code_apogee="VINFR310" module_type="2">
<coefficients ue_reference="1989" coef="10.0"/>
<coefficients ue_reference="1990" coef="18.0"/>
<coefficients ue_reference="1991" coef="16.0"/>
</module>
<module titre="Droit des contrats et du numérique" abbrev="Droit des contrats et du numérique" code="R3.11" heures_cours="0.0" heures_td="28.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="3" numero="460" code_apogee="VINFR311" module_type="2">
<coefficients ue_reference="1986" coef="8.0"/>
<coefficients ue_reference="1989" coef="5.0"/>
<coefficients ue_reference="1990" coef="10.0"/>
</module>
<module titre="Anglais professionnel" abbrev="Anglais professionnel" code="R3.12" heures_cours="0.0" heures_td="16.0" heures_tp="8.0" coefficient="0.0" ects="" semestre_id="3" numero="470" code_apogee="VINFR312" module_type="2">
<coefficients ue_reference="1987" coef="5.0"/>
<coefficients ue_reference="1988" coef="5.0"/>
<coefficients ue_reference="1990" coef="7.0"/>
<coefficients ue_reference="1991" coef="8.0"/>
</module>
<module titre="Communication professionnelle" abbrev="Communication professionnelle" code="R3.13" heures_cours="0.0" heures_td="16.0" heures_tp="8.0" coefficient="0.0" ects="" semestre_id="3" numero="480" code_apogee="VINFR313" module_type="2">
<coefficients ue_reference="1990" coef="7.0"/>
<coefficients ue_reference="1991" coef="16.0"/>
</module>
<module titre="Développement d'une application" abbrev="Développement d'une application" code="S3.01" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="3" numero="490" code_apogee="VINFS301" module_type="3">
<coefficients ue_reference="1986" coef="40.0"/>
<coefficients ue_reference="1987" coef="40.0"/>
<coefficients ue_reference="1988" coef="40.0"/>
<coefficients ue_reference="1989" coef="40.0"/>
<coefficients ue_reference="1990" coef="40.0"/>
<coefficients ue_reference="1991" coef="40.0"/>
</module>
<module titre="Portfolio" abbrev="Portfolio" code="P3.01" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="3" numero="500" code_apogee="" module_type="3"/>
<module titre="Projet personnel et professionnel" abbrev="Projet personnel et professionnel" code="R3.14" heures_cours="0.0" heures_td="8.0" heures_tp="5.0" coefficient="0.0" ects="" semestre_id="3" numero="510" code_apogee="VINFR314" module_type="2">
<coefficients ue_reference="1991" coef="15.0"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE37" code_apogee="" numero="19" coefficient="0.0" titre="Sport / Culture" coef_rcue="1.0" semestre_idx="3" color="#444054" type="1" ue_code="X7.1" ects="0.0" reference="2551">
<matiere titre="UE7" numero="1">
<module titre="Sport / Culture" abbrev="Sport" code="X3.01" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="3" numero="680" code_apogee="" module_type="0"/>
</matiere>
</ue>
<ue is_external="0" acronyme="Sport, culture, engagement" code_apogee="" numero="20" coefficient="0.0" titre="Sport, culture, engagement" coef_rcue="1.0" semestre_idx="4" color="#444054" type="1" ue_code="OPT4" ects="1.0" reference="2594">
<matiere titre="Sport, culture, engagement" numero="1">
<module titre="Bonus" abbrev="Bonus" code="Bonus" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="690" code_apogee="" module_type="0"/>
</matiere>
</ue>
<ue is_external="0" acronyme="UE41" code_apogee="" numero="21" coefficient="0.0" titre="UE41 Compétence 1 : Réaliser un développement d'application" coef_rcue="1.0" semestre_idx="4" color="#b80004" type="0" ue_code="UCOD171" ects="5.0" apc_niveau_libelle="Partir des exigences et aller jusquà une application complète " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="2520">
<matiere titre="UE41 Compétence 1 : Réaliser un développement d'application" numero="1">
<module titre="Architecture logicielle" abbrev="Architecture logicielle" code="R4.01" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="530" code_apogee="" module_type="2">
<coefficients ue_reference="2520" coef="16.0"/>
<coefficients ue_reference="2522" coef="12.0"/>
<coefficients ue_reference="2525" coef="4.0"/>
<parcours code="A" numero="0" libelle="A : Réalisation dapplications : conception, développement, validation"/>
<app_critiques code="AC21.01" libelle="Élaborer et implémenter les spécifications fonctionnelles et non fonctionnelles à partir des exigences"/>
<app_critiques code="AC21.02" libelle="Appliquer des principes daccessibilité et dergonomie"/>
<app_critiques code="AC21.03" libelle="Adopter de bonnes pratiques de conception et de programmation"/>
<app_critiques code="AC23.01" libelle="Concevoir et développer des applications communicantes"/>
<app_critiques code="AC26.02" libelle="Appliquer une démarche pour intégrer une équipe informatique au sein dune organisation"/>
</module>
<module titre="Projet Personnel et Professionnel" abbrev="Projet Personnel et Professionnel" code="R4.07" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="590" code_apogee="" module_type="2">
<coefficients ue_reference="2525" coef="10.0"/>
<app_critiques code="AC21.01" libelle="Élaborer et implémenter les spécifications fonctionnelles et non fonctionnelles à partir des exigences"/>
<app_critiques code="AC21.02" libelle="Appliquer des principes daccessibilité et dergonomie"/>
<app_critiques code="AC21.03" libelle="Adopter de bonnes pratiques de conception et de programmation"/>
<app_critiques code="AC21.04" libelle="Vérifier et valider la qualité de lapplication par les tests"/>
<app_critiques code="AC22.01" libelle="Choisir des structures de données complexes adaptées au problème"/>
<app_critiques code="AC22.02" libelle="Utiliser des techniques algorithmiques adaptées pour des problèmes complexes (par ex. recherche opérationnelle, méthodes arborescentes, optimisation globale, intelligence artificielle...)"/>
<app_critiques code="AC22.03" libelle="Comprendre les enjeux et moyens de sécurisation des données et du code"/>
<app_critiques code="AC22.04" libelle="Évaluer limpact environnemental et sociétal des solutions proposées"/>
<app_critiques code="AC23.01" libelle="Concevoir et développer des applications communicantes"/>
<app_critiques code="AC23.02" libelle="Utiliser des serveurs et des services réseaux virtualisés"/>
<app_critiques code="AC23.03" libelle="Sécuriser les services et données dun système"/>
<app_critiques code="AC24.01" libelle="Optimiser les modèles de données de lentreprise"/>
<app_critiques code="AC24.02" libelle="Assurer la sécurité des données (intégrité et confidentialité)"/>
<app_critiques code="AC24.03" libelle="Organiser la restitution de données à travers la programmation et la visualisation"/>
<app_critiques code="AC24.04" libelle="Manipuler des données hétérogènes"/>
<app_critiques code="AC25.01" libelle="Identifier les processus présents dans une organisation en vue daméliorer les systèmes dinformation"/>
<app_critiques code="AC25.02" libelle="Formaliser les besoins du client et de l'utilisateur"/>
<app_critiques code="AC25.03" libelle="Identifier les critères de faisabilité dun projet informatique"/>
<app_critiques code="AC25.04" libelle="Définir et mettre en œuvre une démarche de suivi de projet"/>
<app_critiques code="AC26.01" libelle="Comprendre la diversité, la structure et la dimension de linformatique dans une organisation (ESN, DSI,...)"/>
<app_critiques code="AC26.02" libelle="Appliquer une démarche pour intégrer une équipe informatique au sein dune organisation"/>
<app_critiques code="AC26.03" libelle="Mobiliser les compétences interpersonnelles pour travailler dans une équipe informatique"/>
<app_critiques code="AC26.04" libelle="Rendre compte de son activité professionnelle"/>
</module>
<module titre="Complétement web" abbrev="Complétement web" code="R4.A.10" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="620" code_apogee="" module_type="2">
<coefficients ue_reference="2520" coef="8.0"/>
<coefficients ue_reference="2521" coef="4.0"/>
<coefficients ue_reference="2523" coef="8.0"/>
<coefficients ue_reference="2524" coef="4.0"/>
<parcours code="A" numero="0" libelle="A : Réalisation dapplications : conception, développement, validation"/>
<app_critiques code="AC21.01" libelle="Élaborer et implémenter les spécifications fonctionnelles et non fonctionnelles à partir des exigences"/>
<app_critiques code="AC21.02" libelle="Appliquer des principes daccessibilité et dergonomie"/>
<app_critiques code="AC22.04" libelle="Évaluer limpact environnemental et sociétal des solutions proposées"/>
<app_critiques code="AC24.02" libelle="Assurer la sécurité des données (intégrité et confidentialité)"/>
<app_critiques code="AC24.03" libelle="Organiser la restitution de données à travers la programmation et la visualisation"/>
<app_critiques code="AC25.02" libelle="Formaliser les besoins du client et de l'utilisateur"/>
</module>
<module titre="Développement pour applications mobiles" abbrev="Développement pour applications mobiles" code="R4.A.11" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="630" code_apogee="" module_type="2">
<coefficients ue_reference="2520" coef="8.0"/>
<coefficients ue_reference="2521" coef="4.0"/>
<coefficients ue_reference="2523" coef="8.0"/>
<coefficients ue_reference="2524" coef="4.0"/>
<parcours code="A" numero="0" libelle="A : Réalisation dapplications : conception, développement, validation"/>
<app_critiques code="AC21.01" libelle="Élaborer et implémenter les spécifications fonctionnelles et non fonctionnelles à partir des exigences"/>
<app_critiques code="AC22.04" libelle="Évaluer limpact environnemental et sociétal des solutions proposées"/>
<app_critiques code="AC24.03" libelle="Organiser la restitution de données à travers la programmation et la visualisation"/>
<app_critiques code="AC25.02" libelle="Formaliser les besoins du client et de l'utilisateur"/>
</module>
<module titre="Développement d'une application complexe" abbrev="Développement d'une application complexe" code="S4.A.01" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="650" code_apogee="" module_type="3">
<coefficients ue_reference="2520" coef="15.0"/>
<coefficients ue_reference="2521" coef="15.0"/>
<coefficients ue_reference="2522" coef="15.0"/>
<coefficients ue_reference="2523" coef="15.0"/>
<coefficients ue_reference="2524" coef="15.0"/>
<coefficients ue_reference="2525" coef="15.0"/>
<parcours code="A" numero="0" libelle="A : Réalisation dapplications : conception, développement, validation"/>
<app_critiques code="AC21.04" libelle="Vérifier et valider la qualité de lapplication par les tests"/>
<app_critiques code="AC22.02" libelle="Utiliser des techniques algorithmiques adaptées pour des problèmes complexes (par ex. recherche opérationnelle, méthodes arborescentes, optimisation globale, intelligence artificielle...)"/>
<app_critiques code="AC22.03" libelle="Comprendre les enjeux et moyens de sécurisation des données et du code"/>
<app_critiques code="AC22.04" libelle="Évaluer limpact environnemental et sociétal des solutions proposées"/>
<app_critiques code="AC23.03" libelle="Sécuriser les services et données dun système"/>
<app_critiques code="AC24.01" libelle="Optimiser les modèles de données de lentreprise"/>
<app_critiques code="AC24.02" libelle="Assurer la sécurité des données (intégrité et confidentialité)"/>
<app_critiques code="AC25.02" libelle="Formaliser les besoins du client et de l'utilisateur"/>
<app_critiques code="AC25.03" libelle="Identifier les critères de faisabilité dun projet informatique"/>
<app_critiques code="AC25.04" libelle="Définir et mettre en œuvre une démarche de suivi de projet"/>
<app_critiques code="AC26.02" libelle="Appliquer une démarche pour intégrer une équipe informatique au sein dune organisation"/>
<app_critiques code="AC26.03" libelle="Mobiliser les compétences interpersonnelles pour travailler dans une équipe informatique"/>
<app_critiques code="AC26.04" libelle="Rendre compte de son activité professionnelle"/>
</module>
<module titre="Stage" abbrev="Stage" code="S4.St" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="660" code_apogee="" module_type="3">
<coefficients ue_reference="2520" coef="40.0"/>
<coefficients ue_reference="2521" coef="40.0"/>
<coefficients ue_reference="2522" coef="40.0"/>
<coefficients ue_reference="2523" coef="40.0"/>
<coefficients ue_reference="2524" coef="40.0"/>
<coefficients ue_reference="2525" coef="40.0"/>
<app_critiques code="AC21.01" libelle="Élaborer et implémenter les spécifications fonctionnelles et non fonctionnelles à partir des exigences"/>
<app_critiques code="AC21.02" libelle="Appliquer des principes daccessibilité et dergonomie"/>
<app_critiques code="AC21.03" libelle="Adopter de bonnes pratiques de conception et de programmation"/>
<app_critiques code="AC21.04" libelle="Vérifier et valider la qualité de lapplication par les tests"/>
<app_critiques code="AC22.01" libelle="Choisir des structures de données complexes adaptées au problème"/>
<app_critiques code="AC22.02" libelle="Utiliser des techniques algorithmiques adaptées pour des problèmes complexes (par ex. recherche opérationnelle, méthodes arborescentes, optimisation globale, intelligence artificielle...)"/>
<app_critiques code="AC22.03" libelle="Comprendre les enjeux et moyens de sécurisation des données et du code"/>
<app_critiques code="AC22.04" libelle="Évaluer limpact environnemental et sociétal des solutions proposées"/>
<app_critiques code="AC23.01" libelle="Concevoir et développer des applications communicantes"/>
<app_critiques code="AC23.02" libelle="Utiliser des serveurs et des services réseaux virtualisés"/>
<app_critiques code="AC23.03" libelle="Sécuriser les services et données dun système"/>
<app_critiques code="AC24.01" libelle="Optimiser les modèles de données de lentreprise"/>
<app_critiques code="AC24.02" libelle="Assurer la sécurité des données (intégrité et confidentialité)"/>
<app_critiques code="AC24.03" libelle="Organiser la restitution de données à travers la programmation et la visualisation"/>
<app_critiques code="AC24.04" libelle="Manipuler des données hétérogènes"/>
<app_critiques code="AC25.01" libelle="Identifier les processus présents dans une organisation en vue daméliorer les systèmes dinformation"/>
<app_critiques code="AC25.02" libelle="Formaliser les besoins du client et de l'utilisateur"/>
<app_critiques code="AC25.03" libelle="Identifier les critères de faisabilité dun projet informatique"/>
<app_critiques code="AC25.04" libelle="Définir et mettre en œuvre une démarche de suivi de projet"/>
<app_critiques code="AC26.01" libelle="Comprendre la diversité, la structure et la dimension de linformatique dans une organisation (ESN, DSI,...)"/>
<app_critiques code="AC26.02" libelle="Appliquer une démarche pour intégrer une équipe informatique au sein dune organisation"/>
<app_critiques code="AC26.03" libelle="Mobiliser les compétences interpersonnelles pour travailler dans une équipe informatique"/>
<app_critiques code="AC26.04" libelle="Rendre compte de son activité professionnelle"/>
</module>
<module titre="Portfolio" abbrev="Portfolio" code="P4.01" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="670" code_apogee="" module_type="3">
<coefficients ue_reference="2520" coef="5.0"/>
<coefficients ue_reference="2521" coef="5.0"/>
<coefficients ue_reference="2522" coef="5.0"/>
<coefficients ue_reference="2523" coef="5.0"/>
<coefficients ue_reference="2524" coef="5.0"/>
<coefficients ue_reference="2525" coef="5.0"/>
<app_critiques code="AC21.01" libelle="Élaborer et implémenter les spécifications fonctionnelles et non fonctionnelles à partir des exigences"/>
<app_critiques code="AC21.02" libelle="Appliquer des principes daccessibilité et dergonomie"/>
<app_critiques code="AC21.03" libelle="Adopter de bonnes pratiques de conception et de programmation"/>
<app_critiques code="AC21.04" libelle="Vérifier et valider la qualité de lapplication par les tests"/>
<app_critiques code="AC22.01" libelle="Choisir des structures de données complexes adaptées au problème"/>
<app_critiques code="AC22.02" libelle="Utiliser des techniques algorithmiques adaptées pour des problèmes complexes (par ex. recherche opérationnelle, méthodes arborescentes, optimisation globale, intelligence artificielle...)"/>
<app_critiques code="AC22.03" libelle="Comprendre les enjeux et moyens de sécurisation des données et du code"/>
<app_critiques code="AC22.04" libelle="Évaluer limpact environnemental et sociétal des solutions proposées"/>
<app_critiques code="AC23.01" libelle="Concevoir et développer des applications communicantes"/>
<app_critiques code="AC23.02" libelle="Utiliser des serveurs et des services réseaux virtualisés"/>
<app_critiques code="AC23.03" libelle="Sécuriser les services et données dun système"/>
<app_critiques code="AC24.01" libelle="Optimiser les modèles de données de lentreprise"/>
<app_critiques code="AC24.02" libelle="Assurer la sécurité des données (intégrité et confidentialité)"/>
<app_critiques code="AC24.03" libelle="Organiser la restitution de données à travers la programmation et la visualisation"/>
<app_critiques code="AC24.04" libelle="Manipuler des données hétérogènes"/>
<app_critiques code="AC25.01" libelle="Identifier les processus présents dans une organisation en vue daméliorer les systèmes dinformation"/>
<app_critiques code="AC25.02" libelle="Formaliser les besoins du client et de l'utilisateur"/>
<app_critiques code="AC25.03" libelle="Identifier les critères de faisabilité dun projet informatique"/>
<app_critiques code="AC25.04" libelle="Définir et mettre en œuvre une démarche de suivi de projet"/>
<app_critiques code="AC26.01" libelle="Comprendre la diversité, la structure et la dimension de linformatique dans une organisation (ESN, DSI,...)"/>
<app_critiques code="AC26.02" libelle="Appliquer une démarche pour intégrer une équipe informatique au sein dune organisation"/>
<app_critiques code="AC26.03" libelle="Mobiliser les compétences interpersonnelles pour travailler dans une équipe informatique"/>
<app_critiques code="AC26.04" libelle="Rendre compte de son activité professionnelle"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE42" code_apogee="" numero="22" coefficient="0.0" titre="UE42 Compétence 2 : Optimiser des applications informatiques" coef_rcue="1.0" semestre_idx="4" color="#f97b3d" type="0" ue_code="UCOD172" ects="5.0" apc_niveau_libelle="Sélectionner les algorithmes adéquats pour répondre à un problème donné " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="2521">
<matiere titre="UE42 Compétence 2 : Optimiser des applications informatiques" numero="1">
<module titre="Méthodes d'optimisation" abbrev="Méthodes d'optimisation" code="R4.04" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="560" code_apogee="" module_type="2">
<coefficients ue_reference="2521" coef="12.0"/>
<parcours code="A" numero="0" libelle="A : Réalisation dapplications : conception, développement, validation"/>
<app_critiques code="AC22.02" libelle="Utiliser des techniques algorithmiques adaptées pour des problèmes complexes (par ex. recherche opérationnelle, méthodes arborescentes, optimisation globale, intelligence artificielle...)"/>
</module>
<module titre="Automates et langages" abbrev="Automates et langages" code="R4.A.12" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="640" code_apogee="" module_type="2">
<coefficients ue_reference="2521" coef="12.0"/>
<parcours code="A" numero="0" libelle="A : Réalisation dapplications : conception, développement, validation"/>
<app_critiques code="AC22.01" libelle="Choisir des structures de données complexes adaptées au problème"/>
<app_critiques code="AC22.02" libelle="Utiliser des techniques algorithmiques adaptées pour des problèmes complexes (par ex. recherche opérationnelle, méthodes arborescentes, optimisation globale, intelligence artificielle...)"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE43" code_apogee="" numero="23" coefficient="0.0" titre="UE43 Compétence 3 : Administrer des systèmes informatiques communicants" coef_rcue="1.0" semestre_idx="4" color="#feb40b" type="0" ue_code="UCOD173" ects="5.0" apc_niveau_libelle="Déployer des services dans une architecture réseau" apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="2522">
<matiere titre="UE43 Compétence 3 : Administrer des systèmes informatiques communicants" numero="1">
<module titre="Virtualisation" abbrev="Virtualisation" code="R4.A.08" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="600" code_apogee="" module_type="2">
<coefficients ue_reference="2522" coef="28.0"/>
<parcours code="A" numero="0" libelle="A : Réalisation dapplications : conception, développement, validation"/>
<app_critiques code="AC23.02" libelle="Utiliser des serveurs et des services réseaux virtualisés"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE44" code_apogee="" numero="24" coefficient="0.0" titre="UE44 Compétence 4 : Gérer des données de l'information" coef_rcue="1.0" semestre_idx="4" color="#80cb3f" type="0" ue_code="UCOD174" ects="5.0" apc_niveau_libelle="Optimiser une base de données, interagir avec une application et mettre en œuvre la sécurité " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="2523">
<matiere titre="UE44 Compétence 4 : Gérer des données de l'information" numero="1">
<module titre="Qualité &amp; Non-relationnel" abbrev="Qualité &amp; Non-relationnel" code="R4.03" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="550" code_apogee="" module_type="2">
<coefficients ue_reference="2523" coef="18.0"/>
<parcours code="A" numero="0" libelle="A : Réalisation dapplications : conception, développement, validation"/>
<app_critiques code="AC24.01" libelle="Optimiser les modèles de données de lentreprise"/>
<app_critiques code="AC24.04" libelle="Manipuler des données hétérogènes"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE45" code_apogee="" numero="25" coefficient="0.0" titre="UE45 Compétence 5 : Conduire un projet" coef_rcue="1.0" semestre_idx="4" color="#05162e" type="0" ue_code="UCOD175" ects="5.0" apc_niveau_libelle="Appliquer une démarche de suivi de projet en fonction des besoins métiers des clients et des utilisateurs " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="2524">
<matiere titre="UE45 Compétence 5 : Conduire un projet" numero="1">
<module titre="Qualité de développement" abbrev="Qualité de développement" code="R4.02" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="540" code_apogee="" module_type="2">
<coefficients ue_reference="2520" coef="8.0"/>
<coefficients ue_reference="2524" coef="10.0"/>
<parcours code="A" numero="0" libelle="A : Réalisation dapplications : conception, développement, validation"/>
<app_critiques code="AC21.04" libelle="Vérifier et valider la qualité de lapplication par les tests"/>
<app_critiques code="AC25.03" libelle="Identifier les critères de faisabilité dun projet informatique"/>
</module>
<module titre="Management avancé des systèmes d'information" abbrev="Management avancé des systèmes d'information" code="R4.A.09" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="610" code_apogee="" module_type="2">
<coefficients ue_reference="2521" coef="4.0"/>
<coefficients ue_reference="2524" coef="22.0"/>
<parcours code="A" numero="0" libelle="A : Réalisation dapplications : conception, développement, validation"/>
<app_critiques code="AC22.04" libelle="Évaluer limpact environnemental et sociétal des solutions proposées"/>
<app_critiques code="AC25.03" libelle="Identifier les critères de faisabilité dun projet informatique"/>
<app_critiques code="AC25.04" libelle="Définir et mettre en œuvre une démarche de suivi de projet"/>
</module>
</matiere>
</ue>
<ue is_external="0" acronyme="UE46" code_apogee="" numero="26" coefficient="0.0" titre="UE46 Compétence 6 : Travailler dans une équipe informatique" coef_rcue="1.0" semestre_idx="4" color="#548687" type="0" ue_code="UCOD176" ects="5.0" apc_niveau_libelle="Situer son rôle et ses missions au sein dune équipe informatique " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="2525">
<matiere titre="UE46 Compétence 6 : Travailler dans une équipe informatique" numero="1">
<module titre="Anglais" abbrev="Anglais" code="R4.05" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="570" code_apogee="" module_type="2">
<coefficients ue_reference="2521" coef="4.0"/>
<coefficients ue_reference="2525" coef="13.0"/>
<parcours code="A" numero="0" libelle="A : Réalisation dapplications : conception, développement, validation"/>
<app_critiques code="AC22.04" libelle="Évaluer limpact environnemental et sociétal des solutions proposées"/>
<app_critiques code="AC26.03" libelle="Mobiliser les compétences interpersonnelles pour travailler dans une équipe informatique"/>
<app_critiques code="AC26.04" libelle="Rendre compte de son activité professionnelle"/>
</module>
<module titre="Communication interne" abbrev="Communication interne" code="R4.06" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="580" code_apogee="" module_type="2">
<coefficients ue_reference="2523" coef="6.0"/>
<coefficients ue_reference="2525" coef="13.0"/>
<parcours code="A" numero="0" libelle="A : Réalisation dapplications : conception, développement, validation"/>
<app_critiques code="AC24.04" libelle="Manipuler des données hétérogènes"/>
<app_critiques code="AC26.02" libelle="Appliquer une démarche pour intégrer une équipe informatique au sein dune organisation"/>
<app_critiques code="AC26.04" libelle="Rendre compte de son activité professionnelle"/>
</module>
</matiere>
</ue>
</formation>

View File

@ -45,7 +45,7 @@ from tools.fakeportal.gen_nomprenoms import nomprenom
random.seed(12345678) # tests reproductibles
# La formation à utiliser:
FORMATION_XML_FILENAME = "tests/ressources/formations/scodoc_formation_RT_BUT_RT_v1.xml"
FORMATION_XML_FILENAME = "tests/ressources/formations/scodoc_formation_BUT_RT_v1.xml"
REFCOMP_FILENAME = (
"ressources/referentiels/but2022/competences/but-RT-05012022-081735.xml"
)