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

View File

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

View File

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

View File

@ -12,7 +12,8 @@
""" """
from datetime import datetime 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 from flask_login import login_required
import app import app
@ -41,24 +42,27 @@ def get_departement(dept_ident: str) -> Departement:
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def departements_list(): def departements_list():
"""Liste les départements""" """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") @bp.route("/departements_ids")
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def departements_ids(): def departements_ids():
"""Liste des ids de départements""" """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>") @bp.route("/departement/<string:acronym>")
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def departement(acronym: str): def departement(acronym: str):
""" """
Info sur un département. Accès par acronyme. 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() 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>") @bp.route("/departement/id/<int:dept_id>")
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def departement_by_id(dept_id: int): def departement_by_id(dept_id: int):
""" """
Info sur un département. Accès par id. Info sur un département. Accès par id.
""" """
dept = Departement.query.get_or_404(dept_id) dept = Departement.query.get_or_404(dept_id)
return jsonify(dept.to_dict()) return dept.to_dict()
@bp.route("/departement/create", methods=["POST"]) @bp.route("/departement/create", methods=["POST"])
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
@as_json
def departement_create(): def departement_create():
""" """
Création d'un département. Création d'un département.
@ -111,13 +117,14 @@ def departement_create():
dept = departements.create_dept(acronym, visible=visible) dept = departements.create_dept(acronym, visible=visible)
except ScoValueError as exc: except ScoValueError as exc:
return json_error(500, exc.args[0] if exc.args else "") 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"]) @bp.route("/departement/<string:acronym>/edit", methods=["POST"])
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
@as_json
def departement_edit(acronym): def departement_edit(acronym):
""" """
Edition d'un département: seul visible peut être modifié Edition d'un département: seul visible peut être modifié
@ -135,7 +142,7 @@ def departement_edit(acronym):
dept.visible = visible dept.visible = visible
db.session.add(dept) db.session.add(dept)
db.session.commit() db.session.commit()
return jsonify(dept.to_dict()) return dept.to_dict()
@bp.route("/departement/<string:acronym>/delete", methods=["POST"]) @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() dept = Departement.query.filter_by(acronym=acronym).first_or_404()
db.session.delete(dept) db.session.delete(dept)
db.session.commit() db.session.commit()
return jsonify({"OK": True}) return {"OK": True}
@bp.route("/departement/<string:acronym>/etudiants", methods=["GET"]) @bp.route("/departement/<string:acronym>/etudiants", methods=["GET"])
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def dept_etudiants(acronym: str): def dept_etudiants(acronym: str):
""" """
Retourne la liste des étudiants d'un département 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() 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") @bp.route("/departement/id/<int:dept_id>/etudiants")
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def dept_etudiants_by_id(dept_id: int): def dept_etudiants_by_id(dept_id: int):
""" """
Retourne la liste des étudiants d'un département d'id donné. Retourne la liste des étudiants d'un département d'id donné.
""" """
dept = Departement.query.get_or_404(dept_id) 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") @bp.route("/departement/<string:acronym>/formsemestres_ids")
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def dept_formsemestres_ids(acronym: str): def dept_formsemestres_ids(acronym: str):
"""liste des ids formsemestre du département""" """liste des ids formsemestre du département"""
dept = Departement.query.filter_by(acronym=acronym).first_or_404() 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") @bp.route("/departement/id/<int:dept_id>/formsemestres_ids")
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def dept_formsemestres_ids_by_id(dept_id: int): def dept_formsemestres_ids_by_id(dept_id: int):
"""liste des ids formsemestre du département""" """liste des ids formsemestre du département"""
dept = Departement.query.get_or_404(dept_id) 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") @bp.route("/departement/<string:acronym>/formsemestres_courants")
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def dept_formsemestres_courants(acronym: str): def dept_formsemestres_courants(acronym: str):
""" """
Liste des semestres actifs d'un département d'acronyme donné 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_debut <= test_date,
FormSemestre.date_fin >= 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") @bp.route("/departement/id/<int:dept_id>/formsemestres_courants")
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def dept_formsemestres_courants_by_id(dept_id: int): def dept_formsemestres_courants_by_id(dept_id: int):
""" """
Liste des semestres actifs d'un département d'id donné 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, 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 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 current_user
from flask_login import login_required from flask_login import login_required
from sqlalchemy import desc, or_ from sqlalchemy import desc, or_
@ -38,11 +39,11 @@ import app.scodoc.sco_photos as sco_photos
# @login_required # @login_required
# @scodoc # @scodoc
# @permission_required(Permission.ScoView) # @permission_required(Permission.ScoView)
# @as_json
# def api_function(arg: int): # def api_function(arg: int):
# """Une fonction quelconque de l'API""" # """Une fonction quelconque de l'API"""
# return jsonify( # return {"current_user": current_user.to_dict(), "arg": arg, "dept": g.scodoc_dept}
# {"current_user": current_user.to_dict(), "arg": arg, "dept": g.scodoc_dept} #
# )
@bp.route("/etudiants/courants", defaults={"long": False}) @bp.route("/etudiants/courants", defaults={"long": False})
@ -52,6 +53,7 @@ import app.scodoc.sco_photos as sco_photos
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def etudiants_courants(long=False): def etudiants_courants(long=False):
""" """
La liste des étudiants des semestres "courants" (tous départements) 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] data = [etud.to_dict_api() for etud in etuds]
else: else:
data = [etud.to_dict_short() for etud in etuds] data = [etud.to_dict_short() for etud in etuds]
return jsonify(data) return data
@bp.route("/etudiant/etudid/<int:etudid>") @bp.route("/etudiant/etudid/<int:etudid>")
@ -109,6 +111,7 @@ def etudiants_courants(long=False):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def etudiant(etudid: int = None, nip: str = None, ine: str = None): def etudiant(etudid: int = None, nip: str = None, ine: str = None):
""" """
Retourne les informations de l'étudiant correspondant, ou 404 si non trouvé. 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", message="étudiant inconnu",
) )
return jsonify(etud.to_dict_api()) return etud.to_dict_api()
@api_web_bp.route("/etudiant/etudid/<int:etudid>/photo") @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"]) @api_web_bp.route("/etudiants/ine/<string:ine>", methods=["GET"])
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def etudiants(etudid: int = None, nip: str = None, ine: str = None): def etudiants(etudid: int = None, nip: str = None, ine: str = None):
""" """
Info sur le ou les étudiants correspondant. Comme /etudiant mais renvoie 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( etuds = etuds.join(Departement).filter(
or_(Departement.acronym == acronym for acronym in allowed_depts) 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") @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") @api_web_bp.route("/etudiant/ine/<string:ine>/formsemestres")
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None): def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None):
""" """
Liste des semestres qu'un étudiant a suivi, triés par ordre chronologique. Liste des semestres qu'un étudiant a suivi, triés par ordre chronologique.
@ -243,7 +248,7 @@ def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None)
formsemestres = query.order_by(FormSemestre.date_debut) 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( @bp.route(
@ -302,7 +307,7 @@ def bulletin(
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first_or_404() formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first_or_404()
dept = Departement.query.filter_by(id=formsemestre.dept_id).first_or_404() dept = Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
if g.scodoc_dept and dept.acronym != g.scodoc_dept: 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) app.set_sco_dept(dept.acronym)
if code_type == "nip": if code_type == "nip":
@ -340,6 +345,7 @@ def bulletin(
) )
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def etudiant_groups(formsemestre_id: int, etudid: int = None): def etudiant_groups(formsemestre_id: int, etudid: int = None):
""" """
Retourne la liste des groupes auxquels appartient l'étudiant dans le formsemestre indiqué 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) app.set_sco_dept(dept.acronym)
data = sco_groups.get_etud_groups(etud.id, formsemestre.id) 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 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 from flask_login import login_required
import app import app
@ -26,7 +27,8 @@ import app.scodoc.sco_utils as scu
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
def evaluation(evaluation_id: int): @as_json
def the_eval(evaluation_id: int):
"""Description d'une évaluation. """Description d'une évaluation.
{ {
@ -56,7 +58,7 @@ def evaluation(evaluation_id: int):
.filter_by(dept_id=g.scodoc_dept_id) .filter_by(dept_id=g.scodoc_dept_id)
) )
e = query.first_or_404() e = query.first_or_404()
return jsonify(e.to_dict_api()) return e.to_dict_api()
@bp.route("/moduleimpl/<int:moduleimpl_id>/evaluations") @bp.route("/moduleimpl/<int:moduleimpl_id>/evaluations")
@ -64,6 +66,7 @@ def evaluation(evaluation_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def evaluations(moduleimpl_id: int): def evaluations(moduleimpl_id: int):
""" """
Retourne la liste des évaluations d'un moduleimpl Retourne la liste des évaluations d'un moduleimpl
@ -79,7 +82,7 @@ def evaluations(moduleimpl_id: int):
.join(FormSemestre) .join(FormSemestre)
.filter_by(dept_id=g.scodoc_dept_id) .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") @bp.route("/evaluation/<int:evaluation_id>/notes")
@ -87,6 +90,7 @@ def evaluations(moduleimpl_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def evaluation_notes(evaluation_id: int): def evaluation_notes(evaluation_id: int):
""" """
Retourne la liste des notes à partir de l'id d'une évaluation donnée 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) .filter_by(dept_id=g.scodoc_dept_id)
) )
evaluation = query.first_or_404() the_eval = query.first_or_404()
dept = evaluation.moduleimpl.formsemestre.departement dept = the_eval.moduleimpl.formsemestre.departement
app.set_sco_dept(dept.acronym) app.set_sco_dept(dept.acronym)
notes = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id) 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. # "ABS", "EXC", etc mais laisse les notes sur le barème de l'éval.
note = notes[etudid] note = notes[etudid]
note["value"] = scu.fmt_note(note["value"], keep_numeric=True) 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"] del note["id"]
return jsonify(notes) return notes

View File

@ -8,7 +8,8 @@
ScoDoc 9 API : accès aux formations 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 from flask_login import login_required
import app import app
@ -18,7 +19,6 @@ from app.scodoc.sco_utils import json_error
from app.decorators import scodoc, permission_required from app.decorators import scodoc, permission_required
from app.models import ApcParcours, Formation, FormSemestre, ModuleImpl, UniteEns from app.models import ApcParcours, Formation, FormSemestre, ModuleImpl, UniteEns
from app.scodoc import sco_formations from app.scodoc import sco_formations
from app.scodoc.sco_exceptions import ScoFormationConflict
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@ -27,6 +27,7 @@ from app.scodoc.sco_permissions import Permission
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def formations(): def formations():
""" """
Retourne la liste de toutes les formations (tous départements) Retourne la liste de toutes les formations (tous départements)
@ -35,7 +36,7 @@ def formations():
if g.scodoc_dept: if g.scodoc_dept:
query = query.filter_by(dept_id=g.scodoc_dept_id) 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") @bp.route("/formations_ids")
@ -43,6 +44,7 @@ def formations():
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def formations_ids(): def formations_ids():
""" """
Retourne la liste de toutes les id de formations (tous départements) Retourne la liste de toutes les id de formations (tous départements)
@ -52,7 +54,7 @@ def formations_ids():
query = Formation.query query = Formation.query
if g.scodoc_dept: if g.scodoc_dept:
query = query.filter_by(dept_id=g.scodoc_dept_id) 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>") @bp.route("/formation/<int:formation_id>")
@ -60,6 +62,7 @@ def formations_ids():
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def formation_by_id(formation_id: int): def formation_by_id(formation_id: int):
""" """
La formation d'id donné La formation d'id donné
@ -84,7 +87,7 @@ def formation_by_id(formation_id: int):
query = Formation.query.filter_by(id=formation_id) query = Formation.query.filter_by(id=formation_id)
if g.scodoc_dept: if g.scodoc_dept:
query = query.filter_by(dept_id=g.scodoc_dept_id) 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( @bp.route(
@ -106,6 +109,7 @@ def formation_by_id(formation_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def formation_export_by_formation_id(formation_id: int, export_ids=False): def formation_export_by_formation_id(formation_id: int, export_ids=False):
""" """
Retourne la formation, avec UE, matières, modules 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: except ValueError:
return json_error(500, message="Erreur inconnue") return json_error(500, message="Erreur inconnue")
return jsonify(data) return data
@bp.route("/formation/<int:formation_id>/referentiel_competences") @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 @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def referentiel_competences(formation_id: int): def referentiel_competences(formation_id: int):
""" """
Retourne le référentiel de compétences 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) query = query.filter_by(dept_id=g.scodoc_dept_id)
formation = query.first_or_404(formation_id) formation = query.first_or_404(formation_id)
if formation.referentiel_competence is None: if formation.referentiel_competence is None:
return jsonify(None) return None
return jsonify(formation.referentiel_competence.to_dict()) return formation.referentiel_competence.to_dict()
@bp.route("/moduleimpl/<int:moduleimpl_id>") @bp.route("/moduleimpl/<int:moduleimpl_id>")
@ -242,6 +247,7 @@ def referentiel_competences(formation_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def moduleimpl(moduleimpl_id: int): def moduleimpl(moduleimpl_id: int):
""" """
Retourne un moduleimpl en fonction de son id Retourne un moduleimpl en fonction de son id
@ -281,7 +287,7 @@ def moduleimpl(moduleimpl_id: int):
if g.scodoc_dept: if g.scodoc_dept:
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id) query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
modimpl: ModuleImpl = query.first_or_404() 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"]) @bp.route("/set_ue_parcours/<int:ue_id>", methods=["POST"])
@ -289,6 +295,7 @@ def moduleimpl(moduleimpl_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoChangeFormation) @permission_required(Permission.ScoChangeFormation)
@as_json
def set_ue_parcours(ue_id: int): def set_ue_parcours(ue_id: int):
"""Associe UE et parcours BUT. """Associe UE et parcours BUT.
La liste des ids de parcours est passée en argument JSON. 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}") log(f"set_ue_parcours: ue_id={ue.id} parcours_ids={parcours_ids}")
ok, error_message = ue.set_parcours(parcours) 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 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 from flask_login import login_required
import app import app
@ -42,6 +43,7 @@ from app.tables.recap import TableRecap
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def formsemestre_infos(formsemestre_id: int): def formsemestre_infos(formsemestre_id: int):
""" """
Information sur le formsemestre indiqué. Information sur le formsemestre indiqué.
@ -83,7 +85,7 @@ def formsemestre_infos(formsemestre_id: int):
if g.scodoc_dept: if g.scodoc_dept:
query = query.filter_by(dept_id=g.scodoc_dept_id) query = query.filter_by(dept_id=g.scodoc_dept_id)
formsemestre: FormSemestre = query.first_or_404(formsemestre_id) formsemestre: FormSemestre = query.first_or_404(formsemestre_id)
return jsonify(formsemestre.to_dict_api()) return formsemestre.to_dict_api()
@bp.route("/formsemestres/query") @bp.route("/formsemestres/query")
@ -91,6 +93,7 @@ def formsemestre_infos(formsemestre_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def formsemestres_query(): def formsemestres_query():
""" """
Retourne les formsemestres filtrés par Retourne les formsemestres filtrés par
@ -146,7 +149,7 @@ def formsemestres_query():
formsemestres = formsemestres.join(FormSemestreInscription).join(Identite) formsemestres = formsemestres.join(FormSemestreInscription).join(Identite)
formsemestres = formsemestres.filter_by(code_ine=ine) 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") @bp.route("/formsemestre/<int:formsemestre_id>/bulletins")
@ -156,6 +159,7 @@ def formsemestres_query():
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def bulletins(formsemestre_id: int, version: str = "long"): def bulletins(formsemestre_id: int, version: str = "long"):
""" """
Retourne les bulletins d'un formsemestre donné Retourne les bulletins d'un formsemestre donné
@ -179,7 +183,7 @@ def bulletins(formsemestre_id: int, version: str = "long"):
) )
data.append(bul_etu.json) data.append(bul_etu.json)
return jsonify(data) return data
@bp.route("/formsemestre/<int:formsemestre_id>/programme") @bp.route("/formsemestre/<int:formsemestre_id>/programme")
@ -187,6 +191,7 @@ def bulletins(formsemestre_id: int, version: str = "long"):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def formsemestre_programme(formsemestre_id: int): def formsemestre_programme(formsemestre_id: int):
""" """
Retourne la liste des Ues, ressources et SAE d'un semestre 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: for modimpl in formsemestre.modimpls_sorted:
d = modimpl.to_dict(convert_objects=True) d = modimpl.to_dict(convert_objects=True)
m_list[modimpl.module.module_type].append(d) m_list[modimpl.module.module_type].append(d)
return jsonify( return {
{ "ues": [ue.to_dict(convert_objects=True) for ue in ues],
"ues": [ue.to_dict(convert_objects=True) for ue in ues], "ressources": m_list[ModuleType.RESSOURCE],
"ressources": m_list[ModuleType.RESSOURCE], "saes": m_list[ModuleType.SAE],
"saes": m_list[ModuleType.SAE], "modules": m_list[ModuleType.STANDARD],
"modules": m_list[ModuleType.STANDARD], "malus": m_list[ModuleType.MALUS],
"malus": m_list[ModuleType.MALUS], }
}
)
@bp.route( @bp.route(
@ -312,6 +315,7 @@ def formsemestre_programme(formsemestre_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def formsemestre_etudiants( def formsemestre_etudiants(
formsemestre_id: int, with_query: bool = False, long: bool = False formsemestre_id: int, with_query: bool = False, long: bool = False
): ):
@ -347,7 +351,7 @@ def formsemestre_etudiants(
etud["id"], formsemestre_id, exclude_default=True 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") @bp.route("/formsemestre/<int:formsemestre_id>/etat_evals")
@ -355,6 +359,7 @@ def formsemestre_etudiants(
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def etat_evals(formsemestre_id: int): def etat_evals(formsemestre_id: int):
""" """
Informations sur l'état des évaluations d'un formsemestre. Informations sur l'état des évaluations d'un formsemestre.
@ -456,7 +461,7 @@ def etat_evals(formsemestre_id: int):
modimpl_dict["evaluations"] = list_eval modimpl_dict["evaluations"] = list_eval
result.append(modimpl_dict) result.append(modimpl_dict)
return jsonify(result) return result
@bp.route("/formsemestre/<int:formsemestre_id>/resultats") @bp.route("/formsemestre/<int:formsemestre_id>/resultats")
@ -464,6 +469,7 @@ def etat_evals(formsemestre_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def formsemestre_resultat(formsemestre_id: int): def formsemestre_resultat(formsemestre_id: int):
"""Tableau récapitulatif des résultats """Tableau récapitulatif des résultats
Pour chaque étudiant, son état, ses groupes, ses moyennes d'UE et de modules. 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: for row in rows:
row["partitions"] = etud_groups.get(row["etudid"], {}) row["partitions"] = etud_groups.get(row["etudid"], {})
return jsonify(rows) return rows

View File

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

View File

@ -30,11 +30,10 @@ Contrib @jmp
""" """
from datetime import datetime from datetime import datetime
from flask import jsonify, g, send_file from flask import Response, send_file
from flask_login import login_required from flask_json import as_json
from app.api import api_bp as bp, api_web_bp from app.api import api_bp as bp
from app.api import requested_format
from app.scodoc.sco_utils import json_error from app.scodoc.sco_utils import json_error
from app.models import Departement from app.models import Departement
from app.scodoc.sco_logos import list_logos, find_logo from app.scodoc.sco_logos import list_logos, find_logo
@ -47,10 +46,11 @@ from app.scodoc.sco_permissions import Permission
@bp.route("/logos") @bp.route("/logos")
@scodoc @scodoc
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
@as_json
def api_get_glob_logos(): def api_get_glob_logos():
"""Liste tous les logos""" """Liste tous les logos"""
logos = list_logos()[None] logos = list_logos()[None]
return jsonify(list(logos.keys())) return list(logos.keys())
@bp.route("/logo/<string:logoname>") @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()) logos = list_logos().get(dept_id, dict())
return jsonify(list(logos.keys())) return list(logos.keys())
@bp.route("/departement/<string:departement>/logos") @bp.route("/departement/<string:departement>/logos")
@scodoc @scodoc
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
@as_json
def api_get_local_logos_by_acronym(departement): def api_get_local_logos_by_acronym(departement):
dept_id = Departement.from_acronym(departement).id 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") @bp.route("/departement/id/<int:dept_id>/logos")
@scodoc @scodoc
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
@as_json
def api_get_local_logos_by_id(dept_id): 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) logo = find_logo(logoname=logoname, dept_id=dept_id)
if logo is None: if logo is None:
return json_error(404, message="logo not found") return json_error(404, message="logo not found")
@ -105,11 +107,11 @@ def core_get_logo(dept_id, logoname):
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
def api_get_local_logo_dept_by_acronym(departement, logoname): def api_get_local_logo_dept_by_acronym(departement, logoname):
dept_id = Departement.from_acronym(departement).id 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>") @bp.route("/departement/id/<int:dept_id>/logo/<string:logoname>")
@scodoc @scodoc
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
def api_get_local_logo_dept_by_id(dept_id, logoname): 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 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 from flask_login import login_required
import app import app
@ -31,6 +32,7 @@ from app.scodoc import sco_utils as scu
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def partition_info(partition_id: int): def partition_info(partition_id: int):
"""Info sur une partition. """Info sur une partition.
@ -55,7 +57,7 @@ def partition_info(partition_id: int):
if g.scodoc_dept: if g.scodoc_dept:
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id) query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
partition = query.first_or_404() 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") @bp.route("/formsemestre/<int:formsemestre_id>/partitions")
@ -63,6 +65,7 @@ def partition_info(partition_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def formsemestre_partitions(formsemestre_id: int): def formsemestre_partitions(formsemestre_id: int):
"""Liste de toutes les partitions d'un formsemestre """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) query = query.filter_by(dept_id=g.scodoc_dept_id)
formsemestre: FormSemestre = query.first_or_404(formsemestre_id) formsemestre: FormSemestre = query.first_or_404(formsemestre_id)
partitions = sorted(formsemestre.partitions, key=attrgetter("numero")) partitions = sorted(formsemestre.partitions, key=attrgetter("numero"))
return jsonify( return {
{ partition.id: partition.to_dict(with_groups=True)
partition.id: partition.to_dict(with_groups=True) for partition in partitions
for partition in partitions if partition.partition_name is not None
if partition.partition_name is not None }
}
)
@bp.route("/group/<int:group_id>/etudiants") @bp.route("/group/<int:group_id>/etudiants")
@ -102,6 +103,7 @@ def formsemestre_partitions(formsemestre_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def etud_in_group(group_id: int): def etud_in_group(group_id: int):
""" """
Retourne la liste des étudiants dans un groupe 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) query.join(Partition).join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
) )
group = query.first_or_404() 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") @bp.route("/group/<int:group_id>/etudiants/query")
@ -136,6 +138,7 @@ def etud_in_group(group_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def etud_in_group_query(group_id: int): def etud_in_group_query(group_id: int):
"""Étudiants du groupe, filtrés par état""" """Étudiants du groupe, filtrés par état"""
etat = request.args.get("etat") 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) 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"]) @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 @login_required
@scodoc @scodoc
@permission_required(Permission.ScoEtudChangeGroups) @permission_required(Permission.ScoEtudChangeGroups)
@as_json
def set_etud_group(etudid: int, group_id: int): def set_etud_group(etudid: int, group_id: int):
"""Affecte l'étudiant au groupe indiqué""" """Affecte l'étudiant au groupe indiqué"""
etud = Identite.query.get_or_404(etudid) 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() 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"]) @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 @login_required
@scodoc @scodoc
@permission_required(Permission.ScoEtudChangeGroups) @permission_required(Permission.ScoEtudChangeGroups)
@as_json
def group_remove_etud(group_id: int, etudid: int): def group_remove_etud(group_id: int, etudid: int):
"""Retire l'étudiant de ce groupe. S'il n'y est pas, ne fait rien.""" """Retire l'étudiant de ce groupe. S'il n'y est pas, ne fait rien."""
etud = Identite.query.get_or_404(etudid) etud = Identite.query.get_or_404(etudid)
@ -215,7 +220,7 @@ def group_remove_etud(group_id: int, etudid: int):
# Update parcours # Update parcours
group.partition.formsemestre.update_inscriptions_parcours_from_groups() group.partition.formsemestre.update_inscriptions_parcours_from_groups()
sco_cache.invalidate_formsemestre(group.partition.formsemestre_id) 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( @bp.route(
@ -227,6 +232,7 @@ def group_remove_etud(group_id: int, etudid: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoEtudChangeGroups) @permission_required(Permission.ScoEtudChangeGroups)
@as_json
def partition_remove_etud(partition_id: int, etudid: int): def partition_remove_etud(partition_id: int, etudid: int):
"""Enlève l'étudiant de tous les groupes de cette partition """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) (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() partition.formsemestre.update_inscriptions_parcours_from_groups()
app.set_sco_dept(partition.formsemestre.departement.acronym) app.set_sco_dept(partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(partition.formsemestre_id) 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"]) @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 @login_required
@scodoc @scodoc
@permission_required(Permission.ScoEtudChangeGroups) @permission_required(Permission.ScoEtudChangeGroups)
@as_json
def group_create(partition_id: int): def group_create(partition_id: int):
"""Création d'un groupe dans une partition """Création d'un groupe dans une partition
@ -294,7 +301,7 @@ def group_create(partition_id: int):
log(f"created group {group}") log(f"created group {group}")
app.set_sco_dept(partition.formsemestre.departement.acronym) app.set_sco_dept(partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(partition.formsemestre_id) 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"]) @bp.route("/group/<int:group_id>/delete", methods=["POST"])
@ -302,6 +309,7 @@ def group_create(partition_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoEtudChangeGroups) @permission_required(Permission.ScoEtudChangeGroups)
@as_json
def group_delete(group_id: int): def group_delete(group_id: int):
"""Suppression d'un groupe""" """Suppression d'un groupe"""
query = GroupDescr.query.filter_by(id=group_id) query = GroupDescr.query.filter_by(id=group_id)
@ -320,7 +328,7 @@ def group_delete(group_id: int):
db.session.commit() db.session.commit()
app.set_sco_dept(group.partition.formsemestre.departement.acronym) app.set_sco_dept(group.partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(formsemestre_id) sco_cache.invalidate_formsemestre(formsemestre_id)
return jsonify({"OK": True}) return {"OK": True}
@bp.route("/group/<int:group_id>/edit", methods=["POST"]) @bp.route("/group/<int:group_id>/edit", methods=["POST"])
@ -328,6 +336,7 @@ def group_delete(group_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoEtudChangeGroups) @permission_required(Permission.ScoEtudChangeGroups)
@as_json
def group_edit(group_id: int): def group_edit(group_id: int):
"""Edit a group""" """Edit a group"""
query = GroupDescr.query.filter_by(id=group_id) query = GroupDescr.query.filter_by(id=group_id)
@ -352,7 +361,7 @@ def group_edit(group_id: int):
log(f"modified {group}") log(f"modified {group}")
app.set_sco_dept(group.partition.formsemestre.departement.acronym) app.set_sco_dept(group.partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(group.partition.formsemestre_id) 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"]) @bp.route("/formsemestre/<int:formsemestre_id>/partition/create", methods=["POST"])
@ -362,6 +371,7 @@ def group_edit(group_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoEtudChangeGroups) @permission_required(Permission.ScoEtudChangeGroups)
@as_json
def partition_create(formsemestre_id: int): def partition_create(formsemestre_id: int):
"""Création d'une partition dans un semestre """Création d'une partition dans un semestre
@ -414,7 +424,7 @@ def partition_create(formsemestre_id: int):
log(f"created partition {partition}") log(f"created partition {partition}")
app.set_sco_dept(formsemestre.departement.acronym) app.set_sco_dept(formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(formsemestre_id) 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"]) @bp.route("/formsemestre/<int:formsemestre_id>/partitions/order", methods=["POST"])
@ -424,6 +434,7 @@ def partition_create(formsemestre_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoEtudChangeGroups) @permission_required(Permission.ScoEtudChangeGroups)
@as_json
def formsemestre_order_partitions(formsemestre_id: int): def formsemestre_order_partitions(formsemestre_id: int):
"""Modifie l'ordre des partitions du formsemestre """Modifie l'ordre des partitions du formsemestre
JSON args: [partition_id1, partition_id2, ...] JSON args: [partition_id1, partition_id2, ...]
@ -449,13 +460,11 @@ def formsemestre_order_partitions(formsemestre_id: int):
db.session.commit() db.session.commit()
app.set_sco_dept(formsemestre.departement.acronym) app.set_sco_dept(formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(formsemestre_id) sco_cache.invalidate_formsemestre(formsemestre_id)
return jsonify( return [
[ partition.to_dict()
partition.to_dict() for partition in formsemestre.partitions.order_by(Partition.numero)
for partition in formsemestre.partitions.order_by(Partition.numero) if partition.partition_name is not None
if partition.partition_name is not None ]
]
)
@bp.route("/partition/<int:partition_id>/groups/order", methods=["POST"]) @bp.route("/partition/<int:partition_id>/groups/order", methods=["POST"])
@ -463,6 +472,7 @@ def formsemestre_order_partitions(formsemestre_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoEtudChangeGroups) @permission_required(Permission.ScoEtudChangeGroups)
@as_json
def partition_order_groups(partition_id: int): def partition_order_groups(partition_id: int):
"""Modifie l'ordre des groupes de la partition """Modifie l'ordre des groupes de la partition
JSON args: [group_id1, group_id2, ...] 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) app.set_sco_dept(partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(partition.formsemestre_id) sco_cache.invalidate_formsemestre(partition.formsemestre_id)
log(f"partition_order_groups: {partition} : {group_ids}") 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"]) @bp.route("/partition/<int:partition_id>/edit", methods=["POST"])
@ -497,6 +507,7 @@ def partition_order_groups(partition_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoEtudChangeGroups) @permission_required(Permission.ScoEtudChangeGroups)
@as_json
def partition_edit(partition_id: int): def partition_edit(partition_id: int):
"""Modification d'une partition dans un semestre """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) app.set_sco_dept(partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(partition.formsemestre_id) 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"]) @bp.route("/partition/<int:partition_id>/delete", methods=["POST"])
@ -566,6 +577,7 @@ def partition_edit(partition_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoEtudChangeGroups) @permission_required(Permission.ScoEtudChangeGroups)
@as_json
def partition_delete(partition_id: int): def partition_delete(partition_id: int):
"""Suppression d'une partition (et de tous ses groupes). """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) sco_cache.invalidate_formsemestre(formsemestre.id)
if is_parcours: if is_parcours:
formsemestre.update_inscriptions_parcours_from_groups() 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 import db, log
from app.api import api_bp as bp from app.api import api_bp as bp
from app.auth.logic import basic_auth, token_auth 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"]) @bp.route("/tokens", methods=["POST"])
@basic_auth.login_required @basic_auth.login_required
@as_json
def get_token(): def get_token():
"renvoie un jeton jwt pour l'utilisateur courant" "renvoie un jeton jwt pour l'utilisateur courant"
token = basic_auth.current_user().get_token() token = basic_auth.current_user().get_token()
log(f"API: giving token to {basic_auth.current_user()}") log(f"API: giving token to {basic_auth.current_user()}")
db.session.commit() db.session.commit()
return jsonify({"token": token}) return {"token": token}
@bp.route("/tokens", methods=["DELETE"]) @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 flask_login import current_user, login_required
from app import db from app import db
@ -29,6 +30,7 @@ from app.scodoc import sco_utils as scu
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoUsersView) @permission_required(Permission.ScoUsersView)
@as_json
def user_info(uid: int): def user_info(uid: int):
""" """
Info sur un compte utilisateur scodoc 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): if (None not in allowed_depts) and (user.dept not in allowed_depts):
return json_error(404, "user not found") return json_error(404, "user not found")
return jsonify(user.to_dict()) return user.to_dict()
@bp.route("/users/query") @bp.route("/users/query")
@ -49,6 +51,7 @@ def user_info(uid: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def users_info_query(): def users_info_query():
"""Utilisateurs, filtrés par dept, active ou début nom """Utilisateurs, filtrés par dept, active ou début nom
/users/query?departement=dept_acronym&active=1&starts_with=<string: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) 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"]) @bp.route("/user/create", methods=["POST"])
@ -87,6 +90,7 @@ def users_info_query():
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoUsersAdmin) @permission_required(Permission.ScoUsersAdmin)
@as_json
def user_create(): def user_create():
"""Création d'un utilisateur """Création d'un utilisateur
The request content type should be "application/json": 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) user = User(user_name=user_name, active=active, dept=dept, nom=nom, prenom=prenom)
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
return jsonify(user.to_dict()) return user.to_dict()
@bp.route("/user/<int:uid>/edit", methods=["POST"]) @bp.route("/user/<int:uid>/edit", methods=["POST"])
@ -129,6 +133,7 @@ def user_create():
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoUsersAdmin) @permission_required(Permission.ScoUsersAdmin)
@as_json
def user_edit(uid: int): def user_edit(uid: int):
"""Modification d'un utilisateur """Modification d'un utilisateur
Champs modifiables: Champs modifiables:
@ -165,7 +170,7 @@ def user_edit(uid: int):
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
return jsonify(user.to_dict()) return user.to_dict()
@bp.route("/user/<int:uid>/password", methods=["POST"]) @bp.route("/user/<int:uid>/password", methods=["POST"])
@ -173,6 +178,7 @@ def user_edit(uid: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoUsersAdmin) @permission_required(Permission.ScoUsersAdmin)
@as_json
def user_password(uid: int): def user_password(uid: int):
"""Modification du mot de passe d'un utilisateur """Modification du mot de passe d'un utilisateur
Champs modifiables: Champs modifiables:
@ -194,7 +200,7 @@ def user_password(uid: int):
user.set_password(password) user.set_password(password)
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
return jsonify(user.to_dict()) return user.to_dict()
@bp.route("/user/<int:uid>/role/<string:role_name>/add", methods=["POST"]) @bp.route("/user/<int:uid>/role/<string:role_name>/add", methods=["POST"])
@ -210,6 +216,7 @@ def user_password(uid: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
@as_json
def user_role_add(uid: int, role_name: str, dept: str = None): def user_role_add(uid: int, role_name: str, dept: str = None):
"""Add a role to the user""" """Add a role to the user"""
user: User = User.query.get_or_404(uid) 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) user.add_role(role, dept)
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
return jsonify(user.to_dict()) return user.to_dict()
@bp.route("/user/<int:uid>/role/<string:role_name>/remove", methods=["POST"]) @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 @login_required
@scodoc @scodoc
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
@as_json
def user_role_remove(uid: int, role_name: str, dept: str = None): def user_role_remove(uid: int, role_name: str, dept: str = None):
"""Remove the role from the user""" """Remove the role from the user"""
user: User = User.query.get_or_404(uid) 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.delete(user_role)
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
return jsonify(user.to_dict()) return user.to_dict()
@bp.route("/permissions") @bp.route("/permissions")
@ -264,9 +272,10 @@ def user_role_remove(uid: int, role_name: str, dept: str = None):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoUsersView) @permission_required(Permission.ScoUsersView)
@as_json
def list_permissions(): def list_permissions():
"""Liste des noms de permissions définies""" """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>") @bp.route("/role/<string:role_name>")
@ -274,9 +283,10 @@ def list_permissions():
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoUsersView) @permission_required(Permission.ScoUsersView)
@as_json
def list_role(role_name: str): def list_role(role_name: str):
"""Un rôle""" """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") @bp.route("/roles")
@ -284,9 +294,10 @@ def list_role(role_name: str):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoUsersView) @permission_required(Permission.ScoUsersView)
@as_json
def list_roles(): def list_roles():
"""Tous les rôles définis""" """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( @bp.route(
@ -300,6 +311,7 @@ def list_roles():
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
@as_json
def role_permission_add(role_name: str, perm_name: str): def role_permission_add(role_name: str, perm_name: str):
"""Add permission to role""" """Add permission to role"""
role: Role = Role.query.filter_by(name=role_name).first_or_404() 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) role.add_permission(permission)
db.session.add(role) db.session.add(role)
db.session.commit() db.session.commit()
return jsonify(role.to_dict()) return role.to_dict()
@bp.route( @bp.route(
@ -323,6 +335,7 @@ def role_permission_add(role_name: str, perm_name: str):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
@as_json
def role_permission_remove(role_name: str, perm_name: str): def role_permission_remove(role_name: str, perm_name: str):
"""Remove permission from role""" """Remove permission from role"""
role: Role = Role.query.filter_by(name=role_name).first_or_404() 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) role.remove_permission(permission)
db.session.add(role) db.session.add(role)
db.session.commit() db.session.commit()
return jsonify(role.to_dict()) return role.to_dict()
@bp.route("/role/create/<string:role_name>", methods=["POST"]) @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 @login_required
@scodoc @scodoc
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
@as_json
def role_create(role_name: str): def role_create(role_name: str):
"""Create a new role with permissions. """Create a new role with permissions.
{ {
@ -359,7 +373,7 @@ def role_create(role_name: str):
return json_error(404, "role_create: invalid permissions") return json_error(404, "role_create: invalid permissions")
db.session.add(role) db.session.add(role)
db.session.commit() db.session.commit()
return jsonify(role.to_dict()) return role.to_dict()
@bp.route("/role/<string:role_name>/edit", methods=["POST"]) @bp.route("/role/<string:role_name>/edit", methods=["POST"])
@ -367,6 +381,7 @@ def role_create(role_name: str):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
@as_json
def role_edit(role_name: str): def role_edit(role_name: str):
"""Edit a role. On peut spécifier un nom et/ou des permissions. """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 role.name = role_name
db.session.add(role) db.session.add(role)
db.session.commit() db.session.commit()
return jsonify(role.to_dict()) return role.to_dict()
@bp.route("/role/<string:role_name>/delete", methods=["POST"]) @bp.route("/role/<string:role_name>/delete", methods=["POST"])
@ -398,9 +413,10 @@ def role_edit(role_name: str):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoSuperAdmin) @permission_required(Permission.ScoSuperAdmin)
@as_json
def role_delete(role_name: str): def role_delete(role_name: str):
"""Delete a role""" """Delete a role"""
role: Role = Role.query.filter_by(name=role_name).first_or_404() role: Role = Role.query.filter_by(name=role_name).first_or_404()
db.session.delete(role) db.session.delete(role)
db.session.commit() db.session.commit()
return jsonify({"OK": True}) return {"OK": True}

View File

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

View File

@ -85,6 +85,7 @@ class ApcReferentielCompetences(db.Model, XMLModel):
backref="referentiel", backref="referentiel",
lazy="dynamic", lazy="dynamic",
cascade="all, delete-orphan", cascade="all, delete-orphan",
order_by="ApcParcours.numero, ApcParcours.code",
) )
formations = db.relationship( formations = db.relationship(
"Formation", "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 NO_SEMESTRE_ID = -1 # code semestre si pas de semestres
# Règles gestion cursus # Règles gestion cursus
class DUTRule(object): class DUTRule(object):
def __init__(self, rule_id, premise, conclusion): def __init__(self, rule_id, premise, conclusion):
@ -298,7 +299,7 @@ class DUTRule(object):
# Types de cursus # 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: class TypeCursus:

View File

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

View File

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

View File

@ -33,7 +33,7 @@ from app.scodoc.sco_exceptions import ScoValueError
class ApoEtapeVDI(object): class ApoEtapeVDI(object):
_ETAPE_VDI_SEP = "!" _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'""" """Build from string representation, e.g. 'V1RT!111'"""
if etape_vdi: if etape_vdi:
self.etape_vdi = etape_vdi self.etape_vdi = etape_vdi
@ -52,6 +52,10 @@ class ApoEtapeVDI(object):
def __str__(self): def __str__(self):
return self.etape_vdi return self.etape_vdi
def __json__(self) -> str:
"json repr for flask_json"
return str(self)
def _cmp(self, other): def _cmp(self, other):
"""Test égalité de deux codes étapes. """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. 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="{{ <li><a class="stdlink" href="{{
url_for('notes.ue_table', scodoc_dept=g.scodoc_dept, formation_id=formation.id ) url_for('notes.ue_table', scodoc_dept=g.scodoc_dept, formation_id=formation.id )
}}">{{ formation.get_titre_version() }}</a></li> }}">{{ formation.get_titre_version() }}</a></li>
{% else %}
<li><em>aucune</em></li>
{% endfor %} {% endfor %}
</ul> </ul>
</li> </li>

View File

@ -32,10 +32,10 @@ Emmanuel Viennet, 2021
""" """
from flask import url_for from flask import url_for
from flask import jsonify
from flask import g, request 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_login import current_user
from flask.templating import render_template
from app.scodoc.codes_cursus import UE_SPORT from app.scodoc.codes_cursus import UE_SPORT
@ -58,6 +58,7 @@ from app.scodoc.sco_permissions import Permission
) )
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def table_modules_ue_coefs(formation_id, semestre_idx=None, parcours_id: int = None): 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 """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 # Les champs de saisie
cells = [] 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 style = "champs champs_" + scu.ModuleType(mod.module_type).name
mod_parcours_ids = {p.id for p in mod.parcours} 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 # met en gris les coefs qui devraient être nuls
# car le module n'est pas dans le parcours de l'UE: # car le module n'est pas dans le parcours de l'UE:
if ( if (
@ -136,7 +137,7 @@ def table_modules_ue_coefs(formation_id, semestre_idx=None, parcours_id: int = N
"ue_id": ue.id, "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"]) @bp.route("/set_module_ue_coef", methods=["POST"])

View File

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

View File

@ -35,8 +35,9 @@ import requests
import time import time
import flask 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 import g, request
from flask_json import as_json
from flask_login import current_user from flask_login import current_user
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileAllowed from flask_wtf.file import FileField, FileAllowed
@ -501,10 +502,11 @@ sco_publish(
@bp.route("/Notes/search_etud_by_name") # for JS apis @bp.route("/Notes/search_etud_by_name") # for JS apis
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@as_json
def search_etud_by_name(): def search_etud_by_name():
term = request.args["term"] term = request.args["term"]
data = sco_find_etud.search_etud_by_name(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 # 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") 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) 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): class ProdConfig(Config):

View File

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

View File

@ -17,7 +17,6 @@ import os
import requests import requests
from dotenv import load_dotenv from dotenv import load_dotenv
import pytest import pytest
from app.scodoc import sco_utils as scu
# --- Lecture configuration (variables d'env ou .env) # --- Lecture configuration (variables d'env ou .env)
try: 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_USER_ADMIN = os.environ.get("API_USER_ADMIN", "admin_api")
API_PASSWORD_ADMIN = os.environ.get("API_PASSWD_ADMIN", "admin_api") API_PASSWORD_ADMIN = os.environ.get("API_PASSWD_ADMIN", "admin_api")
DEPT_ACRONYM = "TAPI" DEPT_ACRONYM = "TAPI"
SCO_TEST_API_TIMEOUT = 5
print(f"SCODOC_URL={SCODOC_URL}") print(f"SCODOC_URL={SCODOC_URL}")
print(f"API URL={API_URL}") print(f"API URL={API_URL}")
@ -78,7 +78,7 @@ def GET(path: str, headers: dict = None, errmsg=None, dept=None):
url, url,
headers=headers or {}, headers=headers or {},
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
timeout=scu.SCO_TEST_API_TIMEOUT, timeout=SCO_TEST_API_TIMEOUT,
) )
if reply.status_code != 200: if reply.status_code != 200:
raise APIError( raise APIError(
@ -111,7 +111,7 @@ def POST_JSON(path: str, data: dict = {}, headers: dict = None, errmsg=None, dep
json=data, json=data,
headers=headers or {}, headers=headers or {},
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
timeout=10, timeout=SCO_TEST_API_TIMEOUT,
) )
if r.status_code != 200: if r.status_code != 200:
raise APIError(errmsg or f"erreur status={r.status_code} !", r.json()) 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 assert r.status_code == 401
# Demande un jeton pour "other" # 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 assert r.status_code == 200
token = r.json()["token"] token = r.json()["token"]
headers = {"Authorization": f"Bearer {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 random.seed(12345678) # tests reproductibles
# La formation à utiliser: # 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 = ( REFCOMP_FILENAME = (
"ressources/referentiels/but2022/competences/but-RT-05012022-081735.xml" "ressources/referentiels/but2022/competences/but-RT-05012022-081735.xml"
) )