forked from ScoDoc/ScoDoc
Merge branch 'master' into feuille_jury
This commit is contained in:
commit
d31d24388f
@ -8,16 +8,17 @@
|
|||||||
API : accès aux étudiants
|
API : accès aux étudiants
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from operator import attrgetter
|
||||||
|
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask_json import as_json
|
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, func, or_
|
||||||
|
from sqlalchemy.dialects.postgresql import VARCHAR
|
||||||
|
|
||||||
import app
|
import app
|
||||||
from app.api import api_bp as bp, api_web_bp
|
from app.api import api_bp as bp, api_web_bp
|
||||||
from app.scodoc.sco_utils import json_error
|
|
||||||
from app.api import tools
|
from app.api import tools
|
||||||
from app.decorators import scodoc, permission_required
|
from app.decorators import scodoc, permission_required
|
||||||
from app.models import (
|
from app.models import (
|
||||||
@ -31,6 +32,8 @@ from app.scodoc import sco_bulletins
|
|||||||
from app.scodoc import sco_groups
|
from app.scodoc import sco_groups
|
||||||
from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud
|
from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
|
from app.scodoc.sco_utils import json_error, suppress_accents
|
||||||
|
|
||||||
|
|
||||||
# Un exemple:
|
# Un exemple:
|
||||||
# @bp.route("/api_function/<int:arg>")
|
# @bp.route("/api_function/<int:arg>")
|
||||||
@ -164,12 +167,39 @@ def etudiants(etudid: int = None, nip: str = None, ine: str = None):
|
|||||||
)
|
)
|
||||||
if not None in allowed_depts:
|
if not None in allowed_depts:
|
||||||
# restreint aux départements autorisés:
|
# restreint aux départements autorisés:
|
||||||
etuds = etuds.join(Departement).filter(
|
query = query.join(Departement).filter(
|
||||||
or_(Departement.acronym == acronym for acronym in allowed_depts)
|
or_(Departement.acronym == acronym for acronym in allowed_depts)
|
||||||
)
|
)
|
||||||
return [etud.to_dict_api() for etud in query]
|
return [etud.to_dict_api() for etud in query]
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/etudiants/name/<string:start>")
|
||||||
|
@api_web_bp.route("/etudiants/name/<string:start>")
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.ScoView)
|
||||||
|
@as_json
|
||||||
|
def etudiants_by_name(start: str = "", min_len=3, limit=32):
|
||||||
|
"""Liste des étudiants dont le nom débute par start.
|
||||||
|
Si start fait moins de min_len=3 caractères, liste vide.
|
||||||
|
La casse et les accents sont ignorés.
|
||||||
|
"""
|
||||||
|
if len(start) < min_len:
|
||||||
|
return []
|
||||||
|
start = suppress_accents(start).lower()
|
||||||
|
query = Identite.query.filter(
|
||||||
|
func.lower(func.unaccent(Identite.nom, type_=VARCHAR)).ilike(start + "%")
|
||||||
|
)
|
||||||
|
allowed_depts = current_user.get_depts_with_permission(Permission.ScoView)
|
||||||
|
if not None in allowed_depts:
|
||||||
|
# restreint aux départements autorisés:
|
||||||
|
query = query.join(Departement).filter(
|
||||||
|
or_(Departement.acronym == acronym for acronym in allowed_depts)
|
||||||
|
)
|
||||||
|
etuds = query.order_by(Identite.nom, Identite.prenom).limit(limit)
|
||||||
|
# Note: on raffine le tri pour les caractères spéciaux et nom usuel ici:
|
||||||
|
return [etud.to_dict_api() for etud in sorted(etuds, key=attrgetter("sort_key"))]
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/etudiant/etudid/<int:etudid>/formsemestres")
|
@bp.route("/etudiant/etudid/<int:etudid>/formsemestres")
|
||||||
@bp.route("/etudiant/nip/<string:nip>/formsemestres")
|
@bp.route("/etudiant/nip/<string:nip>/formsemestres")
|
||||||
@bp.route("/etudiant/ine/<string:ine>/formsemestres")
|
@bp.route("/etudiant/ine/<string:ine>/formsemestres")
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
ScoDoc 9 API : accès aux évaluations
|
ScoDoc 9 API : accès aux évaluations
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask import g
|
from flask import g, request
|
||||||
from flask_json import as_json
|
from flask_json import as_json
|
||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ import app
|
|||||||
from app.api import api_bp as bp, api_web_bp
|
from app.api import api_bp as bp, api_web_bp
|
||||||
from app.decorators import scodoc, permission_required
|
from app.decorators import scodoc, permission_required
|
||||||
from app.models import Evaluation, ModuleImpl, FormSemestre
|
from app.models import Evaluation, ModuleImpl, FormSemestre
|
||||||
from app.scodoc import sco_evaluation_db
|
from app.scodoc import sco_evaluation_db, sco_saisie_notes
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ import app.scodoc.sco_utils as scu
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@as_json
|
@as_json
|
||||||
def the_eval(evaluation_id: int):
|
def evaluation(evaluation_id: int):
|
||||||
"""Description d'une évaluation.
|
"""Description d'une évaluation.
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -93,24 +93,22 @@ def evaluations(moduleimpl_id: int):
|
|||||||
@as_json
|
@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 de l'évaluation
|
||||||
|
|
||||||
evaluation_id : l'id d'une évaluation
|
evaluation_id : l'id de l'évaluation
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
{
|
{
|
||||||
"1": {
|
"11": {
|
||||||
"id": 1,
|
"etudid": 11,
|
||||||
"etudid": 10,
|
|
||||||
"evaluation_id": 1,
|
"evaluation_id": 1,
|
||||||
"value": 15.0,
|
"value": 15.0,
|
||||||
"comment": "",
|
"comment": "",
|
||||||
"date": "Wed, 20 Apr 2022 06:49:05 GMT",
|
"date": "Wed, 20 Apr 2022 06:49:05 GMT",
|
||||||
"uid": 2
|
"uid": 2
|
||||||
},
|
},
|
||||||
"2": {
|
"12": {
|
||||||
"id": 2,
|
"etudid": 12,
|
||||||
"etudid": 1,
|
|
||||||
"evaluation_id": 1,
|
"evaluation_id": 1,
|
||||||
"value": 12.0,
|
"value": 12.0,
|
||||||
"comment": "",
|
"comment": "",
|
||||||
@ -128,8 +126,8 @@ def evaluation_notes(evaluation_id: int):
|
|||||||
.filter_by(dept_id=g.scodoc_dept_id)
|
.filter_by(dept_id=g.scodoc_dept_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
the_eval = query.first_or_404()
|
evaluation = query.first_or_404()
|
||||||
dept = the_eval.moduleimpl.formsemestre.departement
|
dept = evaluation.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)
|
||||||
@ -137,7 +135,49 @@ 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"] = the_eval.note_max
|
note["note_max"] = evaluation.note_max
|
||||||
del note["id"]
|
del note["id"]
|
||||||
|
|
||||||
return notes
|
# in JS, keys must be string, not integers
|
||||||
|
return {str(etudid): note for etudid, note in notes.items()}
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/evaluation/<int:evaluation_id>/notes/set", methods=["POST"])
|
||||||
|
@api_web_bp.route("/evaluation/<int:evaluation_id>/notes/set", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.ScoEnsView)
|
||||||
|
@as_json
|
||||||
|
def evaluation_set_notes(evaluation_id: int):
|
||||||
|
"""Écriture de notes dans une évaluation.
|
||||||
|
The request content type should be "application/json",
|
||||||
|
and contains:
|
||||||
|
{
|
||||||
|
'notes' : [ [etudid, value], ... ],
|
||||||
|
'comment' : optional string
|
||||||
|
}
|
||||||
|
Result:
|
||||||
|
- nb_changed: nombre de notes changées
|
||||||
|
- nb_suppress: nombre de notes effacées
|
||||||
|
- etudids_with_decision: liste des etudiants dont la note a changé
|
||||||
|
alors qu'ils ont une décision de jury enregistrée.
|
||||||
|
"""
|
||||||
|
query = Evaluation.query.filter_by(id=evaluation_id)
|
||||||
|
if g.scodoc_dept:
|
||||||
|
query = (
|
||||||
|
query.join(ModuleImpl)
|
||||||
|
.join(FormSemestre)
|
||||||
|
.filter_by(dept_id=g.scodoc_dept_id)
|
||||||
|
)
|
||||||
|
evaluation = query.first_or_404()
|
||||||
|
dept = evaluation.moduleimpl.formsemestre.departement
|
||||||
|
app.set_sco_dept(dept.acronym)
|
||||||
|
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||||
|
notes = data.get("notes")
|
||||||
|
if notes is None:
|
||||||
|
return scu.json_error(404, "no notes")
|
||||||
|
if not isinstance(notes, list):
|
||||||
|
return scu.json_error(404, "invalid notes argument (must be a list)")
|
||||||
|
return sco_saisie_notes.save_notes(
|
||||||
|
evaluation, notes, comment=data.get("comment", "")
|
||||||
|
)
|
||||||
|
@ -18,7 +18,7 @@ import pandas as pd
|
|||||||
|
|
||||||
from flask import g
|
from flask import g
|
||||||
|
|
||||||
from app.scodoc.codes_cursus import UE_SPORT
|
from app.scodoc.codes_cursus import UE_SPORT, UE_STANDARD
|
||||||
from app.scodoc.codes_cursus import CursusDUT, CursusDUTMono
|
from app.scodoc.codes_cursus import CursusDUT, CursusDUTMono
|
||||||
from app.scodoc.sco_utils import ModuleType
|
from app.scodoc.sco_utils import ModuleType
|
||||||
|
|
||||||
@ -740,6 +740,7 @@ class BonusGrenobleIUT1(BonusSportMultiplicatif):
|
|||||||
|
|
||||||
name = "bonus_iut1grenoble_2017"
|
name = "bonus_iut1grenoble_2017"
|
||||||
displayed_name = "IUT de Grenoble 1"
|
displayed_name = "IUT de Grenoble 1"
|
||||||
|
|
||||||
# C'est un bonus "multiplicatif": on l'exprime en additif,
|
# C'est un bonus "multiplicatif": on l'exprime en additif,
|
||||||
# sur chaque moyenne d'UE m_0
|
# sur chaque moyenne d'UE m_0
|
||||||
# Augmenter de 5% correspond à multiplier par a=1.05
|
# Augmenter de 5% correspond à multiplier par a=1.05
|
||||||
@ -782,6 +783,7 @@ class BonusIUTRennes1(BonusSportAdditif):
|
|||||||
seuil_moy_gen = 10.0
|
seuil_moy_gen = 10.0
|
||||||
proportion_point = 1 / 20.0
|
proportion_point = 1 / 20.0
|
||||||
classic_use_bonus_ues = False
|
classic_use_bonus_ues = False
|
||||||
|
|
||||||
# S'applique aussi en classic, sur la moy. gen.
|
# S'applique aussi en classic, sur la moy. gen.
|
||||||
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
||||||
"""calcul du bonus"""
|
"""calcul du bonus"""
|
||||||
@ -1336,6 +1338,7 @@ class BonusStNazaire(BonusSport):
|
|||||||
classic_use_bonus_ues = True # s'applique aux UEs en DUT et LP
|
classic_use_bonus_ues = True # s'applique aux UEs en DUT et LP
|
||||||
amplitude = 0.01 / 4 # 4pt => 1%
|
amplitude = 0.01 / 4 # 4pt => 1%
|
||||||
factor_max = 0.1 # 10% max
|
factor_max = 0.1 # 10% max
|
||||||
|
|
||||||
# Modifié 2022-11-29: calculer chaque bonus
|
# Modifié 2022-11-29: calculer chaque bonus
|
||||||
# (de 1 à 3 modules) séparément.
|
# (de 1 à 3 modules) séparément.
|
||||||
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
||||||
@ -1533,6 +1536,63 @@ class BonusIUTV(BonusSportAdditif):
|
|||||||
# c'est le bonus par défaut: aucune méthode à surcharger
|
# c'est le bonus par défaut: aucune méthode à surcharger
|
||||||
|
|
||||||
|
|
||||||
|
# Finalement inutile: un bonus direct est mieux adapté à leurs besoins.
|
||||||
|
# # class BonusMastersUSPNIG(BonusSportAdditif):
|
||||||
|
# """Calcul bonus modules optionnels (sport, culture), règle Masters de l'Institut Galilée (USPN)
|
||||||
|
|
||||||
|
# Les étudiants peuvent suivre des enseignements optionnels
|
||||||
|
# de l'USPN (sports, musique, deuxième langue, culture, etc) dans une
|
||||||
|
# UE libre. Les points au-dessus de 10 sur 20 obtenus dans cette UE
|
||||||
|
# libre sont ajoutés au total des points obtenus pour les UE obligatoires
|
||||||
|
# du semestre concerné.
|
||||||
|
# """
|
||||||
|
|
||||||
|
# name = "bonus_masters__uspn_ig"
|
||||||
|
# displayed_name = "Masters de l'Institut Galilée (USPN)"
|
||||||
|
# proportion_point = 1.0
|
||||||
|
# seuil_moy_gen = 10.0
|
||||||
|
|
||||||
|
# def __init__(
|
||||||
|
# self,
|
||||||
|
# formsemestre: "FormSemestre",
|
||||||
|
# sem_modimpl_moys: np.array,
|
||||||
|
# ues: list,
|
||||||
|
# modimpl_inscr_df: pd.DataFrame,
|
||||||
|
# modimpl_coefs: np.array,
|
||||||
|
# etud_moy_gen,
|
||||||
|
# etud_moy_ue,
|
||||||
|
# ):
|
||||||
|
# # Pour ce bonus, il nous faut la somme des coefs des modules non bonus
|
||||||
|
# # du formsemestre (et non auxquels les étudiants sont inscrits !)
|
||||||
|
# self.sum_coefs = sum(
|
||||||
|
# [
|
||||||
|
# m.module.coefficient
|
||||||
|
# for m in formsemestre.modimpls_sorted
|
||||||
|
# if (m.module.module_type == ModuleType.STANDARD)
|
||||||
|
# and (m.module.ue.type == UE_STANDARD)
|
||||||
|
# ]
|
||||||
|
# )
|
||||||
|
# super().__init__(
|
||||||
|
# formsemestre,
|
||||||
|
# sem_modimpl_moys,
|
||||||
|
# ues,
|
||||||
|
# modimpl_inscr_df,
|
||||||
|
# modimpl_coefs,
|
||||||
|
# etud_moy_gen,
|
||||||
|
# etud_moy_ue,
|
||||||
|
# )
|
||||||
|
# # Bonus sur la moyenne générale seulement
|
||||||
|
# # On a dans bonus_moy_arr le bonus additif classique
|
||||||
|
# # Sa valeur sera appliquée comme moy_gen += bonus_moy_gen
|
||||||
|
# # or ici on veut
|
||||||
|
# # moy_gen = (somme des notes + bonus_moy_arr) / somme des coefs
|
||||||
|
# # moy_gen += bonus_moy_arr / somme des coefs
|
||||||
|
|
||||||
|
# self.bonus_moy_gen = (
|
||||||
|
# None if self.bonus_moy_gen is None else self.bonus_moy_gen / self.sum_coefs
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
def get_bonus_class_dict(start=BonusSport, d=None):
|
def get_bonus_class_dict(start=BonusSport, d=None):
|
||||||
"""Dictionnaire des classes de bonus
|
"""Dictionnaire des classes de bonus
|
||||||
(liste les sous-classes de BonusSport ayant un nom)
|
(liste les sous-classes de BonusSport ayant un nom)
|
||||||
|
@ -288,7 +288,7 @@ class ResultatsSemestreBUT(NotesTableCompat):
|
|||||||
if ref_comp is None:
|
if ref_comp is None:
|
||||||
return set()
|
return set()
|
||||||
if parcour_id is None:
|
if parcour_id is None:
|
||||||
ues_ids = {ue.id for ue in self.ues}
|
ues_ids = {ue.id for ue in self.ues if ue.type != UE_SPORT}
|
||||||
else:
|
else:
|
||||||
parcour: ApcParcours = ApcParcours.query.get(parcour_id)
|
parcour: ApcParcours = ApcParcours.query.get(parcour_id)
|
||||||
annee = (self.formsemestre.semestre_id + 1) // 2
|
annee = (self.formsemestre.semestre_id + 1) // 2
|
||||||
@ -306,12 +306,12 @@ class ResultatsSemestreBUT(NotesTableCompat):
|
|||||||
|
|
||||||
return ues_ids
|
return ues_ids
|
||||||
|
|
||||||
def etud_has_decision(self, etudid):
|
def etud_has_decision(self, etudid) -> bool:
|
||||||
"""True s'il y a une décision de jury pour cet étudiant émanant de ce formsemestre.
|
"""True s'il y a une décision de jury pour cet étudiant émanant de ce formsemestre.
|
||||||
prend aussi en compte les autorisations de passage.
|
prend aussi en compte les autorisations de passage.
|
||||||
Sous-classée en BUT pour les RCUEs et années.
|
Sous-classée en BUT pour les RCUEs et années.
|
||||||
"""
|
"""
|
||||||
return (
|
return bool(
|
||||||
super().etud_has_decision(etudid)
|
super().etud_has_decision(etudid)
|
||||||
or ApcValidationAnnee.query.filter_by(
|
or ApcValidationAnnee.query.filter_by(
|
||||||
formsemestre_id=self.formsemestre.id, etudid=etudid
|
formsemestre_id=self.formsemestre.id, etudid=etudid
|
||||||
|
@ -283,12 +283,12 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
]
|
]
|
||||||
return etudids
|
return etudids
|
||||||
|
|
||||||
def etud_has_decision(self, etudid):
|
def etud_has_decision(self, etudid) -> bool:
|
||||||
"""True s'il y a une décision de jury pour cet étudiant émanant de ce formsemestre.
|
"""True s'il y a une décision de jury pour cet étudiant émanant de ce formsemestre.
|
||||||
prend aussi en compte les autorisations de passage.
|
prend aussi en compte les autorisations de passage.
|
||||||
Sous-classée en BUT pour les RCUEs et années.
|
Sous-classée en BUT pour les RCUEs et années.
|
||||||
"""
|
"""
|
||||||
return (
|
return bool(
|
||||||
self.get_etud_decisions_ue(etudid)
|
self.get_etud_decisions_ue(etudid)
|
||||||
or self.get_etud_decision_sem(etudid)
|
or self.get_etud_decision_sem(etudid)
|
||||||
or ScolarAutorisationInscription.query.filter_by(
|
or ScolarAutorisationInscription.query.filter_by(
|
||||||
|
@ -36,6 +36,7 @@ from sqlalchemy import text
|
|||||||
from wtforms import (
|
from wtforms import (
|
||||||
BooleanField,
|
BooleanField,
|
||||||
DateField,
|
DateField,
|
||||||
|
DecimalField,
|
||||||
FieldList,
|
FieldList,
|
||||||
FormField,
|
FormField,
|
||||||
HiddenField,
|
HiddenField,
|
||||||
@ -122,13 +123,13 @@ class EntrepriseCreationForm(FlaskForm):
|
|||||||
origine = _build_string_field("Origine du correspondant", required=False)
|
origine = _build_string_field("Origine du correspondant", required=False)
|
||||||
notes = _build_string_field("Notes sur le correspondant", required=False)
|
notes = _build_string_field("Notes sur le correspondant", required=False)
|
||||||
|
|
||||||
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
|
submit = SubmitField("Enregistrer", render_kw=SUBMIT_MARGE)
|
||||||
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self, extra_validators=None):
|
||||||
validate = True
|
validate = True
|
||||||
if not FlaskForm.validate(self):
|
if not super().validate(extra_validators):
|
||||||
validate = False
|
return False
|
||||||
|
|
||||||
if EntreprisePreferences.get_check_siret() and self.siret.data != "":
|
if EntreprisePreferences.get_check_siret() and self.siret.data != "":
|
||||||
siret_data = self.siret.data.strip().replace(" ", "")
|
siret_data = self.siret.data.strip().replace(" ", "")
|
||||||
@ -248,13 +249,13 @@ class SiteCreationForm(FlaskForm):
|
|||||||
codepostal = _build_string_field("Code postal (*)")
|
codepostal = _build_string_field("Code postal (*)")
|
||||||
ville = _build_string_field("Ville (*)")
|
ville = _build_string_field("Ville (*)")
|
||||||
pays = _build_string_field("Pays", required=False)
|
pays = _build_string_field("Pays", required=False)
|
||||||
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
|
submit = SubmitField("Enregistrer", render_kw=SUBMIT_MARGE)
|
||||||
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self, extra_validators=None):
|
||||||
validate = True
|
validate = True
|
||||||
if not FlaskForm.validate(self):
|
if not super().validate(extra_validators):
|
||||||
validate = False
|
return False
|
||||||
|
|
||||||
site = EntrepriseSite.query.filter_by(
|
site = EntrepriseSite.query.filter_by(
|
||||||
entreprise_id=self.hidden_entreprise_id.data, nom=self.nom.data
|
entreprise_id=self.hidden_entreprise_id.data, nom=self.nom.data
|
||||||
@ -278,10 +279,10 @@ class SiteModificationForm(FlaskForm):
|
|||||||
submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE)
|
submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE)
|
||||||
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self, extra_validators=None):
|
||||||
validate = True
|
validate = True
|
||||||
if not FlaskForm.validate(self):
|
if not super().validate(extra_validators):
|
||||||
validate = False
|
return False
|
||||||
|
|
||||||
site = EntrepriseSite.query.filter(
|
site = EntrepriseSite.query.filter(
|
||||||
EntrepriseSite.entreprise_id == self.hidden_entreprise_id.data,
|
EntrepriseSite.entreprise_id == self.hidden_entreprise_id.data,
|
||||||
@ -326,7 +327,7 @@ class OffreCreationForm(FlaskForm):
|
|||||||
FileAllowed(["pdf", "docx"], "Fichier .pdf ou .docx uniquement"),
|
FileAllowed(["pdf", "docx"], "Fichier .pdf ou .docx uniquement"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
|
submit = SubmitField("Enregistrer", render_kw=SUBMIT_MARGE)
|
||||||
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -344,10 +345,10 @@ class OffreCreationForm(FlaskForm):
|
|||||||
(dept.id, dept.acronym) for dept in Departement.query.all()
|
(dept.id, dept.acronym) for dept in Departement.query.all()
|
||||||
]
|
]
|
||||||
|
|
||||||
def validate(self):
|
def validate(self, extra_validators=None):
|
||||||
validate = True
|
validate = True
|
||||||
if not FlaskForm.validate(self):
|
if not super().validate(extra_validators):
|
||||||
validate = False
|
return False
|
||||||
|
|
||||||
if len(self.depts.data) < 1:
|
if len(self.depts.data) < 1:
|
||||||
self.depts.errors.append("Choisir au moins un département")
|
self.depts.errors.append("Choisir au moins un département")
|
||||||
@ -392,10 +393,10 @@ class OffreModificationForm(FlaskForm):
|
|||||||
(dept.id, dept.acronym) for dept in Departement.query.all()
|
(dept.id, dept.acronym) for dept in Departement.query.all()
|
||||||
]
|
]
|
||||||
|
|
||||||
def validate(self):
|
def validate(self, extra_validators=None):
|
||||||
validate = True
|
validate = True
|
||||||
if not FlaskForm.validate(self):
|
if not super().validate(extra_validators):
|
||||||
validate = False
|
return False
|
||||||
|
|
||||||
if len(self.depts.data) < 1:
|
if len(self.depts.data) < 1:
|
||||||
self.depts.errors.append("Choisir au moins un département")
|
self.depts.errors.append("Choisir au moins un département")
|
||||||
@ -442,10 +443,10 @@ class CorrespondantCreationForm(FlaskForm):
|
|||||||
"Notes", required=False, render_kw={"class": "form-control"}
|
"Notes", required=False, render_kw={"class": "form-control"}
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self, extra_validators=None):
|
||||||
validate = True
|
validate = True
|
||||||
if not FlaskForm.validate(self):
|
if not super().validate(extra_validators):
|
||||||
validate = False
|
return False
|
||||||
|
|
||||||
if not self.telephone.data and not self.mail.data:
|
if not self.telephone.data and not self.mail.data:
|
||||||
msg = "Saisir un moyen de contact (mail ou téléphone)"
|
msg = "Saisir un moyen de contact (mail ou téléphone)"
|
||||||
@ -458,13 +459,13 @@ class CorrespondantCreationForm(FlaskForm):
|
|||||||
class CorrespondantsCreationForm(FlaskForm):
|
class CorrespondantsCreationForm(FlaskForm):
|
||||||
hidden_site_id = HiddenField()
|
hidden_site_id = HiddenField()
|
||||||
correspondants = FieldList(FormField(CorrespondantCreationForm), min_entries=1)
|
correspondants = FieldList(FormField(CorrespondantCreationForm), min_entries=1)
|
||||||
submit = SubmitField("Envoyer")
|
submit = SubmitField("Enregistrer")
|
||||||
cancel = SubmitField("Annuler")
|
cancel = SubmitField("Annuler")
|
||||||
|
|
||||||
def validate(self):
|
def validate(self, extra_validators=None):
|
||||||
validate = True
|
validate = True
|
||||||
if not FlaskForm.validate(self):
|
if not super().validate(extra_validators):
|
||||||
validate = False
|
return False
|
||||||
|
|
||||||
correspondant_list = []
|
correspondant_list = []
|
||||||
for entry in self.correspondants.entries:
|
for entry in self.correspondants.entries:
|
||||||
@ -531,10 +532,10 @@ class CorrespondantModificationForm(FlaskForm):
|
|||||||
.all()
|
.all()
|
||||||
]
|
]
|
||||||
|
|
||||||
def validate(self):
|
def validate(self, extra_validators=None):
|
||||||
validate = True
|
validate = True
|
||||||
if not FlaskForm.validate(self):
|
if not super().validate(extra_validators):
|
||||||
validate = False
|
return False
|
||||||
|
|
||||||
correspondant = EntrepriseCorrespondant.query.filter(
|
correspondant = EntrepriseCorrespondant.query.filter(
|
||||||
EntrepriseCorrespondant.id != self.hidden_correspondant_id.data,
|
EntrepriseCorrespondant.id != self.hidden_correspondant_id.data,
|
||||||
@ -566,7 +567,7 @@ class ContactCreationForm(FlaskForm):
|
|||||||
render_kw={"placeholder": "Tapez le nom de l'utilisateur"},
|
render_kw={"placeholder": "Tapez le nom de l'utilisateur"},
|
||||||
)
|
)
|
||||||
notes = TextAreaField("Notes (*)", validators=[DataRequired(message=CHAMP_REQUIS)])
|
notes = TextAreaField("Notes (*)", validators=[DataRequired(message=CHAMP_REQUIS)])
|
||||||
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
|
submit = SubmitField("Enregistrer", render_kw=SUBMIT_MARGE)
|
||||||
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
||||||
|
|
||||||
def validate_utilisateur(self, utilisateur):
|
def validate_utilisateur(self, utilisateur):
|
||||||
@ -613,8 +614,9 @@ class ContactModificationForm(FlaskForm):
|
|||||||
class StageApprentissageCreationForm(FlaskForm):
|
class StageApprentissageCreationForm(FlaskForm):
|
||||||
etudiant = _build_string_field(
|
etudiant = _build_string_field(
|
||||||
"Étudiant (*)",
|
"Étudiant (*)",
|
||||||
render_kw={"placeholder": "Tapez le nom de l'étudiant"},
|
render_kw={"placeholder": "Tapez le nom de l'étudiant", "autocomplete": "off"},
|
||||||
)
|
)
|
||||||
|
etudid = HiddenField()
|
||||||
type_offre = SelectField(
|
type_offre = SelectField(
|
||||||
"Type de l'offre (*)",
|
"Type de l'offre (*)",
|
||||||
choices=[("Stage"), ("Alternance")],
|
choices=[("Stage"), ("Alternance")],
|
||||||
@ -627,12 +629,12 @@ class StageApprentissageCreationForm(FlaskForm):
|
|||||||
"Date fin (*)", validators=[DataRequired(message=CHAMP_REQUIS)]
|
"Date fin (*)", validators=[DataRequired(message=CHAMP_REQUIS)]
|
||||||
)
|
)
|
||||||
notes = TextAreaField("Notes")
|
notes = TextAreaField("Notes")
|
||||||
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
|
submit = SubmitField("Enregistrer", render_kw=SUBMIT_MARGE)
|
||||||
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self, extra_validators=None):
|
||||||
validate = True
|
validate = True
|
||||||
if not FlaskForm.validate(self):
|
if not super().validate(extra_validators):
|
||||||
validate = False
|
validate = False
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -646,64 +648,27 @@ class StageApprentissageCreationForm(FlaskForm):
|
|||||||
|
|
||||||
return validate
|
return validate
|
||||||
|
|
||||||
def validate_etudiant(self, etudiant):
|
def validate_etudid(self, field):
|
||||||
etudiant_data = etudiant.data.upper().strip()
|
"L'etudid doit avoit été placé par le JS"
|
||||||
stm = text(
|
etudid = int(field.data) if field.data else None
|
||||||
"SELECT id, CONCAT(nom, ' ', prenom) as nom_prenom FROM Identite WHERE CONCAT(nom, ' ', prenom)=:nom_prenom"
|
etudiant = Identite.query.get(etudid) if etudid is not None else None
|
||||||
)
|
|
||||||
etudiant = (
|
|
||||||
Identite.query.from_statement(stm).params(nom_prenom=etudiant_data).first()
|
|
||||||
)
|
|
||||||
if etudiant is None:
|
if etudiant is None:
|
||||||
raise ValidationError("Champ incorrect (selectionnez dans la liste)")
|
raise ValidationError("Étudiant introuvable (sélectionnez dans la liste)")
|
||||||
|
|
||||||
|
|
||||||
class StageApprentissageModificationForm(FlaskForm):
|
class FrenchFloatField(StringField):
|
||||||
etudiant = _build_string_field(
|
"A field allowing to enter . or ,"
|
||||||
"Étudiant (*)",
|
|
||||||
render_kw={"placeholder": "Tapez le nom de l'étudiant"},
|
|
||||||
)
|
|
||||||
type_offre = SelectField(
|
|
||||||
"Type de l'offre (*)",
|
|
||||||
choices=[("Stage"), ("Alternance")],
|
|
||||||
validators=[DataRequired(message=CHAMP_REQUIS)],
|
|
||||||
)
|
|
||||||
date_debut = DateField(
|
|
||||||
"Date début (*)", validators=[DataRequired(message=CHAMP_REQUIS)]
|
|
||||||
)
|
|
||||||
date_fin = DateField(
|
|
||||||
"Date fin (*)", validators=[DataRequired(message=CHAMP_REQUIS)]
|
|
||||||
)
|
|
||||||
notes = TextAreaField("Notes")
|
|
||||||
submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE)
|
|
||||||
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
|
||||||
|
|
||||||
def validate(self):
|
def process_formdata(self, valuelist):
|
||||||
validate = True
|
"catch incoming data"
|
||||||
if not FlaskForm.validate(self):
|
if not valuelist:
|
||||||
validate = False
|
return
|
||||||
|
try:
|
||||||
if (
|
value = valuelist[0].replace(",", ".")
|
||||||
self.date_debut.data
|
self.data = float(value)
|
||||||
and self.date_fin.data
|
except ValueError as exc:
|
||||||
and self.date_debut.data > self.date_fin.data
|
self.data = None
|
||||||
):
|
raise ValueError(self.gettext("Not a valid decimal value.")) from exc
|
||||||
self.date_debut.errors.append("Les dates sont incompatibles")
|
|
||||||
self.date_fin.errors.append("Les dates sont incompatibles")
|
|
||||||
validate = False
|
|
||||||
|
|
||||||
return validate
|
|
||||||
|
|
||||||
def validate_etudiant(self, etudiant):
|
|
||||||
etudiant_data = etudiant.data.upper().strip()
|
|
||||||
stm = text(
|
|
||||||
"SELECT id, CONCAT(nom, ' ', prenom) as nom_prenom FROM Identite WHERE CONCAT(nom, ' ', prenom)=:nom_prenom"
|
|
||||||
)
|
|
||||||
etudiant = (
|
|
||||||
Identite.query.from_statement(stm).params(nom_prenom=etudiant_data).first()
|
|
||||||
)
|
|
||||||
if etudiant is None:
|
|
||||||
raise ValidationError("Champ incorrect (selectionnez dans la liste)")
|
|
||||||
|
|
||||||
|
|
||||||
class TaxeApprentissageForm(FlaskForm):
|
class TaxeApprentissageForm(FlaskForm):
|
||||||
@ -720,25 +685,26 @@ class TaxeApprentissageForm(FlaskForm):
|
|||||||
],
|
],
|
||||||
default=int(datetime.now().strftime("%Y")),
|
default=int(datetime.now().strftime("%Y")),
|
||||||
)
|
)
|
||||||
montant = IntegerField(
|
montant = FrenchFloatField(
|
||||||
"Montant (*)",
|
"Montant (*)",
|
||||||
validators=[
|
validators=[
|
||||||
DataRequired(message=CHAMP_REQUIS),
|
DataRequired(message=CHAMP_REQUIS),
|
||||||
NumberRange(
|
# NumberRange(
|
||||||
min=1,
|
# min=0.1,
|
||||||
message="Le montant doit être supérieur à 0",
|
# max=1e8,
|
||||||
),
|
# message="Le montant doit être supérieur à 0",
|
||||||
|
# ),
|
||||||
],
|
],
|
||||||
default=1,
|
default=1,
|
||||||
)
|
)
|
||||||
notes = TextAreaField("Notes")
|
notes = TextAreaField("Notes")
|
||||||
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
|
submit = SubmitField("Enregistrer", render_kw=SUBMIT_MARGE)
|
||||||
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self, extra_validators=None):
|
||||||
validate = True
|
validate = True
|
||||||
if not FlaskForm.validate(self):
|
if not super().validate(extra_validators):
|
||||||
validate = False
|
return False
|
||||||
|
|
||||||
taxe = EntrepriseTaxeApprentissage.query.filter_by(
|
taxe = EntrepriseTaxeApprentissage.query.filter_by(
|
||||||
entreprise_id=self.hidden_entreprise_id.data, annee=self.annee.data
|
entreprise_id=self.hidden_entreprise_id.data, annee=self.annee.data
|
||||||
@ -788,12 +754,12 @@ class EnvoiOffreForm(FlaskForm):
|
|||||||
submit = SubmitField("Envoyer")
|
submit = SubmitField("Envoyer")
|
||||||
cancel = SubmitField("Annuler")
|
cancel = SubmitField("Annuler")
|
||||||
|
|
||||||
def validate(self):
|
def validate(self, extra_validators=None):
|
||||||
validate = True
|
validate = True
|
||||||
list_select = True
|
list_select = True
|
||||||
|
|
||||||
if not FlaskForm.validate(self):
|
if not super().validate(extra_validators):
|
||||||
validate = False
|
return False
|
||||||
|
|
||||||
for entry in self.responsables.entries:
|
for entry in self.responsables.entries:
|
||||||
if entry.data:
|
if entry.data:
|
||||||
|
@ -164,7 +164,10 @@ class EntrepriseStageApprentissage(db.Model):
|
|||||||
entreprise_id = db.Column(
|
entreprise_id = db.Column(
|
||||||
db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade")
|
db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade")
|
||||||
)
|
)
|
||||||
etudid = db.Column(db.Integer)
|
etudid = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey("identite.id", ondelete="CASCADE"),
|
||||||
|
)
|
||||||
type_offre = db.Column(db.Text)
|
type_offre = db.Column(db.Text)
|
||||||
date_debut = db.Column(db.Date)
|
date_debut = db.Column(db.Date)
|
||||||
date_fin = db.Column(db.Date)
|
date_fin = db.Column(db.Date)
|
||||||
@ -180,7 +183,7 @@ class EntrepriseTaxeApprentissage(db.Model):
|
|||||||
db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade")
|
db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade")
|
||||||
)
|
)
|
||||||
annee = db.Column(db.Integer)
|
annee = db.Column(db.Integer)
|
||||||
montant = db.Column(db.Integer)
|
montant = db.Column(db.Float)
|
||||||
notes = db.Column(db.Text)
|
notes = db.Column(db.Text)
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@ from app.entreprises.forms import (
|
|||||||
ContactCreationForm,
|
ContactCreationForm,
|
||||||
ContactModificationForm,
|
ContactModificationForm,
|
||||||
StageApprentissageCreationForm,
|
StageApprentissageCreationForm,
|
||||||
StageApprentissageModificationForm,
|
|
||||||
EnvoiOffreForm,
|
EnvoiOffreForm,
|
||||||
AjoutFichierForm,
|
AjoutFichierForm,
|
||||||
TaxeApprentissageForm,
|
TaxeApprentissageForm,
|
||||||
@ -239,7 +238,7 @@ def delete_validation_entreprise(entreprise_id):
|
|||||||
text=f"Non validation de la fiche entreprise ({entreprise.nom})",
|
text=f"Non validation de la fiche entreprise ({entreprise.nom})",
|
||||||
)
|
)
|
||||||
db.session.add(log)
|
db.session.add(log)
|
||||||
flash("L'entreprise a été supprimé de la liste des entreprise à valider.")
|
flash("L'entreprise a été supprimée de la liste des entreprises à valider.")
|
||||||
return redirect(url_for("entreprises.validation"))
|
return redirect(url_for("entreprises.validation"))
|
||||||
return render_template(
|
return render_template(
|
||||||
"entreprises/form_confirmation.j2",
|
"entreprises/form_confirmation.j2",
|
||||||
@ -770,7 +769,7 @@ def delete_taxe_apprentissage(entreprise_id, taxe_id):
|
|||||||
)
|
)
|
||||||
db.session.add(log)
|
db.session.add(log)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("La taxe d'apprentissage a été supprimé de la liste.")
|
flash("La taxe d'apprentissage a été supprimée de la liste.")
|
||||||
return redirect(
|
return redirect(
|
||||||
url_for("entreprises.fiche_entreprise", entreprise_id=taxe.entreprise_id)
|
url_for("entreprises.fiche_entreprise", entreprise_id=taxe.entreprise_id)
|
||||||
)
|
)
|
||||||
@ -966,7 +965,7 @@ def delete_offre(entreprise_id, offre_id):
|
|||||||
)
|
)
|
||||||
db.session.add(log)
|
db.session.add(log)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("L'offre a été supprimé de la fiche entreprise.")
|
flash("L'offre a été supprimée de la fiche entreprise.")
|
||||||
return redirect(
|
return redirect(
|
||||||
url_for("entreprises.fiche_entreprise", entreprise_id=offre.entreprise_id)
|
url_for("entreprises.fiche_entreprise", entreprise_id=offre.entreprise_id)
|
||||||
)
|
)
|
||||||
@ -1473,7 +1472,8 @@ def delete_contact(entreprise_id, contact_id):
|
|||||||
@permission_required(Permission.RelationsEntreprisesChange)
|
@permission_required(Permission.RelationsEntreprisesChange)
|
||||||
def add_stage_apprentissage(entreprise_id):
|
def add_stage_apprentissage(entreprise_id):
|
||||||
"""
|
"""
|
||||||
Permet d'ajouter un étudiant ayant réalisé un stage ou une alternance sur la fiche entreprise de l'entreprise
|
Permet d'ajouter un étudiant ayant réalisé un stage ou alternance
|
||||||
|
sur la fiche de l'entreprise
|
||||||
"""
|
"""
|
||||||
entreprise = Entreprise.query.filter_by(
|
entreprise = Entreprise.query.filter_by(
|
||||||
id=entreprise_id, visible=True
|
id=entreprise_id, visible=True
|
||||||
@ -1484,15 +1484,8 @@ def add_stage_apprentissage(entreprise_id):
|
|||||||
url_for("entreprises.fiche_entreprise", entreprise_id=entreprise_id)
|
url_for("entreprises.fiche_entreprise", entreprise_id=entreprise_id)
|
||||||
)
|
)
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
etudiant_nomcomplet = form.etudiant.data.upper().strip()
|
etudid = form.etudid.data
|
||||||
stm = text(
|
etudiant = Identite.query.get_or_404(etudid)
|
||||||
"SELECT id, CONCAT(nom, ' ', prenom) as nom_prenom FROM Identite WHERE CONCAT(nom, ' ', prenom)=:nom_prenom"
|
|
||||||
)
|
|
||||||
etudiant = (
|
|
||||||
Identite.query.from_statement(stm)
|
|
||||||
.params(nom_prenom=etudiant_nomcomplet)
|
|
||||||
.first()
|
|
||||||
)
|
|
||||||
formation = etudiant.inscription_courante_date(
|
formation = etudiant.inscription_courante_date(
|
||||||
form.date_debut.data, form.date_fin.data
|
form.date_debut.data, form.date_fin.data
|
||||||
)
|
)
|
||||||
@ -1538,7 +1531,7 @@ def add_stage_apprentissage(entreprise_id):
|
|||||||
@permission_required(Permission.RelationsEntreprisesChange)
|
@permission_required(Permission.RelationsEntreprisesChange)
|
||||||
def edit_stage_apprentissage(entreprise_id, stage_apprentissage_id):
|
def edit_stage_apprentissage(entreprise_id, stage_apprentissage_id):
|
||||||
"""
|
"""
|
||||||
Permet de modifier un étudiant ayant réalisé un stage ou une alternance sur la fiche entreprise de l'entreprise
|
Permet de modifier un étudiant ayant réalisé un stage ou alternance sur la fiche de l'entreprise
|
||||||
"""
|
"""
|
||||||
stage_apprentissage = EntrepriseStageApprentissage.query.filter_by(
|
stage_apprentissage = EntrepriseStageApprentissage.query.filter_by(
|
||||||
id=stage_apprentissage_id, entreprise_id=entreprise_id
|
id=stage_apprentissage_id, entreprise_id=entreprise_id
|
||||||
@ -1548,21 +1541,14 @@ def edit_stage_apprentissage(entreprise_id, stage_apprentissage_id):
|
|||||||
etudiant = Identite.query.filter_by(id=stage_apprentissage.etudid).first_or_404(
|
etudiant = Identite.query.filter_by(id=stage_apprentissage.etudid).first_or_404(
|
||||||
description=f"etudiant {stage_apprentissage.etudid} inconnue"
|
description=f"etudiant {stage_apprentissage.etudid} inconnue"
|
||||||
)
|
)
|
||||||
form = StageApprentissageModificationForm()
|
form = StageApprentissageCreationForm()
|
||||||
if request.method == "POST" and form.cancel.data:
|
if request.method == "POST" and form.cancel.data:
|
||||||
return redirect(
|
return redirect(
|
||||||
url_for("entreprises.fiche_entreprise", entreprise_id=entreprise_id)
|
url_for("entreprises.fiche_entreprise", entreprise_id=entreprise_id)
|
||||||
)
|
)
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
etudiant_nomcomplet = form.etudiant.data.upper().strip()
|
etudid = form.etudid.data
|
||||||
stm = text(
|
etudiant = Identite.query.get_or_404(etudid)
|
||||||
"SELECT id, CONCAT(nom, ' ', prenom) as nom_prenom FROM Identite WHERE CONCAT(nom, ' ', prenom)=:nom_prenom"
|
|
||||||
)
|
|
||||||
etudiant = (
|
|
||||||
Identite.query.from_statement(stm)
|
|
||||||
.params(nom_prenom=etudiant_nomcomplet)
|
|
||||||
.first()
|
|
||||||
)
|
|
||||||
formation = etudiant.inscription_courante_date(
|
formation = etudiant.inscription_courante_date(
|
||||||
form.date_debut.data, form.date_fin.data
|
form.date_debut.data, form.date_fin.data
|
||||||
)
|
)
|
||||||
@ -1577,6 +1563,7 @@ def edit_stage_apprentissage(entreprise_id, stage_apprentissage_id):
|
|||||||
formation.formsemestre.formsemestre_id if formation else None,
|
formation.formsemestre.formsemestre_id if formation else None,
|
||||||
)
|
)
|
||||||
stage_apprentissage.notes = form.notes.data.strip()
|
stage_apprentissage.notes = form.notes.data.strip()
|
||||||
|
db.session.add(stage_apprentissage)
|
||||||
log = EntrepriseHistorique(
|
log = EntrepriseHistorique(
|
||||||
authenticated_user=current_user.user_name,
|
authenticated_user=current_user.user_name,
|
||||||
entreprise_id=stage_apprentissage.entreprise_id,
|
entreprise_id=stage_apprentissage.entreprise_id,
|
||||||
@ -1593,7 +1580,9 @@ def edit_stage_apprentissage(entreprise_id, stage_apprentissage_id):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif request.method == "GET":
|
elif request.method == "GET":
|
||||||
form.etudiant.data = f"{sco_etud.format_nom(etudiant.nom)} {sco_etud.format_prenom(etudiant.prenom)}"
|
form.etudiant.data = f"""{sco_etud.format_nom(etudiant.nom)} {
|
||||||
|
sco_etud.format_prenom(etudiant.prenom)}"""
|
||||||
|
form.etudid.data = etudiant.id
|
||||||
form.type_offre.data = stage_apprentissage.type_offre
|
form.type_offre.data = stage_apprentissage.type_offre
|
||||||
form.date_debut.data = stage_apprentissage.date_debut
|
form.date_debut.data = stage_apprentissage.date_debut
|
||||||
form.date_fin.data = stage_apprentissage.date_fin
|
form.date_fin.data = stage_apprentissage.date_fin
|
||||||
|
@ -43,8 +43,8 @@ class Identite(db.Model):
|
|||||||
"optionnel (si present, affiché à la place du nom)"
|
"optionnel (si present, affiché à la place du nom)"
|
||||||
civilite = db.Column(db.String(1), nullable=False)
|
civilite = db.Column(db.String(1), nullable=False)
|
||||||
|
|
||||||
# données d'état-civil. Si présent remplace les données d'usage dans les documents officiels (bulletins, PV)
|
# données d'état-civil. Si présent remplace les données d'usage dans les documents
|
||||||
# cf nomprenom_etat_civil()
|
# officiels (bulletins, PV): voir nomprenom_etat_civil()
|
||||||
civilite_etat_civil = db.Column(db.String(1), nullable=False, server_default="X")
|
civilite_etat_civil = db.Column(db.String(1), nullable=False, server_default="X")
|
||||||
prenom_etat_civil = db.Column(db.Text(), nullable=False, server_default="")
|
prenom_etat_civil = db.Column(db.Text(), nullable=False, server_default="")
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ class Identite(db.Model):
|
|||||||
}
|
}
|
||||||
args_dict = {}
|
args_dict = {}
|
||||||
for key, value in args.items():
|
for key, value in args.items():
|
||||||
if hasattr(cls, key):
|
if hasattr(cls, key) and not isinstance(getattr(cls, key, None), property):
|
||||||
# compat scodoc7 (mauvaise idée de l'époque)
|
# compat scodoc7 (mauvaise idée de l'époque)
|
||||||
if key in fs_empty_stored_as_nulls and value == "":
|
if key in fs_empty_stored_as_nulls and value == "":
|
||||||
value = None
|
value = None
|
||||||
|
@ -145,6 +145,18 @@ class Evaluation(db.Model):
|
|||||||
db.session.add(copy)
|
db.session.add(copy)
|
||||||
return copy
|
return copy
|
||||||
|
|
||||||
|
def is_matin(self) -> bool:
|
||||||
|
"Evaluation ayant lieu le matin (faux si pas de date)"
|
||||||
|
heure_debut_dt = self.heure_debut or datetime.time(8, 00)
|
||||||
|
# 8:00 au cas ou pas d'heure (note externe?)
|
||||||
|
return bool(self.jour) and heure_debut_dt < datetime.time(12, 00)
|
||||||
|
|
||||||
|
def is_apresmidi(self) -> bool:
|
||||||
|
"Evaluation ayant lieu l'après midi (faux si pas de date)"
|
||||||
|
heure_debut_dt = self.heure_debut or datetime.time(8, 00)
|
||||||
|
# 8:00 au cas ou pas d'heure (note externe?)
|
||||||
|
return bool(self.jour) and heure_debut_dt >= datetime.time(12, 00)
|
||||||
|
|
||||||
def set_default_poids(self) -> bool:
|
def set_default_poids(self) -> bool:
|
||||||
"""Initialize les poids bvers les UE à leurs valeurs par défaut
|
"""Initialize les poids bvers les UE à leurs valeurs par défaut
|
||||||
C'est à dire à 1 si le coef. module/UE est non nul, 0 sinon.
|
C'est à dire à 1 si le coef. module/UE est non nul, 0 sinon.
|
||||||
|
@ -782,6 +782,8 @@ class FormSemestre(db.Model):
|
|||||||
Les groupes de parcours sont ceux de la partition scu.PARTITION_PARCOURS
|
Les groupes de parcours sont ceux de la partition scu.PARTITION_PARCOURS
|
||||||
et leur nom est le code du parcours (eg "Cyber").
|
et leur nom est le code du parcours (eg "Cyber").
|
||||||
"""
|
"""
|
||||||
|
if self.formation.referentiel_competence_id is None:
|
||||||
|
return # safety net
|
||||||
partition = Partition.query.filter_by(
|
partition = Partition.query.filter_by(
|
||||||
formsemestre_id=self.id, partition_name=scu.PARTITION_PARCOURS
|
formsemestre_id=self.id, partition_name=scu.PARTITION_PARCOURS
|
||||||
).first()
|
).first()
|
||||||
@ -805,7 +807,10 @@ class FormSemestre(db.Model):
|
|||||||
query = (
|
query = (
|
||||||
ApcParcours.query.filter_by(code=group.group_name)
|
ApcParcours.query.filter_by(code=group.group_name)
|
||||||
.join(ApcReferentielCompetences)
|
.join(ApcReferentielCompetences)
|
||||||
.filter_by(dept_id=g.scodoc_dept_id)
|
.filter_by(
|
||||||
|
dept_id=g.scodoc_dept_id,
|
||||||
|
id=self.formation.referentiel_competence_id,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if query.count() != 1:
|
if query.count() != 1:
|
||||||
log(
|
log(
|
||||||
|
@ -55,7 +55,7 @@ class Module(db.Model):
|
|||||||
secondary=parcours_modules,
|
secondary=parcours_modules,
|
||||||
lazy="subquery",
|
lazy="subquery",
|
||||||
backref=db.backref("modules", lazy=True),
|
backref=db.backref("modules", lazy=True),
|
||||||
order_by="ApcParcours.numero",
|
order_by="ApcParcours.numero, ApcParcours.code",
|
||||||
)
|
)
|
||||||
|
|
||||||
app_critiques = db.relationship(
|
app_critiques = db.relationship(
|
||||||
|
@ -56,7 +56,7 @@ class NotesNotes(db.Model):
|
|||||||
"pour debug"
|
"pour debug"
|
||||||
from app.models.evaluations import Evaluation
|
from app.models.evaluations import Evaluation
|
||||||
|
|
||||||
return f"""<{self.__class__.__name__} {self.id} v={self.value} {self.date.isoformat()
|
return f"""<{self.__class__.__name__} {self.id} etudid={self.etudid} v={self.value} {self.date.isoformat()
|
||||||
} {Evaluation.query.get(self.evaluation_id) if self.evaluation_id else "X" }>"""
|
} {Evaluation.query.get(self.evaluation_id) if self.evaluation_id else "X" }>"""
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,7 +58,10 @@ class UniteEns(db.Model):
|
|||||||
|
|
||||||
# Une UE appartient soit à tous les parcours (tronc commun), soit à un sous-ensemble
|
# Une UE appartient soit à tous les parcours (tronc commun), soit à un sous-ensemble
|
||||||
parcours = db.relationship(
|
parcours = db.relationship(
|
||||||
ApcParcours, secondary="ue_parcours", backref=db.backref("ues", lazy=True)
|
ApcParcours,
|
||||||
|
secondary="ue_parcours",
|
||||||
|
backref=db.backref("ues", lazy=True),
|
||||||
|
order_by="ApcParcours.numero, ApcParcours.code",
|
||||||
)
|
)
|
||||||
|
|
||||||
# relations
|
# relations
|
||||||
|
@ -70,7 +70,7 @@ from app.comp import res_sem
|
|||||||
from app.comp.res_compat import NotesTableCompat
|
from app.comp.res_compat import NotesTableCompat
|
||||||
from app.models import Departement, FormSemestre
|
from app.models import Departement, FormSemestre
|
||||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||||
from app.scodoc.sco_exceptions import ScoPermissionDenied
|
from app.scodoc.sco_exceptions import ScoException, ScoPermissionDenied
|
||||||
from app.scodoc import html_sco_header
|
from app.scodoc import html_sco_header
|
||||||
from app.scodoc import sco_bulletins_pdf
|
from app.scodoc import sco_bulletins_pdf
|
||||||
from app.scodoc import sco_groups
|
from app.scodoc import sco_groups
|
||||||
@ -125,6 +125,12 @@ class BaseArchiver(object):
|
|||||||
if not os.path.isdir(obj_dir):
|
if not os.path.isdir(obj_dir):
|
||||||
log(f"creating directory {obj_dir}")
|
log(f"creating directory {obj_dir}")
|
||||||
os.mkdir(obj_dir)
|
os.mkdir(obj_dir)
|
||||||
|
except FileExistsError as exc:
|
||||||
|
raise ScoException(
|
||||||
|
f"""BaseArchiver error: obj_dir={obj_dir} exists={
|
||||||
|
os.path.exists(obj_dir)
|
||||||
|
} isdir={os.path.isdir(obj_dir)}"""
|
||||||
|
) from exc
|
||||||
finally:
|
finally:
|
||||||
scu.GSL.release()
|
scu.GSL.release()
|
||||||
return obj_dir
|
return obj_dir
|
||||||
|
@ -62,7 +62,9 @@ def format_etud_ident(etud):
|
|||||||
else:
|
else:
|
||||||
etud["prenom_etat_civil"] = ""
|
etud["prenom_etat_civil"] = ""
|
||||||
etud["civilite_str"] = format_civilite(etud["civilite"])
|
etud["civilite_str"] = format_civilite(etud["civilite"])
|
||||||
etud["civilite_etat_civil_str"] = format_civilite(etud["civilite_etat_civil"])
|
etud["civilite_etat_civil_str"] = format_civilite(
|
||||||
|
etud.get("civilite_etat_civil", "X")
|
||||||
|
)
|
||||||
# Nom à afficher:
|
# Nom à afficher:
|
||||||
if etud["nom_usuel"]:
|
if etud["nom_usuel"]:
|
||||||
etud["nom_disp"] = etud["nom_usuel"]
|
etud["nom_disp"] = etud["nom_usuel"]
|
||||||
@ -145,7 +147,7 @@ def format_civilite(civilite):
|
|||||||
|
|
||||||
def format_etat_civil(etud: dict):
|
def format_etat_civil(etud: dict):
|
||||||
if etud["prenom_etat_civil"]:
|
if etud["prenom_etat_civil"]:
|
||||||
civ = {"M": "M.", "F": "Mme", "X": ""}[etud["civilite_etat_civil"]]
|
civ = {"M": "M.", "F": "Mme", "X": ""}[etud.get("civilite_etat_civil", "X")]
|
||||||
return f'{civ} {etud["prenom_etat_civil"]} {etud["nom"]}'
|
return f'{civ} {etud["prenom_etat_civil"]} {etud["nom"]}'
|
||||||
else:
|
else:
|
||||||
return etud["nomprenom"]
|
return etud["nomprenom"]
|
||||||
|
@ -252,12 +252,11 @@ def do_evaluation_delete(evaluation_id):
|
|||||||
def do_evaluation_get_all_notes(
|
def do_evaluation_get_all_notes(
|
||||||
evaluation_id, table="notes_notes", filter_suppressed=True, by_uid=None
|
evaluation_id, table="notes_notes", filter_suppressed=True, by_uid=None
|
||||||
):
|
):
|
||||||
"""Toutes les notes pour une evaluation: { etudid : { 'value' : value, 'date' : date ... }}
|
"""Toutes les notes pour une évaluation: { etudid : { 'value' : value, 'date' : date ... }}
|
||||||
Attention: inclut aussi les notes des étudiants qui ne sont plus inscrits au module.
|
Attention: inclut aussi les notes des étudiants qui ne sont plus inscrits au module.
|
||||||
"""
|
"""
|
||||||
do_cache = (
|
# pas de cache pour (rares) appels via undo_notes ou specifiant un enseignant
|
||||||
filter_suppressed and table == "notes_notes" and (by_uid is None)
|
do_cache = filter_suppressed and table == "notes_notes" and (by_uid is None)
|
||||||
) # pas de cache pour (rares) appels via undo_notes ou specifiant un enseignant
|
|
||||||
if do_cache:
|
if do_cache:
|
||||||
r = sco_cache.EvaluationCache.get(evaluation_id)
|
r = sco_cache.EvaluationCache.get(evaluation_id)
|
||||||
if r is not None:
|
if r is not None:
|
||||||
|
@ -433,7 +433,7 @@ def excel_simple_table(
|
|||||||
return ws.generate()
|
return ws.generate()
|
||||||
|
|
||||||
|
|
||||||
def excel_feuille_saisie(e, titreannee, description, lines):
|
def excel_feuille_saisie(evaluation: "Evaluation", titreannee, description, lines):
|
||||||
"""Genere feuille excel pour saisie des notes.
|
"""Genere feuille excel pour saisie des notes.
|
||||||
E: evaluation (dict)
|
E: evaluation (dict)
|
||||||
lines: liste de tuples
|
lines: liste de tuples
|
||||||
@ -512,18 +512,20 @@ def excel_feuille_saisie(e, titreannee, description, lines):
|
|||||||
# description evaluation
|
# description evaluation
|
||||||
ws.append_single_cell_row(scu.unescape_html(description), style_titres)
|
ws.append_single_cell_row(scu.unescape_html(description), style_titres)
|
||||||
ws.append_single_cell_row(
|
ws.append_single_cell_row(
|
||||||
"Evaluation du %s (coef. %g)" % (e["jour"], e["coefficient"]), style
|
"Evaluation du %s (coef. %g)"
|
||||||
|
% (evaluation.jour or "sans date", evaluation.coefficient or 0.0),
|
||||||
|
style,
|
||||||
)
|
)
|
||||||
# ligne blanche
|
# ligne blanche
|
||||||
ws.append_blank_row()
|
ws.append_blank_row()
|
||||||
# code et titres colonnes
|
# code et titres colonnes
|
||||||
ws.append_row(
|
ws.append_row(
|
||||||
[
|
[
|
||||||
ws.make_cell("!%s" % e["evaluation_id"], style_ro),
|
ws.make_cell("!%s" % evaluation.id, style_ro),
|
||||||
ws.make_cell("Nom", style_titres),
|
ws.make_cell("Nom", style_titres),
|
||||||
ws.make_cell("Prénom", style_titres),
|
ws.make_cell("Prénom", style_titres),
|
||||||
ws.make_cell("Groupe", style_titres),
|
ws.make_cell("Groupe", style_titres),
|
||||||
ws.make_cell("Note sur %g" % e["note_max"], style_titres),
|
ws.make_cell("Note sur %g" % (evaluation.note_max or 0.0), style_titres),
|
||||||
ws.make_cell("Remarque", style_titres),
|
ws.make_cell("Remarque", style_titres),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -1333,11 +1333,18 @@ Ceci n'est possible que si :
|
|||||||
cancelbutton="Annuler",
|
cancelbutton="Annuler",
|
||||||
)
|
)
|
||||||
if tf[0] == 0:
|
if tf[0] == 0:
|
||||||
if formsemestre_has_decisions_or_compensations(formsemestre):
|
has_decisions, message = formsemestre_has_decisions_or_compensations(
|
||||||
|
formsemestre
|
||||||
|
)
|
||||||
|
if has_decisions:
|
||||||
H.append(
|
H.append(
|
||||||
"""<p><b>Ce semestre ne peut pas être supprimé !
|
f"""<p><b>Ce semestre ne peut pas être supprimé !</b></p>
|
||||||
(il y a des décisions de jury ou des compensations par d'autres semestres)</b>
|
<p>il y a des décisions de jury ou des compensations par d'autres semestres:
|
||||||
</p>"""
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>{message}</li>
|
||||||
|
</ul>
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
H.append(tf[1])
|
H.append(tf[1])
|
||||||
@ -1372,32 +1379,46 @@ def formsemestre_delete2(formsemestre_id, dialog_confirmed=False):
|
|||||||
return flask.redirect(scu.ScoURL())
|
return flask.redirect(scu.ScoURL())
|
||||||
|
|
||||||
|
|
||||||
def formsemestre_has_decisions_or_compensations(formsemestre: FormSemestre):
|
def formsemestre_has_decisions_or_compensations(
|
||||||
|
formsemestre: FormSemestre,
|
||||||
|
) -> tuple[bool, str]:
|
||||||
"""True if decision de jury (sem. UE, RCUE, année) émanant de ce semestre
|
"""True if decision de jury (sem. UE, RCUE, année) émanant de ce semestre
|
||||||
ou compensation de ce semestre par d'autres semestres
|
ou compensation de ce semestre par d'autres semestres
|
||||||
ou autorisations de passage.
|
ou autorisations de passage.
|
||||||
"""
|
"""
|
||||||
# Validations de semestre ou d'UEs
|
# Validations de semestre ou d'UEs
|
||||||
if ScolarFormSemestreValidation.query.filter_by(
|
nb_validations = ScolarFormSemestreValidation.query.filter_by(
|
||||||
formsemestre_id=formsemestre.id
|
formsemestre_id=formsemestre.id
|
||||||
).count():
|
).count()
|
||||||
return True
|
if nb_validations:
|
||||||
if ScolarFormSemestreValidation.query.filter_by(
|
return True, f"{nb_validations} validations de semestre ou d'UE"
|
||||||
|
nb_validations = ScolarFormSemestreValidation.query.filter_by(
|
||||||
compense_formsemestre_id=formsemestre.id
|
compense_formsemestre_id=formsemestre.id
|
||||||
).count():
|
).count()
|
||||||
return True
|
if nb_validations:
|
||||||
|
return True, f"{nb_validations} compensations utilisées dans d'autres semestres"
|
||||||
# Autorisations d'inscription:
|
# Autorisations d'inscription:
|
||||||
if ScolarAutorisationInscription.query.filter_by(
|
nb_validations = ScolarAutorisationInscription.query.filter_by(
|
||||||
origin_formsemestre_id=formsemestre.id
|
origin_formsemestre_id=formsemestre.id
|
||||||
).count():
|
).count()
|
||||||
return True
|
if nb_validations:
|
||||||
|
return (
|
||||||
|
True,
|
||||||
|
f"{nb_validations} autorisations d'inscriptions émanant de ce semestre",
|
||||||
|
)
|
||||||
# Validations d'années BUT
|
# Validations d'années BUT
|
||||||
if ApcValidationAnnee.query.filter_by(formsemestre_id=formsemestre.id).count():
|
nb_validations = ApcValidationAnnee.query.filter_by(
|
||||||
return True
|
formsemestre_id=formsemestre.id
|
||||||
|
).count()
|
||||||
|
if nb_validations:
|
||||||
|
return True, f"{nb_validations} validations d'année BUT utilisant ce semestre"
|
||||||
# Validations de RCUEs
|
# Validations de RCUEs
|
||||||
if ApcValidationRCUE.query.filter_by(formsemestre_id=formsemestre.id).count():
|
nb_validations = ApcValidationRCUE.query.filter_by(
|
||||||
return True
|
formsemestre_id=formsemestre.id
|
||||||
return False
|
).count()
|
||||||
|
if nb_validations:
|
||||||
|
return True, f"{nb_validations} validations de RCUE utilisant ce semestre"
|
||||||
|
return False, ""
|
||||||
|
|
||||||
|
|
||||||
def do_formsemestre_delete(formsemestre_id):
|
def do_formsemestre_delete(formsemestre_id):
|
||||||
|
@ -175,9 +175,7 @@ def do_formsemestre_demission(
|
|||||||
)
|
)
|
||||||
db.session.add(event)
|
db.session.add(event)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
sco_cache.invalidate_formsemestre(
|
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre_id)
|
||||||
formsemestre_id=formsemestre_id
|
|
||||||
) # > démission ou défaillance
|
|
||||||
if etat_new == scu.DEMISSION:
|
if etat_new == scu.DEMISSION:
|
||||||
flash("Démission enregistrée")
|
flash("Démission enregistrée")
|
||||||
elif etat_new == scu.DEF:
|
elif etat_new == scu.DEF:
|
||||||
@ -210,7 +208,7 @@ def do_formsemestre_desinscription(etudid, formsemestre_id):
|
|||||||
|
|
||||||
if nt.etud_has_decision(etudid):
|
if nt.etud_has_decision(etudid):
|
||||||
raise ScoValueError(
|
raise ScoValueError(
|
||||||
"""désinscription impossible: l'étudiant {etud.nomprenom} a
|
f"""désinscription impossible: l'étudiant {etud.nomprenom} a
|
||||||
une décision de jury (la supprimer avant si nécessaire)"""
|
une décision de jury (la supprimer avant si nécessaire)"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -600,6 +600,7 @@ def formsemestre_description_table(
|
|||||||
formsemestre: FormSemestre = FormSemestre.query.filter_by(
|
formsemestre: FormSemestre = FormSemestre.query.filter_by(
|
||||||
id=formsemestre_id, dept_id=g.scodoc_dept_id
|
id=formsemestre_id, dept_id=g.scodoc_dept_id
|
||||||
).first_or_404()
|
).first_or_404()
|
||||||
|
is_apc = formsemestre.formation.is_apc()
|
||||||
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||||
use_ue_coefs = sco_preferences.get_preference("use_ue_coefs", formsemestre_id)
|
use_ue_coefs = sco_preferences.get_preference("use_ue_coefs", formsemestre_id)
|
||||||
parcours = codes_cursus.get_cursus_from_code(formsemestre.formation.type_parcours)
|
parcours = codes_cursus.get_cursus_from_code(formsemestre.formation.type_parcours)
|
||||||
@ -613,7 +614,7 @@ def formsemestre_description_table(
|
|||||||
else:
|
else:
|
||||||
ues = formsemestre.get_ues()
|
ues = formsemestre.get_ues()
|
||||||
columns_ids += [f"ue_{ue.id}" for ue in ues]
|
columns_ids += [f"ue_{ue.id}" for ue in ues]
|
||||||
if sco_preferences.get_preference("bul_show_ects", formsemestre_id):
|
if sco_preferences.get_preference("bul_show_ects", formsemestre_id) and not is_apc:
|
||||||
columns_ids += ["ects"]
|
columns_ids += ["ects"]
|
||||||
columns_ids += ["Inscrits", "Responsable", "Enseignants"]
|
columns_ids += ["Inscrits", "Responsable", "Enseignants"]
|
||||||
if with_evals:
|
if with_evals:
|
||||||
@ -640,6 +641,7 @@ def formsemestre_description_table(
|
|||||||
sum_coef = 0
|
sum_coef = 0
|
||||||
sum_ects = 0
|
sum_ects = 0
|
||||||
last_ue_id = None
|
last_ue_id = None
|
||||||
|
formsemestre_parcours_ids = {p.id for p in formsemestre.parcours}
|
||||||
for modimpl in formsemestre.modimpls_sorted:
|
for modimpl in formsemestre.modimpls_sorted:
|
||||||
# Ligne UE avec ECTS:
|
# Ligne UE avec ECTS:
|
||||||
ue = modimpl.module.ue
|
ue = modimpl.module.ue
|
||||||
@ -666,7 +668,7 @@ def formsemestre_description_table(
|
|||||||
ue_info[
|
ue_info[
|
||||||
f"_{k}_td_attrs"
|
f"_{k}_td_attrs"
|
||||||
] = f'style="background-color: {ue.color} !important;"'
|
] = f'style="background-color: {ue.color} !important;"'
|
||||||
if not formsemestre.formation.is_apc():
|
if not is_apc:
|
||||||
# n'affiche la ligne UE qu'en formation classique
|
# n'affiche la ligne UE qu'en formation classique
|
||||||
# car l'UE de rattachement n'a pas d'intérêt en BUT
|
# car l'UE de rattachement n'a pas d'intérêt en BUT
|
||||||
rows.append(ue_info)
|
rows.append(ue_info)
|
||||||
@ -707,8 +709,17 @@ def formsemestre_description_table(
|
|||||||
for ue in ues:
|
for ue in ues:
|
||||||
row[f"ue_{ue.id}"] = coef_dict.get(ue.id, 0.0) or ""
|
row[f"ue_{ue.id}"] = coef_dict.get(ue.id, 0.0) or ""
|
||||||
if with_parcours:
|
if with_parcours:
|
||||||
|
# Intersection des parcours du module avec ceux du formsemestre
|
||||||
row["parcours"] = ", ".join(
|
row["parcours"] = ", ".join(
|
||||||
sorted([pa.code for pa in modimpl.module.parcours])
|
[
|
||||||
|
pa.code
|
||||||
|
for pa in (
|
||||||
|
modimpl.module.parcours
|
||||||
|
if modimpl.module.parcours
|
||||||
|
else modimpl.formsemestre.parcours
|
||||||
|
)
|
||||||
|
if pa.id in formsemestre_parcours_ids
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
rows.append(row)
|
rows.append(row)
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
"""Opérations d'inscriptions aux modules (interface pour gérer options ou parcours)
|
"""Opérations d'inscriptions aux modules (interface pour gérer options ou parcours)
|
||||||
"""
|
"""
|
||||||
import collections
|
import collections
|
||||||
from operator import itemgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
from flask import url_for, g, request
|
from flask import url_for, g, request
|
||||||
@ -553,8 +553,11 @@ def _list_but_ue_inscriptions(res: NotesTableCompat, read_only: bool = True) ->
|
|||||||
>{etud.nomprenom}</a></td>"""
|
>{etud.nomprenom}</a></td>"""
|
||||||
)
|
)
|
||||||
# Parcours:
|
# Parcours:
|
||||||
group = partition_parcours.get_etud_group(etud.id)
|
if partition_parcours:
|
||||||
parcours_name = group.group_name if group else ""
|
group = partition_parcours.get_etud_group(etud.id)
|
||||||
|
parcours_name = group.group_name if group else ""
|
||||||
|
else:
|
||||||
|
parcours_name = ""
|
||||||
H.append(f"""<td class="parcours">{parcours_name}</td>""")
|
H.append(f"""<td class="parcours">{parcours_name}</td>""")
|
||||||
# UEs:
|
# UEs:
|
||||||
for ue in ues:
|
for ue in ues:
|
||||||
@ -668,7 +671,7 @@ def descr_inscrs_module(moduleimpl_id, set_all, partitions):
|
|||||||
gr.append((partition["partition_name"], grp))
|
gr.append((partition["partition_name"], grp))
|
||||||
#
|
#
|
||||||
d = []
|
d = []
|
||||||
for (partition_name, grp) in gr:
|
for partition_name, grp in gr:
|
||||||
if grp:
|
if grp:
|
||||||
d.append("groupes de %s: %s" % (partition_name, ", ".join(grp)))
|
d.append("groupes de %s: %s" % (partition_name, ", ".join(grp)))
|
||||||
r = []
|
r = []
|
||||||
@ -680,25 +683,25 @@ def descr_inscrs_module(moduleimpl_id, set_all, partitions):
|
|||||||
return False, len(ins), " et ".join(r)
|
return False, len(ins), " et ".join(r)
|
||||||
|
|
||||||
|
|
||||||
def _fmt_etud_set(ins, max_list_size=7):
|
def _fmt_etud_set(etudids, max_list_size=7) -> str:
|
||||||
# max_list_size est le nombre max de noms d'etudiants listés
|
# max_list_size est le nombre max de noms d'etudiants listés
|
||||||
# au delà, on indique juste le nombre, sans les noms.
|
# au delà, on indique juste le nombre, sans les noms.
|
||||||
if len(ins) > max_list_size:
|
if len(etudids) > max_list_size:
|
||||||
return "%d étudiants" % len(ins)
|
return f"{len(etudids)} étudiants"
|
||||||
etuds = []
|
etuds = []
|
||||||
for etudid in ins:
|
for etudid in etudids:
|
||||||
etuds.append(sco_etud.get_etud_info(etudid=etudid, filled=True)[0])
|
etud = Identite.query.get(etudid)
|
||||||
etuds.sort(key=itemgetter("nom"))
|
if etud:
|
||||||
|
etuds.append(etud)
|
||||||
|
|
||||||
return ", ".join(
|
return ", ".join(
|
||||||
[
|
[
|
||||||
'<a class="discretelink" href="%s">%s</a>'
|
f"""<a class="discretelink" href="{
|
||||||
% (
|
|
||||||
url_for(
|
url_for(
|
||||||
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud["etudid"]
|
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud.id
|
||||||
),
|
)
|
||||||
etud["nomprenom"],
|
}">{etud.nomprenom}</a>"""
|
||||||
)
|
for etud in sorted(etuds, key=attrgetter("sort_key"))
|
||||||
for etud in etuds
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -337,17 +337,18 @@ def ficheEtud(etudid=None):
|
|||||||
if not sco_permissions_check.can_suppress_annotation(a["id"]):
|
if not sco_permissions_check.can_suppress_annotation(a["id"]):
|
||||||
a["dellink"] = ""
|
a["dellink"] = ""
|
||||||
else:
|
else:
|
||||||
a[
|
a["dellink"] = (
|
||||||
"dellink"
|
'<td class="annodel"><a href="doSuppressAnnotation?etudid=%s&annotation_id=%s">%s</a></td>'
|
||||||
] = '<td class="annodel"><a href="doSuppressAnnotation?etudid=%s&annotation_id=%s">%s</a></td>' % (
|
% (
|
||||||
etudid,
|
etudid,
|
||||||
a["id"],
|
a["id"],
|
||||||
scu.icontag(
|
scu.icontag(
|
||||||
"delete_img",
|
"delete_img",
|
||||||
border="0",
|
border="0",
|
||||||
alt="suppress",
|
alt="suppress",
|
||||||
title="Supprimer cette annotation",
|
title="Supprimer cette annotation",
|
||||||
),
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
author = sco_users.user_info(a["author"])
|
author = sco_users.user_info(a["author"])
|
||||||
alist.append(
|
alist.append(
|
||||||
@ -446,7 +447,7 @@ def ficheEtud(etudid=None):
|
|||||||
info[
|
info[
|
||||||
"inscriptions_mkup"
|
"inscriptions_mkup"
|
||||||
] = f"""<div class="ficheinscriptions" id="ficheinscriptions">
|
] = f"""<div class="ficheinscriptions" id="ficheinscriptions">
|
||||||
<div class="fichetitre">Parcours</div>{info["liste_inscriptions"]}
|
<div class="fichetitre">Cursus</div>{info["liste_inscriptions"]}
|
||||||
{info["link_bul_pdf"]} {info["link_inscrire_ailleurs"]}
|
{info["link_bul_pdf"]} {info["link_inscrire_ailleurs"]}
|
||||||
</div>"""
|
</div>"""
|
||||||
|
|
||||||
|
@ -489,6 +489,7 @@ def _normalize_apo_fields(infolist):
|
|||||||
recode les champs: paiementinscription (-> booleen), datefinalisationinscription (date)
|
recode les champs: paiementinscription (-> booleen), datefinalisationinscription (date)
|
||||||
ajoute le champs 'paiementinscription_str' : 'ok', 'Non' ou '?'
|
ajoute le champs 'paiementinscription_str' : 'ok', 'Non' ou '?'
|
||||||
ajoute les champs 'etape' (= None) et 'prenom' ('') s'ils ne sont pas présents.
|
ajoute les champs 'etape' (= None) et 'prenom' ('') s'ils ne sont pas présents.
|
||||||
|
ajoute le champ 'civilite_etat_civil' (='X'), et 'prenom_etat_civil' (='') si non présent.
|
||||||
"""
|
"""
|
||||||
for infos in infolist:
|
for infos in infolist:
|
||||||
if "paiementinscription" in infos:
|
if "paiementinscription" in infos:
|
||||||
@ -520,6 +521,15 @@ def _normalize_apo_fields(infolist):
|
|||||||
if "prenom" not in infos:
|
if "prenom" not in infos:
|
||||||
infos["prenom"] = ""
|
infos["prenom"] = ""
|
||||||
|
|
||||||
|
if "civilite_etat_civil" not in infos:
|
||||||
|
infos["civilite_etat_civil"] = "X"
|
||||||
|
|
||||||
|
if "civilite_etat_civil" not in infos:
|
||||||
|
infos["civilite_etat_civil"] = "X"
|
||||||
|
|
||||||
|
if "prenom_etat_civil" not in infos:
|
||||||
|
infos["prenom_etat_civil"] = ""
|
||||||
|
|
||||||
return infolist
|
return infolist
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,41 +36,49 @@ import flask
|
|||||||
from flask import g, url_for, request
|
from flask import g, url_for, request
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
|
from app import log
|
||||||
from app.auth.models import User
|
from app.auth.models import User
|
||||||
from app.comp import res_sem
|
from app.comp import res_sem
|
||||||
from app.comp.res_compat import NotesTableCompat
|
from app.comp.res_compat import NotesTableCompat
|
||||||
from app.models import Evaluation, FormSemestre
|
from app.models import (
|
||||||
from app.models import ModuleImpl, ScolarNews
|
Evaluation,
|
||||||
|
FormSemestre,
|
||||||
|
Module,
|
||||||
|
ModuleImpl,
|
||||||
|
NotesNotes,
|
||||||
|
ScolarNews,
|
||||||
|
)
|
||||||
from app.models.etudiants import Identite
|
from app.models.etudiants import Identite
|
||||||
import app.scodoc.sco_utils as scu
|
|
||||||
from app.scodoc.sco_utils import ModuleType
|
|
||||||
import app.scodoc.notesdb as ndb
|
|
||||||
from app import log
|
|
||||||
from app.scodoc.sco_exceptions import (
|
from app.scodoc.sco_exceptions import (
|
||||||
AccessDenied,
|
AccessDenied,
|
||||||
InvalidNoteValue,
|
InvalidNoteValue,
|
||||||
NoteProcessError,
|
NoteProcessError,
|
||||||
ScoGenError,
|
ScoBugCatcher,
|
||||||
|
ScoException,
|
||||||
ScoInvalidParamError,
|
ScoInvalidParamError,
|
||||||
ScoValueError,
|
ScoValueError,
|
||||||
)
|
)
|
||||||
from app.scodoc.TrivialFormulator import TrivialFormulator, TF
|
|
||||||
from app.scodoc import html_sco_header, sco_users
|
from app.scodoc import html_sco_header, sco_users
|
||||||
from app.scodoc import htmlutils
|
from app.scodoc import htmlutils
|
||||||
from app.scodoc import sco_abs
|
from app.scodoc import sco_abs
|
||||||
from app.scodoc import sco_cache
|
from app.scodoc import sco_cache
|
||||||
from app.scodoc import sco_edit_module
|
from app.scodoc import sco_edit_module
|
||||||
from app.scodoc import sco_evaluations
|
from app.scodoc import sco_etud
|
||||||
from app.scodoc import sco_evaluation_db
|
from app.scodoc import sco_evaluation_db
|
||||||
|
from app.scodoc import sco_evaluations
|
||||||
from app.scodoc import sco_excel
|
from app.scodoc import sco_excel
|
||||||
from app.scodoc import sco_formsemestre
|
|
||||||
from app.scodoc import sco_formsemestre_inscriptions
|
from app.scodoc import sco_formsemestre_inscriptions
|
||||||
from app.scodoc import sco_groups
|
from app.scodoc import sco_groups
|
||||||
from app.scodoc import sco_groups_view
|
from app.scodoc import sco_groups_view
|
||||||
from app.scodoc import sco_moduleimpl
|
from app.scodoc import sco_moduleimpl
|
||||||
from app.scodoc import sco_permissions_check
|
from app.scodoc import sco_permissions_check
|
||||||
from app.scodoc import sco_undo_notes
|
from app.scodoc import sco_undo_notes
|
||||||
from app.scodoc import sco_etud
|
import app.scodoc.notesdb as ndb
|
||||||
|
from app.scodoc.TrivialFormulator import TrivialFormulator, TF
|
||||||
|
import app.scodoc.sco_utils as scu
|
||||||
|
from app.scodoc.sco_utils import json_error
|
||||||
|
from app.scodoc.sco_utils import ModuleType
|
||||||
|
|
||||||
|
|
||||||
def convert_note_from_string(
|
def convert_note_from_string(
|
||||||
@ -128,29 +136,30 @@ def _displayNote(val):
|
|||||||
return val
|
return val
|
||||||
|
|
||||||
|
|
||||||
def _check_notes(notes: list[(int, float)], evaluation: dict, mod: dict):
|
def _check_notes(notes: list[(int, float)], evaluation: Evaluation):
|
||||||
# XXX typehint : float or str
|
# XXX typehint : float or str
|
||||||
"""notes is a list of tuples (etudid, value)
|
"""notes is a list of tuples (etudid, value)
|
||||||
mod is the module (used to ckeck type, for malus)
|
mod is the module (used to ckeck type, for malus)
|
||||||
returns list of valid notes (etudid, float value)
|
returns list of valid notes (etudid, float value)
|
||||||
and 4 lists of etudid: invalids, withoutnotes, absents, tosuppress, existingjury
|
and 4 lists of etudid: etudids_invalids, etudids_without_notes, etudids_absents, etudid_to_suppress
|
||||||
"""
|
"""
|
||||||
note_max = evaluation["note_max"]
|
note_max = evaluation.note_max or 0.0
|
||||||
if mod["module_type"] in (
|
module: Module = evaluation.moduleimpl.module
|
||||||
|
if module.module_type in (
|
||||||
scu.ModuleType.STANDARD,
|
scu.ModuleType.STANDARD,
|
||||||
scu.ModuleType.RESSOURCE,
|
scu.ModuleType.RESSOURCE,
|
||||||
scu.ModuleType.SAE,
|
scu.ModuleType.SAE,
|
||||||
):
|
):
|
||||||
note_min = scu.NOTES_MIN
|
note_min = scu.NOTES_MIN
|
||||||
elif mod["module_type"] == ModuleType.MALUS:
|
elif module.module_type == ModuleType.MALUS:
|
||||||
note_min = -20.0
|
note_min = -20.0
|
||||||
else:
|
else:
|
||||||
raise ValueError("Invalid module type") # bug
|
raise ValueError("Invalid module type") # bug
|
||||||
L = [] # liste (etudid, note) des notes ok (ou absent)
|
valid_notes = [] # liste (etudid, note) des notes ok (ou absent)
|
||||||
invalids = [] # etudid avec notes invalides
|
etudids_invalids = [] # etudid avec notes invalides
|
||||||
withoutnotes = [] # etudid sans notes (champs vides)
|
etudids_without_notes = [] # etudid sans notes (champs vides)
|
||||||
absents = [] # etudid absents
|
etudids_absents = [] # etudid absents
|
||||||
tosuppress = [] # etudids avec ancienne note à supprimer
|
etudid_to_suppress = [] # etudids avec ancienne note à supprimer
|
||||||
|
|
||||||
for etudid, note in notes:
|
for etudid, note in notes:
|
||||||
note = str(note).strip().upper()
|
note = str(note).strip().upper()
|
||||||
@ -166,31 +175,34 @@ def _check_notes(notes: list[(int, float)], evaluation: dict, mod: dict):
|
|||||||
note_max,
|
note_max,
|
||||||
note_min=note_min,
|
note_min=note_min,
|
||||||
etudid=etudid,
|
etudid=etudid,
|
||||||
absents=absents,
|
absents=etudids_absents,
|
||||||
tosuppress=tosuppress,
|
tosuppress=etudid_to_suppress,
|
||||||
invalids=invalids,
|
invalids=etudids_invalids,
|
||||||
)
|
)
|
||||||
if not invalid:
|
if not invalid:
|
||||||
L.append((etudid, value))
|
valid_notes.append((etudid, value))
|
||||||
else:
|
else:
|
||||||
withoutnotes.append(etudid)
|
etudids_without_notes.append(etudid)
|
||||||
return L, invalids, withoutnotes, absents, tosuppress
|
return (
|
||||||
|
valid_notes,
|
||||||
|
etudids_invalids,
|
||||||
|
etudids_without_notes,
|
||||||
|
etudids_absents,
|
||||||
|
etudid_to_suppress,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def do_evaluation_upload_xls():
|
def do_evaluation_upload_xls():
|
||||||
"""
|
"""
|
||||||
Soumission d'un fichier XLS (evaluation_id, notefile)
|
Soumission d'un fichier XLS (evaluation_id, notefile)
|
||||||
"""
|
"""
|
||||||
authuser = current_user
|
|
||||||
vals = scu.get_request_args()
|
vals = scu.get_request_args()
|
||||||
evaluation_id = int(vals["evaluation_id"])
|
evaluation_id = int(vals["evaluation_id"])
|
||||||
comment = vals["comment"]
|
comment = vals["comment"]
|
||||||
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
evaluation: Evaluation = Evaluation.query.get_or_404(evaluation_id)
|
||||||
M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
# Check access (admin, respformation, and responsable_id)
|
||||||
# Check access
|
if not sco_permissions_check.can_edit_notes(current_user, evaluation.moduleimpl_id):
|
||||||
# (admin, respformation, and responsable_id)
|
raise AccessDenied(f"Modification des notes impossible pour {current_user}")
|
||||||
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]):
|
|
||||||
raise AccessDenied("Modification des notes impossible pour %s" % authuser)
|
|
||||||
#
|
#
|
||||||
diag, lines = sco_excel.excel_file_to_list(vals["notefile"])
|
diag, lines = sco_excel.excel_file_to_list(vals["notefile"])
|
||||||
try:
|
try:
|
||||||
@ -239,14 +251,16 @@ def do_evaluation_upload_xls():
|
|||||||
if etudid:
|
if etudid:
|
||||||
notes.append((etudid, val))
|
notes.append((etudid, val))
|
||||||
ni += 1
|
ni += 1
|
||||||
except:
|
except Exception as exc:
|
||||||
diag.append(
|
diag.append(
|
||||||
f"""Erreur: Ligne invalide ! (erreur ligne {ni})<br>{lines[ni]}"""
|
f"""Erreur: Ligne invalide ! (erreur ligne {ni})<br>{lines[ni]}"""
|
||||||
)
|
)
|
||||||
raise InvalidNoteValue()
|
raise InvalidNoteValue() from exc
|
||||||
# -- check values
|
# -- check values
|
||||||
L, invalids, withoutnotes, absents, _ = _check_notes(notes, E, M["module"])
|
valid_notes, invalids, withoutnotes, absents, _ = _check_notes(
|
||||||
if len(invalids):
|
notes, evaluation
|
||||||
|
)
|
||||||
|
if invalids:
|
||||||
diag.append(
|
diag.append(
|
||||||
f"Erreur: la feuille contient {len(invalids)} notes invalides</p>"
|
f"Erreur: la feuille contient {len(invalids)} notes invalides</p>"
|
||||||
)
|
)
|
||||||
@ -258,37 +272,33 @@ def do_evaluation_upload_xls():
|
|||||||
diag.append("Notes invalides pour: " + ", ".join(etudsnames))
|
diag.append("Notes invalides pour: " + ", ".join(etudsnames))
|
||||||
raise InvalidNoteValue()
|
raise InvalidNoteValue()
|
||||||
else:
|
else:
|
||||||
nb_changed, nb_suppress, existing_decisions = notes_add(
|
etudids_changed, nb_suppress, etudids_with_decisions = notes_add(
|
||||||
authuser, evaluation_id, L, comment
|
current_user, evaluation_id, valid_notes, comment
|
||||||
)
|
)
|
||||||
# news
|
# news
|
||||||
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[
|
module: Module = evaluation.moduleimpl.module
|
||||||
0
|
status_url = url_for(
|
||||||
]
|
|
||||||
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
|
||||||
mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
|
|
||||||
mod["moduleimpl_id"] = M["moduleimpl_id"]
|
|
||||||
mod["url"] = url_for(
|
|
||||||
"notes.moduleimpl_status",
|
"notes.moduleimpl_status",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
moduleimpl_id=mod["moduleimpl_id"],
|
moduleimpl_id=evaluation.moduleimpl_id,
|
||||||
_external=True,
|
_external=True,
|
||||||
)
|
)
|
||||||
ScolarNews.add(
|
ScolarNews.add(
|
||||||
typ=ScolarNews.NEWS_NOTE,
|
typ=ScolarNews.NEWS_NOTE,
|
||||||
obj=M["moduleimpl_id"],
|
obj=evaluation.moduleimpl_id,
|
||||||
text='Chargement notes dans <a href="%(url)s">%(titre)s</a>' % mod,
|
text=f"""Chargement notes dans <a href="{status_url}">{
|
||||||
url=mod["url"],
|
module.titre or module.code}</a>""",
|
||||||
|
url=status_url,
|
||||||
max_frequency=30 * 60, # 30 minutes
|
max_frequency=30 * 60, # 30 minutes
|
||||||
)
|
)
|
||||||
|
|
||||||
msg = (
|
msg = f"""<p>{len(etudids_changed)} notes changées ({len(withoutnotes)} sans notes, {
|
||||||
"<p>%d notes changées (%d sans notes, %d absents, %d note supprimées)</p>"
|
len(absents)} absents, {nb_suppress} note supprimées)
|
||||||
% (nb_changed, len(withoutnotes), len(absents), nb_suppress)
|
</p>"""
|
||||||
)
|
if etudids_with_decisions:
|
||||||
if existing_decisions:
|
msg += """<p class="warning">Important: il y avait déjà des décisions de jury
|
||||||
msg += """<p class="warning">Important: il y avait déjà des décisions de jury enregistrées, qui sont potentiellement à revoir suite à cette modification !</p>"""
|
enregistrées, qui sont peut-être à revoir suite à cette modification !</p>
|
||||||
# msg += '<p>' + str(notes) # debug
|
"""
|
||||||
return 1, msg
|
return 1, msg
|
||||||
|
|
||||||
except InvalidNoteValue:
|
except InvalidNoteValue:
|
||||||
@ -310,14 +320,12 @@ def do_evaluation_set_etud_note(evaluation: Evaluation, etud: Identite, value) -
|
|||||||
if not sco_permissions_check.can_edit_notes(current_user, evaluation.moduleimpl.id):
|
if not sco_permissions_check.can_edit_notes(current_user, evaluation.moduleimpl.id):
|
||||||
raise AccessDenied(f"Modification des notes impossible pour {current_user}")
|
raise AccessDenied(f"Modification des notes impossible pour {current_user}")
|
||||||
# Convert and check value
|
# Convert and check value
|
||||||
L, invalids, _, _, _ = _check_notes(
|
L, invalids, _, _, _ = _check_notes([(etud.id, value)], evaluation)
|
||||||
[(etud.id, value)], evaluation.to_dict(), evaluation.moduleimpl.module.to_dict()
|
|
||||||
)
|
|
||||||
if len(invalids) == 0:
|
if len(invalids) == 0:
|
||||||
nb_changed, _, _ = notes_add(
|
etudids_changed, _, _ = notes_add(
|
||||||
current_user, evaluation.id, L, "Initialisation notes"
|
current_user, evaluation.id, L, "Initialisation notes"
|
||||||
)
|
)
|
||||||
if nb_changed == 1:
|
if len(etudids_changed) == 1:
|
||||||
return True
|
return True
|
||||||
return False # error
|
return False # error
|
||||||
|
|
||||||
@ -352,9 +360,7 @@ def do_evaluation_set_missing(
|
|||||||
if etudid not in notes_db: # pas de note
|
if etudid not in notes_db: # pas de note
|
||||||
notes.append((etudid, value))
|
notes.append((etudid, value))
|
||||||
# Convert and check values
|
# Convert and check values
|
||||||
L, invalids, _, _, _ = _check_notes(
|
valid_notes, invalids, _, _, _ = _check_notes(notes, evaluation)
|
||||||
notes, evaluation.to_dict(), modimpl.module.to_dict()
|
|
||||||
)
|
|
||||||
dest_url = url_for(
|
dest_url = url_for(
|
||||||
"notes.saisie_notes", scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id
|
"notes.saisie_notes", scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id
|
||||||
)
|
)
|
||||||
@ -372,13 +378,13 @@ def do_evaluation_set_missing(
|
|||||||
"""
|
"""
|
||||||
# Confirm action
|
# Confirm action
|
||||||
if not dialog_confirmed:
|
if not dialog_confirmed:
|
||||||
plural = len(L) > 1
|
plural = len(valid_notes) > 1
|
||||||
return scu.confirm_dialog(
|
return scu.confirm_dialog(
|
||||||
f"""<h2>Mettre toutes les notes manquantes de l'évaluation
|
f"""<h2>Mettre toutes les notes manquantes de l'évaluation
|
||||||
à la valeur {value} ?</h2>
|
à la valeur {value} ?</h2>
|
||||||
<p>Seuls les étudiants pour lesquels aucune note (ni valeur, ni ABS, ni EXC)
|
<p>Seuls les étudiants pour lesquels aucune note (ni valeur, ni ABS, ni EXC)
|
||||||
n'a été rentrée seront affectés.</p>
|
n'a été rentrée seront affectés.</p>
|
||||||
<p><b>{len(L)} étudiant{"s" if plural else ""} concerné{"s" if plural else ""}
|
<p><b>{len(valid_notes)} étudiant{"s" if plural else ""} concerné{"s" if plural else ""}
|
||||||
par ce changement de note.</b>
|
par ce changement de note.</b>
|
||||||
</p>
|
</p>
|
||||||
""",
|
""",
|
||||||
@ -392,7 +398,7 @@ def do_evaluation_set_missing(
|
|||||||
)
|
)
|
||||||
# ok
|
# ok
|
||||||
comment = "Initialisation notes manquantes"
|
comment = "Initialisation notes manquantes"
|
||||||
nb_changed, _, _ = notes_add(current_user, evaluation_id, L, comment)
|
etudids_changed, _, _ = notes_add(current_user, evaluation_id, valid_notes, comment)
|
||||||
# news
|
# news
|
||||||
url = url_for(
|
url = url_for(
|
||||||
"notes.moduleimpl_status",
|
"notes.moduleimpl_status",
|
||||||
@ -408,7 +414,7 @@ def do_evaluation_set_missing(
|
|||||||
)
|
)
|
||||||
return f"""
|
return f"""
|
||||||
{ html_sco_header.sco_header() }
|
{ html_sco_header.sco_header() }
|
||||||
<h2>{nb_changed} notes changées</h2>
|
<h2>{len(etudids_changed)} notes changées</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a class="stdlink" href="{dest_url}">
|
<li><a class="stdlink" href="{dest_url}">
|
||||||
Revenir au formulaire de saisie des notes</a>
|
Revenir au formulaire de saisie des notes</a>
|
||||||
@ -454,7 +460,7 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not dialog_confirmed:
|
if not dialog_confirmed:
|
||||||
nb_changed, nb_suppress, existing_decisions = notes_add(
|
etudids_changed, nb_suppress, existing_decisions = notes_add(
|
||||||
current_user, evaluation_id, notes, do_it=False, check_inscription=False
|
current_user, evaluation_id, notes, do_it=False, check_inscription=False
|
||||||
)
|
)
|
||||||
msg = f"""<p>Confirmer la suppression des {nb_suppress} notes ?
|
msg = f"""<p>Confirmer la suppression des {nb_suppress} notes ?
|
||||||
@ -475,14 +481,14 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# modif
|
# modif
|
||||||
nb_changed, nb_suppress, existing_decisions = notes_add(
|
etudids_changed, nb_suppress, existing_decisions = notes_add(
|
||||||
current_user,
|
current_user,
|
||||||
evaluation_id,
|
evaluation_id,
|
||||||
notes,
|
notes,
|
||||||
comment="effacer tout",
|
comment="effacer tout",
|
||||||
check_inscription=False,
|
check_inscription=False,
|
||||||
)
|
)
|
||||||
assert nb_changed == nb_suppress
|
assert len(etudids_changed) == nb_suppress
|
||||||
H = [f"""<p>{nb_suppress} notes supprimées</p>"""]
|
H = [f"""<p>{nb_suppress} notes supprimées</p>"""]
|
||||||
if existing_decisions:
|
if existing_decisions:
|
||||||
H.append(
|
H.append(
|
||||||
@ -516,7 +522,7 @@ def notes_add(
|
|||||||
comment=None,
|
comment=None,
|
||||||
do_it=True,
|
do_it=True,
|
||||||
check_inscription=True,
|
check_inscription=True,
|
||||||
) -> tuple:
|
) -> tuple[list[int], int, list[int]]:
|
||||||
"""
|
"""
|
||||||
Insert or update notes
|
Insert or update notes
|
||||||
notes is a list of tuples (etudid,value)
|
notes is a list of tuples (etudid,value)
|
||||||
@ -524,12 +530,12 @@ def notes_add(
|
|||||||
WOULD be changed or suppressed.
|
WOULD be changed or suppressed.
|
||||||
Nota:
|
Nota:
|
||||||
- si la note existe deja avec valeur distincte, ajoute une entree au log (notes_notes_log)
|
- si la note existe deja avec valeur distincte, ajoute une entree au log (notes_notes_log)
|
||||||
Return tuple (nb_changed, nb_suppress, existing_decisions)
|
|
||||||
|
Return: tuple (etudids_changed, nb_suppress, etudids_with_decision)
|
||||||
"""
|
"""
|
||||||
now = psycopg2.Timestamp(
|
now = psycopg2.Timestamp(*time.localtime()[:6])
|
||||||
*time.localtime()[:6]
|
|
||||||
) # datetime.datetime.now().isoformat()
|
# Vérifie inscription et valeur note
|
||||||
# Verifie inscription et valeur note
|
|
||||||
inscrits = {
|
inscrits = {
|
||||||
x[0]
|
x[0]
|
||||||
for x in sco_groups.do_evaluation_listeetuds_groups(
|
for x in sco_groups.do_evaluation_listeetuds_groups(
|
||||||
@ -548,13 +554,13 @@ def notes_add(
|
|||||||
# Met a jour la base
|
# Met a jour la base
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||||
nb_changed = 0
|
etudids_changed = []
|
||||||
nb_suppress = 0
|
nb_suppress = 0
|
||||||
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
evaluation: Evaluation = Evaluation.query.get_or_404(evaluation_id)
|
||||||
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
formsemestre: FormSemestre = evaluation.moduleimpl.formsemestre
|
||||||
existing_decisions = (
|
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||||
[]
|
# etudids pour lesquels il y a une decision de jury et que la note change:
|
||||||
) # etudids pour lesquels il y a une decision de jury et que la note change
|
etudids_with_decision = []
|
||||||
try:
|
try:
|
||||||
for etudid, value in notes:
|
for etudid, value in notes:
|
||||||
changed = False
|
changed = False
|
||||||
@ -562,7 +568,7 @@ def notes_add(
|
|||||||
# nouvelle note
|
# nouvelle note
|
||||||
if value != scu.NOTES_SUPPRESS:
|
if value != scu.NOTES_SUPPRESS:
|
||||||
if do_it:
|
if do_it:
|
||||||
aa = {
|
args = {
|
||||||
"etudid": etudid,
|
"etudid": etudid,
|
||||||
"evaluation_id": evaluation_id,
|
"evaluation_id": evaluation_id,
|
||||||
"value": value,
|
"value": value,
|
||||||
@ -570,13 +576,20 @@ def notes_add(
|
|||||||
"uid": user.id,
|
"uid": user.id,
|
||||||
"date": now,
|
"date": now,
|
||||||
}
|
}
|
||||||
ndb.quote_dict(aa)
|
ndb.quote_dict(args)
|
||||||
|
# Note: le conflit ci-dessous peut arriver si un autre thread
|
||||||
|
# a modifié la base après qu'on ait lu notes_db
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""INSERT INTO notes_notes
|
"""INSERT INTO notes_notes
|
||||||
(etudid, evaluation_id, value, comment, date, uid)
|
(etudid, evaluation_id, value, comment, date, uid)
|
||||||
VALUES (%(etudid)s,%(evaluation_id)s,%(value)s,%(comment)s,%(date)s,%(uid)s)
|
VALUES
|
||||||
|
(%(etudid)s,%(evaluation_id)s,%(value)s,
|
||||||
|
%(comment)s,%(date)s,%(uid)s)
|
||||||
|
ON CONFLICT ON CONSTRAINT notes_notes_etudid_evaluation_id_key
|
||||||
|
DO UPDATE SET etudid=%(etudid)s, evaluation_id=%(evaluation_id)s,
|
||||||
|
value=%(value)s, comment=%(comment)s, date=%(date)s, uid=%(uid)s
|
||||||
""",
|
""",
|
||||||
aa,
|
args,
|
||||||
)
|
)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
@ -584,7 +597,7 @@ def notes_add(
|
|||||||
oldval = notes_db[etudid]["value"]
|
oldval = notes_db[etudid]["value"]
|
||||||
if type(value) != type(oldval):
|
if type(value) != type(oldval):
|
||||||
changed = True
|
changed = True
|
||||||
elif type(value) == float and (
|
elif isinstance(value, float) and (
|
||||||
abs(value - oldval) > scu.NOTES_PRECISION
|
abs(value - oldval) > scu.NOTES_PRECISION
|
||||||
):
|
):
|
||||||
changed = True
|
changed = True
|
||||||
@ -603,7 +616,7 @@ def notes_add(
|
|||||||
""",
|
""",
|
||||||
{"etudid": etudid, "evaluation_id": evaluation_id},
|
{"etudid": etudid, "evaluation_id": evaluation_id},
|
||||||
)
|
)
|
||||||
aa = {
|
args = {
|
||||||
"etudid": etudid,
|
"etudid": etudid,
|
||||||
"evaluation_id": evaluation_id,
|
"evaluation_id": evaluation_id,
|
||||||
"value": value,
|
"value": value,
|
||||||
@ -611,7 +624,7 @@ def notes_add(
|
|||||||
"comment": comment,
|
"comment": comment,
|
||||||
"uid": user.id,
|
"uid": user.id,
|
||||||
}
|
}
|
||||||
ndb.quote_dict(aa)
|
ndb.quote_dict(args)
|
||||||
if value != scu.NOTES_SUPPRESS:
|
if value != scu.NOTES_SUPPRESS:
|
||||||
if do_it:
|
if do_it:
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
@ -620,52 +633,49 @@ def notes_add(
|
|||||||
WHERE etudid = %(etudid)s
|
WHERE etudid = %(etudid)s
|
||||||
and evaluation_id = %(evaluation_id)s
|
and evaluation_id = %(evaluation_id)s
|
||||||
""",
|
""",
|
||||||
aa,
|
args,
|
||||||
)
|
)
|
||||||
else: # suppression ancienne note
|
else: # suppression ancienne note
|
||||||
if do_it:
|
if do_it:
|
||||||
log(
|
log(
|
||||||
"notes_add, suppress, evaluation_id=%s, etudid=%s, oldval=%s"
|
f"""notes_add, suppress, evaluation_id={evaluation_id}, etudid={
|
||||||
% (evaluation_id, etudid, oldval)
|
etudid}, oldval={oldval}"""
|
||||||
)
|
)
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""DELETE FROM notes_notes
|
"""DELETE FROM notes_notes
|
||||||
WHERE etudid = %(etudid)s
|
WHERE etudid = %(etudid)s
|
||||||
AND evaluation_id = %(evaluation_id)s
|
AND evaluation_id = %(evaluation_id)s
|
||||||
""",
|
""",
|
||||||
aa,
|
args,
|
||||||
)
|
)
|
||||||
# garde trace de la suppression dans l'historique:
|
# garde trace de la suppression dans l'historique:
|
||||||
aa["value"] = scu.NOTES_SUPPRESS
|
args["value"] = scu.NOTES_SUPPRESS
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""INSERT INTO notes_notes_log (etudid,evaluation_id,value,comment,date,uid)
|
"""INSERT INTO notes_notes_log
|
||||||
VALUES (%(etudid)s, %(evaluation_id)s, %(value)s, %(comment)s, %(date)s, %(uid)s)
|
(etudid,evaluation_id,value,comment,date,uid)
|
||||||
|
VALUES
|
||||||
|
(%(etudid)s, %(evaluation_id)s, %(value)s, %(comment)s, %(date)s, %(uid)s)
|
||||||
""",
|
""",
|
||||||
aa,
|
args,
|
||||||
)
|
)
|
||||||
nb_suppress += 1
|
nb_suppress += 1
|
||||||
if changed:
|
if changed:
|
||||||
nb_changed += 1
|
etudids_changed.append(etudid)
|
||||||
if has_existing_decision(M, E, etudid):
|
if res.etud_has_decision(etudid):
|
||||||
existing_decisions.append(etudid)
|
etudids_with_decision.append(etudid)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
log("*** exception in notes_add")
|
log("*** exception in notes_add")
|
||||||
if do_it:
|
if do_it:
|
||||||
cnx.rollback() # abort
|
cnx.rollback() # abort
|
||||||
# inval cache
|
# inval cache
|
||||||
sco_cache.invalidate_formsemestre(
|
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
|
||||||
formsemestre_id=M["formsemestre_id"]
|
|
||||||
) # > modif notes (exception)
|
|
||||||
sco_cache.EvaluationCache.delete(evaluation_id)
|
sco_cache.EvaluationCache.delete(evaluation_id)
|
||||||
raise # XXX
|
raise ScoException from exc
|
||||||
raise ScoGenError("Erreur enregistrement note: merci de ré-essayer") from exc
|
|
||||||
if do_it:
|
if do_it:
|
||||||
cnx.commit()
|
cnx.commit()
|
||||||
sco_cache.invalidate_formsemestre(
|
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
|
||||||
formsemestre_id=M["formsemestre_id"]
|
|
||||||
) # > modif notes
|
|
||||||
sco_cache.EvaluationCache.delete(evaluation_id)
|
sco_cache.EvaluationCache.delete(evaluation_id)
|
||||||
return nb_changed, nb_suppress, existing_decisions
|
return etudids_changed, nb_suppress, etudids_with_decision
|
||||||
|
|
||||||
|
|
||||||
def saisie_notes_tableur(evaluation_id, group_ids=()):
|
def saisie_notes_tableur(evaluation_id, group_ids=()):
|
||||||
@ -868,44 +878,39 @@ def saisie_notes_tableur(evaluation_id, group_ids=()):
|
|||||||
|
|
||||||
def feuille_saisie_notes(evaluation_id, group_ids=[]):
|
def feuille_saisie_notes(evaluation_id, group_ids=[]):
|
||||||
"""Document Excel pour saisie notes dans l'évaluation et les groupes indiqués"""
|
"""Document Excel pour saisie notes dans l'évaluation et les groupes indiqués"""
|
||||||
evals = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})
|
evaluation: Evaluation = Evaluation.query.get(evaluation_id)
|
||||||
if not evals:
|
if not evaluation:
|
||||||
raise ScoValueError("invalid evaluation_id")
|
raise ScoValueError("invalid evaluation_id")
|
||||||
eval_dict = evals[0]
|
modimpl = evaluation.moduleimpl
|
||||||
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=eval_dict["moduleimpl_id"])[0]
|
formsemestre = modimpl.formsemestre
|
||||||
formsemestre_id = M["formsemestre_id"]
|
mod_responsable = sco_users.user_info(modimpl.responsable_id)
|
||||||
Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
|
if evaluation.jour:
|
||||||
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
|
indication_date = evaluation.jour.isoformat()
|
||||||
mod_responsable = sco_users.user_info(M["responsable_id"])
|
|
||||||
if eval_dict["jour"]:
|
|
||||||
indication_date = ndb.DateDMYtoISO(eval_dict["jour"])
|
|
||||||
else:
|
else:
|
||||||
indication_date = scu.sanitize_filename(eval_dict["description"])[:12]
|
indication_date = scu.sanitize_filename(evaluation.description or "")[:12]
|
||||||
eval_name = "%s-%s" % (Mod["code"], indication_date)
|
eval_name = f"{evaluation.moduleimpl.module.code}-{indication_date}"
|
||||||
|
|
||||||
if eval_dict["description"]:
|
date_str = (
|
||||||
evaltitre = "%s du %s" % (eval_dict["description"], eval_dict["jour"])
|
f"""du {evaluation.jour.strftime("%d/%m/%Y")}"""
|
||||||
else:
|
if evaluation.jour
|
||||||
evaltitre = "évaluation du %s" % eval_dict["jour"]
|
else "(sans date)"
|
||||||
description = "%s en %s (%s) resp. %s" % (
|
|
||||||
evaltitre,
|
|
||||||
Mod["abbrev"] or "",
|
|
||||||
Mod["code"] or "",
|
|
||||||
mod_responsable["prenomnom"],
|
|
||||||
)
|
)
|
||||||
|
eval_titre = f"""{evaluation.description if evaluation.description else "évaluation"} {date_str}"""
|
||||||
|
|
||||||
|
description = f"""{eval_titre} en {evaluation.moduleimpl.module.abbrev or ""} ({
|
||||||
|
evaluation.moduleimpl.module.code
|
||||||
|
}) resp. {mod_responsable["prenomnom"]}"""
|
||||||
|
|
||||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
||||||
group_ids=group_ids,
|
group_ids=group_ids,
|
||||||
formsemestre_id=formsemestre_id,
|
formsemestre_id=formsemestre.id,
|
||||||
select_all_when_unspecified=True,
|
select_all_when_unspecified=True,
|
||||||
etat=None,
|
etat=None,
|
||||||
)
|
)
|
||||||
groups = sco_groups.listgroups(groups_infos.group_ids)
|
groups = sco_groups.listgroups(groups_infos.group_ids)
|
||||||
gr_title_filename = sco_groups.listgroups_filename(groups)
|
gr_title_filename = sco_groups.listgroups_filename(groups)
|
||||||
# gr_title = sco_groups.listgroups_abbrev(groups)
|
|
||||||
if None in [g["group_name"] for g in groups]: # tous les etudiants
|
if None in [g["group_name"] for g in groups]: # tous les etudiants
|
||||||
getallstudents = True
|
getallstudents = True
|
||||||
# gr_title = "tous"
|
|
||||||
gr_title_filename = "tous"
|
gr_title_filename = "tous"
|
||||||
else:
|
else:
|
||||||
getallstudents = False
|
getallstudents = False
|
||||||
@ -917,17 +922,17 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]):
|
|||||||
]
|
]
|
||||||
|
|
||||||
# une liste de liste de chaines: lignes de la feuille de calcul
|
# une liste de liste de chaines: lignes de la feuille de calcul
|
||||||
L = []
|
rows = []
|
||||||
|
|
||||||
etuds = _get_sorted_etuds(eval_dict, etudids, formsemestre_id)
|
etuds = _get_sorted_etuds(evaluation, etudids, formsemestre.id)
|
||||||
for e in etuds:
|
for e in etuds:
|
||||||
etudid = e["etudid"]
|
etudid = e["etudid"]
|
||||||
groups = sco_groups.get_etud_groups(etudid, formsemestre_id)
|
groups = sco_groups.get_etud_groups(etudid, formsemestre.id)
|
||||||
grc = sco_groups.listgroups_abbrev(groups)
|
grc = sco_groups.listgroups_abbrev(groups)
|
||||||
|
|
||||||
L.append(
|
rows.append(
|
||||||
[
|
[
|
||||||
"%s" % etudid,
|
str(etudid),
|
||||||
e["nom"].upper(),
|
e["nom"].upper(),
|
||||||
e["prenom"].lower().capitalize(),
|
e["prenom"].lower().capitalize(),
|
||||||
e["inscr"]["etat"],
|
e["inscr"]["etat"],
|
||||||
@ -937,31 +942,11 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
filename = "notes_%s_%s" % (eval_name, gr_title_filename)
|
filename = f"notes_{eval_name}_{gr_title_filename}"
|
||||||
xls = sco_excel.excel_feuille_saisie(
|
xls = sco_excel.excel_feuille_saisie(
|
||||||
eval_dict, sem["titreannee"], description, lines=L
|
evaluation, formsemestre.titre_annee(), description, lines=rows
|
||||||
)
|
)
|
||||||
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE)
|
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE)
|
||||||
# return sco_excel.send_excel_file(xls, filename)
|
|
||||||
|
|
||||||
|
|
||||||
def has_existing_decision(M, E, etudid):
|
|
||||||
"""Verifie s'il y a une validation pour cet etudiant dans ce semestre ou UE
|
|
||||||
Si oui, return True
|
|
||||||
"""
|
|
||||||
formsemestre_id = M["formsemestre_id"]
|
|
||||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
|
||||||
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
|
||||||
if nt.get_etud_decision_sem(etudid):
|
|
||||||
return True
|
|
||||||
dec_ues = nt.get_etud_decisions_ue(etudid)
|
|
||||||
if dec_ues:
|
|
||||||
mod = sco_edit_module.module_list({"module_id": M["module_id"]})[0]
|
|
||||||
ue_id = mod["ue_id"]
|
|
||||||
if ue_id in dec_ues:
|
|
||||||
return True # decision pour l'UE a laquelle appartient cette evaluation
|
|
||||||
|
|
||||||
return False # pas de decision de jury affectee par cette note
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
@ -973,20 +958,18 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
|
|||||||
if not isinstance(evaluation_id, int):
|
if not isinstance(evaluation_id, int):
|
||||||
raise ScoInvalidParamError()
|
raise ScoInvalidParamError()
|
||||||
group_ids = [int(group_id) for group_id in (group_ids or [])]
|
group_ids = [int(group_id) for group_id in (group_ids or [])]
|
||||||
evals = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})
|
evaluation: Evaluation = Evaluation.query.get(evaluation_id)
|
||||||
if not evals:
|
if evaluation is None:
|
||||||
raise ScoValueError("évaluation inexistante")
|
raise ScoValueError("évaluation inexistante")
|
||||||
E = evals[0]
|
modimpl = evaluation.moduleimpl
|
||||||
M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
|
||||||
formsemestre_id = M["formsemestre_id"]
|
|
||||||
moduleimpl_status_url = url_for(
|
moduleimpl_status_url = url_for(
|
||||||
"notes.moduleimpl_status",
|
"notes.moduleimpl_status",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
moduleimpl_id=E["moduleimpl_id"],
|
moduleimpl_id=evaluation.moduleimpl_id,
|
||||||
)
|
)
|
||||||
# Check access
|
# Check access
|
||||||
# (admin, respformation, and responsable_id)
|
# (admin, respformation, and responsable_id)
|
||||||
if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]):
|
if not sco_permissions_check.can_edit_notes(current_user, evaluation.moduleimpl_id):
|
||||||
return f"""
|
return f"""
|
||||||
{html_sco_header.sco_header()}
|
{html_sco_header.sco_header()}
|
||||||
<h2>Modification des notes impossible pour {current_user.user_name}</h2>
|
<h2>Modification des notes impossible pour {current_user.user_name}</h2>
|
||||||
@ -1001,16 +984,16 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
|
|||||||
# Informations sur les groupes à afficher:
|
# Informations sur les groupes à afficher:
|
||||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
||||||
group_ids=group_ids,
|
group_ids=group_ids,
|
||||||
formsemestre_id=formsemestre_id,
|
formsemestre_id=modimpl.formsemestre_id,
|
||||||
select_all_when_unspecified=True,
|
select_all_when_unspecified=True,
|
||||||
etat=None,
|
etat=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
if E["description"]:
|
page_title = (
|
||||||
page_title = 'Saisie "%s"' % E["description"]
|
f'Saisie "{evaluation.description}"'
|
||||||
else:
|
if evaluation.description
|
||||||
page_title = "Saisie des notes"
|
else "Saisie des notes"
|
||||||
|
)
|
||||||
# HTML page:
|
# HTML page:
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(
|
html_sco_header.sco_header(
|
||||||
@ -1036,19 +1019,19 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
|
|||||||
"id": "menu_saisie_tableur",
|
"id": "menu_saisie_tableur",
|
||||||
"endpoint": "notes.saisie_notes_tableur",
|
"endpoint": "notes.saisie_notes_tableur",
|
||||||
"args": {
|
"args": {
|
||||||
"evaluation_id": E["evaluation_id"],
|
"evaluation_id": evaluation.id,
|
||||||
"group_ids": groups_infos.group_ids,
|
"group_ids": groups_infos.group_ids,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Voir toutes les notes du module",
|
"title": "Voir toutes les notes du module",
|
||||||
"endpoint": "notes.evaluation_listenotes",
|
"endpoint": "notes.evaluation_listenotes",
|
||||||
"args": {"moduleimpl_id": E["moduleimpl_id"]},
|
"args": {"moduleimpl_id": evaluation.moduleimpl_id},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Effacer toutes les notes de cette évaluation",
|
"title": "Effacer toutes les notes de cette évaluation",
|
||||||
"endpoint": "notes.evaluation_suppress_alln",
|
"endpoint": "notes.evaluation_suppress_alln",
|
||||||
"args": {"evaluation_id": E["evaluation_id"]},
|
"args": {"evaluation_id": evaluation.id},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
alone=True,
|
alone=True,
|
||||||
@ -1077,7 +1060,9 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Le formulaire de saisie des notes:
|
# Le formulaire de saisie des notes:
|
||||||
form = _form_saisie_notes(E, M, groups_infos, destination=moduleimpl_status_url)
|
form = _form_saisie_notes(
|
||||||
|
evaluation, modimpl, groups_infos, destination=moduleimpl_status_url
|
||||||
|
)
|
||||||
if form is None:
|
if form is None:
|
||||||
return flask.redirect(moduleimpl_status_url)
|
return flask.redirect(moduleimpl_status_url)
|
||||||
H.append(form)
|
H.append(form)
|
||||||
@ -1101,10 +1086,9 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
|
|||||||
return "\n".join(H)
|
return "\n".join(H)
|
||||||
|
|
||||||
|
|
||||||
def _get_sorted_etuds(eval_dict: dict, etudids: list, formsemestre_id: int):
|
def _get_sorted_etuds(evaluation: Evaluation, etudids: list, formsemestre_id: int):
|
||||||
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(
|
# Notes existantes
|
||||||
eval_dict["evaluation_id"]
|
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation.id)
|
||||||
) # Notes existantes
|
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
etuds = []
|
etuds = []
|
||||||
for etudid in etudids:
|
for etudid in etudids:
|
||||||
@ -1123,17 +1107,17 @@ def _get_sorted_etuds(eval_dict: dict, etudids: list, formsemestre_id: int):
|
|||||||
e["groups"] = sco_groups.get_etud_groups(etudid, formsemestre_id)
|
e["groups"] = sco_groups.get_etud_groups(etudid, formsemestre_id)
|
||||||
|
|
||||||
# Information sur absence (tenant compte de la demi-journée)
|
# Information sur absence (tenant compte de la demi-journée)
|
||||||
jour_iso = ndb.DateDMYtoISO(eval_dict["jour"])
|
jour_iso = evaluation.jour.isoformat() if evaluation.jour else ""
|
||||||
warn_abs_lst = []
|
warn_abs_lst = []
|
||||||
if eval_dict["matin"]:
|
if evaluation.is_matin():
|
||||||
nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=1)
|
nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=True)
|
||||||
nbabsjust = sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=1)
|
nbabsjust = sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=True)
|
||||||
if nbabs:
|
if nbabs:
|
||||||
if nbabsjust:
|
if nbabsjust:
|
||||||
warn_abs_lst.append("absent justifié le matin !")
|
warn_abs_lst.append("absent justifié le matin !")
|
||||||
else:
|
else:
|
||||||
warn_abs_lst.append("absent le matin !")
|
warn_abs_lst.append("absent le matin !")
|
||||||
if eval_dict["apresmidi"]:
|
if evaluation.is_apresmidi():
|
||||||
nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=0)
|
nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=0)
|
||||||
nbabsjust = sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=0)
|
nbabsjust = sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=0)
|
||||||
if nbabs:
|
if nbabs:
|
||||||
@ -1169,35 +1153,38 @@ def _get_sorted_etuds(eval_dict: dict, etudids: list, formsemestre_id: int):
|
|||||||
return etuds
|
return etuds
|
||||||
|
|
||||||
|
|
||||||
def _form_saisie_notes(E, M, groups_infos, destination=""):
|
def _form_saisie_notes(
|
||||||
|
evaluation: Evaluation, modimpl: ModuleImpl, groups_infos, destination=""
|
||||||
|
):
|
||||||
"""Formulaire HTML saisie des notes dans l'évaluation E du moduleimpl M
|
"""Formulaire HTML saisie des notes dans l'évaluation E du moduleimpl M
|
||||||
pour les groupes indiqués.
|
pour les groupes indiqués.
|
||||||
|
|
||||||
On charge tous les étudiants, ne seront montrés que ceux
|
On charge tous les étudiants, ne seront montrés que ceux
|
||||||
des groupes sélectionnés grace a un filtre en javascript.
|
des groupes sélectionnés grace a un filtre en javascript.
|
||||||
"""
|
"""
|
||||||
evaluation_id = E["evaluation_id"]
|
formsemestre_id = modimpl.formsemestre_id
|
||||||
formsemestre_id = M["formsemestre_id"]
|
formsemestre: FormSemestre = evaluation.moduleimpl.formsemestre
|
||||||
|
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||||
etudids = [
|
etudids = [
|
||||||
x[0]
|
x[0]
|
||||||
for x in sco_groups.do_evaluation_listeetuds_groups(
|
for x in sco_groups.do_evaluation_listeetuds_groups(
|
||||||
evaluation_id, getallstudents=True, include_demdef=True
|
evaluation.id, getallstudents=True, include_demdef=True
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
if not etudids:
|
if not etudids:
|
||||||
return '<div class="ue_warning"><span>Aucun étudiant sélectionné !</span></div>'
|
return '<div class="ue_warning"><span>Aucun étudiant sélectionné !</span></div>'
|
||||||
|
|
||||||
# Decisions de jury existantes ?
|
# Décisions de jury existantes ?
|
||||||
decisions_jury = {etudid: has_existing_decision(M, E, etudid) for etudid in etudids}
|
decisions_jury = {etudid: res.etud_has_decision(etudid) for etudid in etudids}
|
||||||
# Nb de decisions de jury (pour les inscrits à l'évaluation):
|
|
||||||
|
# Nb de décisions de jury (pour les inscrits à l'évaluation):
|
||||||
nb_decisions = sum(decisions_jury.values())
|
nb_decisions = sum(decisions_jury.values())
|
||||||
|
|
||||||
etuds = _get_sorted_etuds(E, etudids, formsemestre_id)
|
etuds = _get_sorted_etuds(evaluation, etudids, formsemestre_id)
|
||||||
|
|
||||||
# Build form:
|
# Build form:
|
||||||
descr = [
|
descr = [
|
||||||
("evaluation_id", {"default": evaluation_id, "input_type": "hidden"}),
|
("evaluation_id", {"default": evaluation.id, "input_type": "hidden"}),
|
||||||
("formsemestre_id", {"default": formsemestre_id, "input_type": "hidden"}),
|
("formsemestre_id", {"default": formsemestre_id, "input_type": "hidden"}),
|
||||||
(
|
(
|
||||||
"group_ids",
|
"group_ids",
|
||||||
@ -1207,7 +1194,7 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
|
|||||||
("comment", {"size": 44, "title": "Commentaire", "return_focus_next": True}),
|
("comment", {"size": 44, "title": "Commentaire", "return_focus_next": True}),
|
||||||
("changed", {"default": "0", "input_type": "hidden"}), # changed in JS
|
("changed", {"default": "0", "input_type": "hidden"}), # changed in JS
|
||||||
]
|
]
|
||||||
if M["module"]["module_type"] in (
|
if modimpl.module.module_type in (
|
||||||
ModuleType.STANDARD,
|
ModuleType.STANDARD,
|
||||||
ModuleType.RESSOURCE,
|
ModuleType.RESSOURCE,
|
||||||
ModuleType.SAE,
|
ModuleType.SAE,
|
||||||
@ -1220,11 +1207,11 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
|
|||||||
"title": "Notes ",
|
"title": "Notes ",
|
||||||
"cssclass": "formnote_bareme",
|
"cssclass": "formnote_bareme",
|
||||||
"readonly": True,
|
"readonly": True,
|
||||||
"default": " / %g" % E["note_max"],
|
"default": " / %g" % evaluation.note_max,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif M["module"]["module_type"] == ModuleType.MALUS:
|
elif modimpl.module.module_type == ModuleType.MALUS:
|
||||||
descr.append(
|
descr.append(
|
||||||
(
|
(
|
||||||
"s3",
|
"s3",
|
||||||
@ -1238,7 +1225,7 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError("invalid module type (%s)" % M["module"]["module_type"]) # bug
|
raise ValueError(f"invalid module type ({modimpl.module.module_type})") # bug
|
||||||
|
|
||||||
initvalues = {}
|
initvalues = {}
|
||||||
for e in etuds:
|
for e in etuds:
|
||||||
@ -1248,7 +1235,7 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
|
|||||||
if disabled:
|
if disabled:
|
||||||
classdem = " etud_dem"
|
classdem = " etud_dem"
|
||||||
etud_classes.append("etud_dem")
|
etud_classes.append("etud_dem")
|
||||||
disabled_attr = 'disabled="%d"' % disabled
|
disabled_attr = f'disabled="{disabled}"'
|
||||||
else:
|
else:
|
||||||
classdem = ""
|
classdem = ""
|
||||||
disabled_attr = ""
|
disabled_attr = ""
|
||||||
@ -1265,18 +1252,17 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Historique des saisies de notes:
|
# Historique des saisies de notes:
|
||||||
if not disabled:
|
explanation = (
|
||||||
explanation = (
|
""
|
||||||
'<span id="hist_%s">' % etudid
|
if disabled
|
||||||
+ get_note_history_menu(evaluation_id, etudid)
|
else f"""<span id="hist_{etudid}">{
|
||||||
+ "</span>"
|
get_note_history_menu(evaluation.id, etudid)
|
||||||
)
|
}</span>"""
|
||||||
else:
|
)
|
||||||
explanation = ""
|
|
||||||
explanation = e["absinfo"] + explanation
|
explanation = e["absinfo"] + explanation
|
||||||
|
|
||||||
# Lien modif decision de jury:
|
# Lien modif decision de jury:
|
||||||
explanation += '<span id="jurylink_%s" class="jurylink"></span>' % etudid
|
explanation += f'<span id="jurylink_{etudid}" class="jurylink"></span>'
|
||||||
|
|
||||||
# Valeur actuelle du champ:
|
# Valeur actuelle du champ:
|
||||||
initvalues["note_" + str(etudid)] = e["val"]
|
initvalues["note_" + str(etudid)] = e["val"]
|
||||||
@ -1330,7 +1316,7 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
|
|||||||
H.append(tf.getform()) # check and init
|
H.append(tf.getform()) # check and init
|
||||||
H.append(
|
H.append(
|
||||||
f"""<a href="{url_for("notes.moduleimpl_status", scodoc_dept=g.scodoc_dept,
|
f"""<a href="{url_for("notes.moduleimpl_status", scodoc_dept=g.scodoc_dept,
|
||||||
moduleimpl_id=M["moduleimpl_id"])
|
moduleimpl_id=modimpl.id)
|
||||||
}" class="btn btn-primary">Terminer</a>
|
}" class="btn btn-primary">Terminer</a>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
@ -1345,7 +1331,7 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
|
|||||||
Mettre les notes manquantes à
|
Mettre les notes manquantes à
|
||||||
<input type="text" size="5" name="value"/>
|
<input type="text" size="5" name="value"/>
|
||||||
<input type="submit" value="OK"/>
|
<input type="submit" value="OK"/>
|
||||||
<input type="hidden" name="evaluation_id" value="{evaluation_id}"/>
|
<input type="hidden" name="evaluation_id" value="{evaluation.id}"/>
|
||||||
<input class="group_ids_str" type="hidden" name="group_ids_str" value="{
|
<input class="group_ids_str" type="hidden" name="group_ids_str" value="{
|
||||||
",".join([str(x) for x in groups_infos.group_ids])
|
",".join([str(x) for x in groups_infos.group_ids])
|
||||||
}"/>
|
}"/>
|
||||||
@ -1362,50 +1348,56 @@ def _form_saisie_notes(E, M, groups_infos, destination=""):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def save_note(etudid=None, evaluation_id=None, value=None, comment=""):
|
def save_notes(
|
||||||
"""Enregistre une note (ajax)"""
|
evaluation: Evaluation, notes: list[tuple[(int, float)]], comment: str = ""
|
||||||
authuser = current_user
|
) -> dict:
|
||||||
log(
|
"""Enregistre une liste de notes.
|
||||||
"save_note: evaluation_id=%s etudid=%s uid=%s value=%s"
|
Vérifie que les étudiants sont bien inscrits à ce module, et que l'on a le droit.
|
||||||
% (evaluation_id, etudid, authuser, value)
|
Result: dict avec
|
||||||
)
|
"""
|
||||||
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
log(f"save_note: evaluation_id={evaluation.id} uid={current_user} notes={notes}")
|
||||||
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
status_url = url_for(
|
||||||
Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
|
|
||||||
Mod["url"] = url_for(
|
|
||||||
"notes.moduleimpl_status",
|
"notes.moduleimpl_status",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
moduleimpl_id=M["moduleimpl_id"],
|
moduleimpl_id=evaluation.moduleimpl_id,
|
||||||
_external=True,
|
_external=True,
|
||||||
)
|
)
|
||||||
result = {"nbchanged": 0} # JSON
|
|
||||||
# Check access: admin, respformation, or responsable_id
|
# Check access: admin, respformation, or responsable_id
|
||||||
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]):
|
if not sco_permissions_check.can_edit_notes(current_user, evaluation.moduleimpl_id):
|
||||||
result["status"] = "unauthorized"
|
return json_error(403, "modification notes non autorisee pour cet utilisateur")
|
||||||
|
#
|
||||||
|
valid_notes, _, _, _, _ = _check_notes(notes, evaluation)
|
||||||
|
if valid_notes:
|
||||||
|
etudids_changed, _, etudids_with_decision = notes_add(
|
||||||
|
current_user, evaluation.id, valid_notes, comment=comment, do_it=True
|
||||||
|
)
|
||||||
|
ScolarNews.add(
|
||||||
|
typ=ScolarNews.NEWS_NOTE,
|
||||||
|
obj=evaluation.moduleimpl_id,
|
||||||
|
text=f"""Chargement notes dans <a href="{status_url}">{
|
||||||
|
evaluation.moduleimpl.module.titre or evaluation.moduleimpl.module.code}</a>""",
|
||||||
|
url=status_url,
|
||||||
|
max_frequency=30 * 60, # 30 minutes
|
||||||
|
)
|
||||||
|
result = {
|
||||||
|
"etudids_with_decision": etudids_with_decision,
|
||||||
|
"etudids_changed": etudids_changed,
|
||||||
|
"history_menu": {
|
||||||
|
etudid: get_note_history_menu(evaluation.id, etudid)
|
||||||
|
for etudid in etudids_changed
|
||||||
|
},
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
L, _, _, _, _ = _check_notes([(etudid, value)], E, Mod)
|
result = {
|
||||||
if L:
|
"etudids_changed": [],
|
||||||
nbchanged, _, existing_decisions = notes_add(
|
"etudids_with_decision": [],
|
||||||
authuser, evaluation_id, L, comment=comment, do_it=True
|
"history_menu": [],
|
||||||
)
|
}
|
||||||
ScolarNews.add(
|
|
||||||
typ=ScolarNews.NEWS_NOTE,
|
return result
|
||||||
obj=M["moduleimpl_id"],
|
|
||||||
text='Chargement notes dans <a href="%(url)s">%(titre)s</a>' % Mod,
|
|
||||||
url=Mod["url"],
|
|
||||||
max_frequency=30 * 60, # 30 minutes
|
|
||||||
)
|
|
||||||
result["nbchanged"] = nbchanged
|
|
||||||
result["existing_decisions"] = existing_decisions
|
|
||||||
if nbchanged > 0:
|
|
||||||
result["history_menu"] = get_note_history_menu(evaluation_id, etudid)
|
|
||||||
else:
|
|
||||||
result["history_menu"] = "" # no update needed
|
|
||||||
result["status"] = "ok"
|
|
||||||
return scu.sendJSON(result)
|
|
||||||
|
|
||||||
|
|
||||||
def get_note_history_menu(evaluation_id, etudid):
|
def get_note_history_menu(evaluation_id: int, etudid: int) -> str:
|
||||||
"""Menu HTML historique de la note"""
|
"""Menu HTML historique de la note"""
|
||||||
history = sco_undo_notes.get_note_history(evaluation_id, etudid)
|
history = sco_undo_notes.get_note_history(evaluation_id, etudid)
|
||||||
if not history:
|
if not history:
|
||||||
|
@ -354,6 +354,10 @@ body.editionActivated .filtres .nonEditable .move {
|
|||||||
display: initial;
|
display: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.groupe:has(.etudiants:empty) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* .filtres .unselect {
|
/* .filtres .unselect {
|
||||||
background: rgba(0, 153, 204, 0.5) !important;
|
background: rgba(0, 153, 204, 0.5) !important;
|
||||||
} */
|
} */
|
||||||
|
@ -3173,6 +3173,19 @@ li.tf-msg {
|
|||||||
/* EMO_WARNING, "⚠️" */
|
/* EMO_WARNING, "⚠️" */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.error {
|
||||||
|
font-weight: bold;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.error::before {
|
||||||
|
content: "\2049 \fe0f";
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark {
|
||||||
|
padding-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.infop {
|
.infop {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
65
app/static/js/etud_autocomplete.js
Normal file
65
app/static/js/etud_autocomplete.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
|
||||||
|
// Mécanisme d'auto-complétion (choix) d'un étudiant
|
||||||
|
// Il faut un champs #etudiant (text input) et à coté un champ hidden etudid qui sera rempli.
|
||||||
|
// utilise autoComplete.js, source https://tarekraafat.github.io/autoComplete.js
|
||||||
|
// EV 2023-06-01
|
||||||
|
|
||||||
|
function etud_autocomplete_config(with_dept = false) {
|
||||||
|
return {
|
||||||
|
selector: "#etudiant",
|
||||||
|
placeHolder: "Nom...",
|
||||||
|
threshold: 3,
|
||||||
|
data: {
|
||||||
|
src: async (query) => {
|
||||||
|
try {
|
||||||
|
// Fetch Data from external Source
|
||||||
|
const source = await fetch(`/ScoDoc/api/etudiants/name/${query}`);
|
||||||
|
// Data should be an array of `Objects` or `Strings`
|
||||||
|
const data = await source.json();
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Data source 'Object' key to be searched
|
||||||
|
keys: ["nom"]
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
input: {
|
||||||
|
selection: (event) => {
|
||||||
|
const prenom = sco_capitalize(event.detail.selection.value.prenom);
|
||||||
|
const selection = with_dept ? `${event.detail.selection.value.nom} ${prenom} (${event.detail.selection.value.dept_acronym})` : `${event.detail.selection.value.nom} ${prenom}`;
|
||||||
|
// store etudid
|
||||||
|
const etudidField = document.getElementById('etudid');
|
||||||
|
etudidField.value = event.detail.selection.value.id;
|
||||||
|
autoCompleteJS.input.value = selection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resultsList: {
|
||||||
|
element: (list, data) => {
|
||||||
|
if (!data.results.length) {
|
||||||
|
// Create "No Results" message element
|
||||||
|
const message = document.createElement("div");
|
||||||
|
// Add class to the created element
|
||||||
|
message.setAttribute("class", "no_result");
|
||||||
|
// Add message text content
|
||||||
|
message.innerHTML = `<span>Pas de résultat pour "${data.query}"</span>`;
|
||||||
|
// Append message element to the results list
|
||||||
|
list.prepend(message);
|
||||||
|
// Efface l'etudid
|
||||||
|
const etudidField = document.getElementById('etudid');
|
||||||
|
etudidField.value = "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
noResults: true,
|
||||||
|
},
|
||||||
|
resultItem: {
|
||||||
|
highlight: true,
|
||||||
|
element: (item, data) => {
|
||||||
|
const prenom = sco_capitalize(data.value.prenom);
|
||||||
|
item.innerHTML += with_dept ? ` ${prenom} (${data.value.dept_acronym})` : ` ${prenom}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -1,132 +1,142 @@
|
|||||||
// Formulaire saisie des notes
|
// Formulaire saisie des notes
|
||||||
|
|
||||||
$().ready(function () {
|
$().ready(function () {
|
||||||
|
$("#formnotes .note").bind("blur", valid_note);
|
||||||
|
|
||||||
$("#formnotes .note").bind("blur", valid_note);
|
$("#formnotes input").bind("paste", paste_text);
|
||||||
|
$(".btn_masquer_DEM").bind("click", masquer_DEM);
|
||||||
$("#formnotes input").bind("paste", paste_text);
|
|
||||||
$(".btn_masquer_DEM").bind("click", masquer_DEM);
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function is_valid_note(v) {
|
function is_valid_note(v) {
|
||||||
if (!v)
|
if (!v) return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
var note_min = parseFloat($("#eval_note_min").text());
|
var note_min = parseFloat($("#eval_note_min").text());
|
||||||
var note_max = parseFloat($("#eval_note_max").text());
|
var note_max = parseFloat($("#eval_note_max").text());
|
||||||
|
|
||||||
if (!v.match("^-?[0-9]*.?[0-9]*$")) {
|
if (!v.match("^-?[0-9]*.?[0-9]*$")) {
|
||||||
return (v == "ABS") || (v == "EXC") || (v == "SUPR") || (v == "ATT") || (v == "DEM");
|
return v == "ABS" || v == "EXC" || v == "SUPR" || v == "ATT" || v == "DEM";
|
||||||
} else {
|
} else {
|
||||||
var x = parseFloat(v);
|
var x = parseFloat(v);
|
||||||
return (x >= note_min) && (x <= note_max);
|
return x >= note_min && x <= note_max;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function valid_note(e) {
|
function valid_note(e) {
|
||||||
var v = this.value.trim().toUpperCase().replace(",", ".");
|
var v = this.value.trim().toUpperCase().replace(",", ".");
|
||||||
if (is_valid_note(v)) {
|
if (is_valid_note(v)) {
|
||||||
if (v && (v != $(this).attr('data-last-saved-value'))) {
|
if (v && v != $(this).attr("data-last-saved-value")) {
|
||||||
this.className = "note_valid_new";
|
this.className = "note_valid_new";
|
||||||
var etudid = $(this).attr('data-etudid');
|
const etudid = parseInt($(this).attr("data-etudid"));
|
||||||
save_note(this, v, etudid);
|
save_note(this, v, etudid);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Saisie invalide */
|
|
||||||
this.className = "note_invalid";
|
|
||||||
sco_message("valeur invalide ou hors barème");
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
/* Saisie invalide */
|
||||||
|
this.className = "note_invalid";
|
||||||
|
sco_message("valeur invalide ou hors barème");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function save_note(elem, v, etudid) {
|
async function save_note(elem, v, etudid) {
|
||||||
var evaluation_id = $("#formnotes_evaluation_id").attr("value");
|
let evaluation_id = $("#formnotes_evaluation_id").attr("value");
|
||||||
var formsemestre_id = $("#formnotes_formsemestre_id").attr("value");
|
let formsemestre_id = $("#formnotes_formsemestre_id").attr("value");
|
||||||
$('#sco_msg').html("en cours...").show();
|
$("#sco_msg").html("en cours...").show();
|
||||||
$.post(SCO_URL + '/Notes/save_note',
|
try {
|
||||||
{
|
const response = await fetch(
|
||||||
'etudid': etudid,
|
SCO_URL + "/../api/evaluation/" + evaluation_id + "/notes/set",
|
||||||
'evaluation_id': evaluation_id,
|
{
|
||||||
'value': v,
|
method: "POST",
|
||||||
'comment': document.getElementById('formnotes_comment').value
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
function (result) {
|
body: JSON.stringify({
|
||||||
if (result['nbchanged'] > 0) {
|
notes: [[etudid, v]],
|
||||||
sco_message("enregistré");
|
comment: document.getElementById("formnotes_comment").value,
|
||||||
elem.className = "note_saved";
|
}),
|
||||||
// il y avait une decision de jury ?
|
}
|
||||||
if (result.existing_decisions[0] == etudid) {
|
|
||||||
if (v != $(elem).attr('data-orig-value')) {
|
|
||||||
$("#jurylink_" + etudid).html('<a href="formsemestre_validation_etud_form?formsemestre_id=' + formsemestre_id + '&etudid=' + etudid + '">mettre à jour décision de jury</a>');
|
|
||||||
} else {
|
|
||||||
$("#jurylink_" + etudid).html('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// mise a jour menu historique
|
|
||||||
if (result['history_menu']) {
|
|
||||||
$("#hist_" + etudid).html(result['history_menu']);
|
|
||||||
}
|
|
||||||
$(elem).attr('data-last-saved-value', v);
|
|
||||||
} else {
|
|
||||||
$('#sco_msg').html("").show();
|
|
||||||
sco_message("valeur non enregistrée");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
if (!response.ok) {
|
||||||
|
sco_message("Erreur: valeur non enregistrée");
|
||||||
|
} else {
|
||||||
|
const data = await response.json();
|
||||||
|
$("#sco_msg").hide();
|
||||||
|
if (data.etudids_changed.length > 0) {
|
||||||
|
sco_message("enregistré");
|
||||||
|
elem.className = "note_saved";
|
||||||
|
// Il y avait une decision de jury ?
|
||||||
|
if (data.etudids_with_decision.includes(etudid)) {
|
||||||
|
if (v != $(elem).attr("data-orig-value")) {
|
||||||
|
$("#jurylink_" + etudid).html(
|
||||||
|
'<a href="formsemestre_validation_etud_form?formsemestre_id=' +
|
||||||
|
formsemestre_id +
|
||||||
|
"&etudid=" +
|
||||||
|
etudid +
|
||||||
|
'">mettre à jour décision de jury</a>'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$("#jurylink_" + etudid).html("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Mise à jour menu historique
|
||||||
|
if (data.history_menu[etudid]) {
|
||||||
|
$("#hist_" + etudid).html(data.history_menu[etudid]);
|
||||||
|
}
|
||||||
|
$(elem).attr("data-last-saved-value", v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fetch error:", error);
|
||||||
|
sco_message("Erreur réseau: valeur non enregistrée");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function change_history(e) {
|
function change_history(e) {
|
||||||
var opt = e.selectedOptions[0];
|
let opt = e.selectedOptions[0];
|
||||||
var val = $(opt).attr("data-note");
|
let val = $(opt).attr("data-note");
|
||||||
var etudid = $(e).attr('data-etudid');
|
const etudid = parseInt($(e).attr("data-etudid"));
|
||||||
// le input associé a ce menu:
|
// le input associé a ce menu:
|
||||||
var input_elem = e.parentElement.parentElement.parentElement.childNodes[0];
|
let input_elem = e.parentElement.parentElement.parentElement.childNodes[0];
|
||||||
input_elem.value = val;
|
input_elem.value = val;
|
||||||
save_note(input_elem, val, etudid);
|
save_note(input_elem, val, etudid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contribution S.L.: copier/coller des notes
|
// Contribution S.L.: copier/coller des notes
|
||||||
|
|
||||||
|
|
||||||
function paste_text(e) {
|
function paste_text(e) {
|
||||||
var event = e.originalEvent;
|
var event = e.originalEvent;
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
var clipb = e.originalEvent.clipboardData;
|
var clipb = e.originalEvent.clipboardData;
|
||||||
var data = clipb.getData('Text');
|
var data = clipb.getData("Text");
|
||||||
var list = data.split(/\r\n|\r|\n|\t| /g);
|
var list = data.split(/\r\n|\r|\n|\t| /g);
|
||||||
var currentInput = event.currentTarget;
|
var currentInput = event.currentTarget;
|
||||||
var masquerDEM = document.querySelector("body").classList.contains("masquer_DEM");
|
var masquerDEM = document
|
||||||
|
.querySelector("body")
|
||||||
|
.classList.contains("masquer_DEM");
|
||||||
|
|
||||||
for (var i = 0; i < list.length; i++) {
|
for (var i = 0; i < list.length; i++) {
|
||||||
currentInput.value = list[i];
|
currentInput.value = list[i];
|
||||||
var evt = document.createEvent("HTMLEvents");
|
var evt = document.createEvent("HTMLEvents");
|
||||||
evt.initEvent("blur", false, true);
|
evt.initEvent("blur", false, true);
|
||||||
currentInput.dispatchEvent(evt);
|
currentInput.dispatchEvent(evt);
|
||||||
var sibbling = currentInput.parentElement.parentElement.nextElementSibling;
|
var sibbling = currentInput.parentElement.parentElement.nextElementSibling;
|
||||||
while (
|
while (
|
||||||
sibbling &&
|
sibbling &&
|
||||||
(
|
(sibbling.style.display == "none" ||
|
||||||
sibbling.style.display == "none" ||
|
(masquerDEM && sibbling.classList.contains("etud_dem")))
|
||||||
(
|
) {
|
||||||
masquerDEM && sibbling.classList.contains("etud_dem")
|
sibbling = sibbling.nextElementSibling;
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
sibbling = sibbling.nextElementSibling;
|
|
||||||
}
|
|
||||||
if (sibbling) {
|
|
||||||
currentInput = sibbling.querySelector("input");
|
|
||||||
if (!currentInput) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (sibbling) {
|
||||||
|
currentInput = sibbling.querySelector("input");
|
||||||
|
if (!currentInput) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function masquer_DEM() {
|
function masquer_DEM() {
|
||||||
document.querySelector("body").classList.toggle("masquer_DEM");
|
document.querySelector("body").classList.toggle("masquer_DEM");
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,10 @@ $(function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function sco_capitalize(string) {
|
||||||
|
return string[0].toUpperCase() + string.slice(1).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
// Affiche un message transitoire (duration milliseconds, 0 means infinity)
|
// Affiche un message transitoire (duration milliseconds, 0 means infinity)
|
||||||
function sco_message(msg, className = "message_custom", duration = 0) {
|
function sco_message(msg, className = "message_custom", duration = 0) {
|
||||||
var div = document.createElement("div");
|
var div = document.createElement("div");
|
||||||
|
1
app/static/libjs/autoComplete.js-10.2.7/dist/autoComplete.min.js
vendored
Normal file
1
app/static/libjs/autoComplete.js-10.2.7/dist/autoComplete.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
92
app/static/libjs/autoComplete.js-10.2.7/dist/css/autoComplete.01.css
vendored
Normal file
92
app/static/libjs/autoComplete.js-10.2.7/dist/css/autoComplete.01.css
vendored
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
.autoComplete_wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > input {
|
||||||
|
width: 370px;
|
||||||
|
height: 40px;
|
||||||
|
padding-left: 20px;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: rgba(123, 123, 123, 1);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
background-color: #f1f3f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > input::placeholder {
|
||||||
|
color: rgba(123, 123, 123, 0.5);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul {
|
||||||
|
position: absolute;
|
||||||
|
max-height: 226px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0.5rem 0 0 0;
|
||||||
|
border-radius: 0.6rem;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 3px 6px rgba(149, 157, 165, 0.15);
|
||||||
|
border: 1px solid rgba(33, 33, 33, 0.07);
|
||||||
|
z-index: 1000;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul[hidden],
|
||||||
|
.autoComplete_wrapper > ul:empty {
|
||||||
|
display: block;
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li {
|
||||||
|
margin: 0.3rem;
|
||||||
|
padding: 0.3rem 0.5rem;
|
||||||
|
list-style: none;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #212121;
|
||||||
|
transition: all 0.1s ease-in-out;
|
||||||
|
border-radius: 0.35rem;
|
||||||
|
background-color: rgba(255, 255, 255, 1);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li::selection {
|
||||||
|
color: rgba(#ffffff, 0);
|
||||||
|
background-color: rgba(#ffffff, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: rgba(123, 123, 123, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li mark {
|
||||||
|
background-color: transparent;
|
||||||
|
color: rgba(255, 122, 122, 1);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li mark::selection {
|
||||||
|
color: rgba(#ffffff, 0);
|
||||||
|
background-color: rgba(#ffffff, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li[aria-selected="true"] {
|
||||||
|
background-color: rgba(123, 123, 123, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 600px) {
|
||||||
|
.autoComplete_wrapper > input {
|
||||||
|
width: 18rem;
|
||||||
|
}
|
||||||
|
}
|
82
app/static/libjs/autoComplete.js-10.2.7/dist/css/autoComplete.02.css
vendored
Normal file
82
app/static/libjs/autoComplete.js-10.2.7/dist/css/autoComplete.02.css
vendored
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
.autoComplete_wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > input {
|
||||||
|
width: 370px;
|
||||||
|
height: 40px;
|
||||||
|
padding-left: 10px;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: rgb(116, 116, 116);
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid rgba(33, 33, 33, 0.2);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > input::placeholder {
|
||||||
|
color: rgba(123, 123, 123, 0.5);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul {
|
||||||
|
position: absolute;
|
||||||
|
max-height: 226px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0.5rem 0 0 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid rgba(33, 33, 33, 0.1);
|
||||||
|
z-index: 1000;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li {
|
||||||
|
padding: 10px 20px;
|
||||||
|
list-style: none;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #212121;
|
||||||
|
transition: all 0.1s ease-in-out;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: rgba(255, 255, 255, 1);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li::selection {
|
||||||
|
color: rgba(#ffffff, 0);
|
||||||
|
background-color: rgba(#ffffff, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: rgba(123, 123, 123, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li mark {
|
||||||
|
background-color: transparent;
|
||||||
|
color: rgba(255, 122, 122, 1);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li mark::selection {
|
||||||
|
color: rgba(#ffffff, 0);
|
||||||
|
background-color: rgba(#ffffff, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li[aria-selected="true"] {
|
||||||
|
background-color: rgba(123, 123, 123, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 600px) {
|
||||||
|
.autoComplete_wrapper > input {
|
||||||
|
width: 18rem;
|
||||||
|
}
|
||||||
|
}
|
128
app/static/libjs/autoComplete.js-10.2.7/dist/css/autoComplete.css
vendored
Normal file
128
app/static/libjs/autoComplete.js-10.2.7/dist/css/autoComplete.css
vendored
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
.autoComplete_wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > input {
|
||||||
|
height: 3rem;
|
||||||
|
width: 370px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 2rem 0 3.2rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
font-size: 1rem;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
color: rgba(255, 122, 122, 0.3);
|
||||||
|
outline: none;
|
||||||
|
border-radius: 10rem;
|
||||||
|
border: 0.05rem solid rgba(255, 122, 122, 0.5);
|
||||||
|
background-image: url(./images/search.svg);
|
||||||
|
background-size: 1.4rem;
|
||||||
|
background-position: left 1.05rem top 0.8rem;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-origin: border-box;
|
||||||
|
background-color: #fff;
|
||||||
|
transition: all 0.4s ease;
|
||||||
|
-webkit-transition: all -webkit-transform 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > input::placeholder {
|
||||||
|
color: rgba(255, 122, 122, 0.5);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
-webkit-transition: all -webkit-transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > input:hover::placeholder {
|
||||||
|
color: rgba(255, 122, 122, 0.6);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
-webkit-transition: all -webkit-transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > input:focus::placeholder {
|
||||||
|
padding: 0.1rem 0.6rem;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: rgba(255, 122, 122, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > input:focus::selection {
|
||||||
|
background-color: rgba(255, 122, 122, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > input::selection {
|
||||||
|
background-color: rgba(255, 122, 122, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > input:hover {
|
||||||
|
color: rgba(255, 122, 122, 0.8);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
-webkit-transition: all -webkit-transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > input:focus {
|
||||||
|
color: rgba(255, 122, 122, 1);
|
||||||
|
border: 0.06rem solid rgba(255, 122, 122, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul {
|
||||||
|
position: absolute;
|
||||||
|
max-height: 226px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
box-sizing: border-box;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: 0.5rem 0 0 0;
|
||||||
|
padding: 0;
|
||||||
|
z-index: 1;
|
||||||
|
list-style: none;
|
||||||
|
border-radius: 0.6rem;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid rgba(33, 33, 33, 0.07);
|
||||||
|
box-shadow: 0 3px 6px rgba(149, 157, 165, 0.15);
|
||||||
|
outline: none;
|
||||||
|
transition: opacity 0.15s ease-in-out;
|
||||||
|
-moz-transition: opacity 0.15s ease-in-out;
|
||||||
|
-webkit-transition: opacity 0.15s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul[hidden],
|
||||||
|
.autoComplete_wrapper > ul:empty {
|
||||||
|
display: block;
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li {
|
||||||
|
margin: 0.3rem;
|
||||||
|
padding: 0.3rem 0.5rem;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #212121;
|
||||||
|
border-radius: 0.35rem;
|
||||||
|
background-color: rgba(255, 255, 255, 1);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li mark {
|
||||||
|
background-color: transparent;
|
||||||
|
color: rgba(255, 122, 122, 1);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: rgba(255, 122, 122, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoComplete_wrapper > ul > li[aria-selected="true"] {
|
||||||
|
background-color: rgba(255, 122, 122, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 600px) {
|
||||||
|
.autoComplete_wrapper > input {
|
||||||
|
width: 18rem;
|
||||||
|
}
|
||||||
|
}
|
8
app/static/libjs/autoComplete.js-10.2.7/dist/css/images/search.svg
vendored
Normal file
8
app/static/libjs/autoComplete.js-10.2.7/dist/css/images/search.svg
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" focusable="false" x="0px" y="0px" width="30" height="30" viewBox="0 0 171 171" style=" fill:#000000;">
|
||||||
|
<g fill="none" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal">
|
||||||
|
<path d="M0,171.99609v-171.99609h171.99609v171.99609z" fill="none"></path>
|
||||||
|
<g fill="#ff7a7a">
|
||||||
|
<path d="M74.1,17.1c-31.41272,0 -57,25.58728 -57,57c0,31.41272 25.58728,57 57,57c13.6601,0 26.20509,-4.85078 36.03692,-12.90293l34.03301,34.03301c1.42965,1.48907 3.55262,2.08891 5.55014,1.56818c1.99752,-0.52073 3.55746,-2.08067 4.07819,-4.07819c0.52073,-1.99752 -0.0791,-4.12049 -1.56818,-5.55014l-34.03301,-34.03301c8.05215,-9.83182 12.90293,-22.37682 12.90293,-36.03692c0,-31.41272 -25.58728,-57 -57,-57zM74.1,28.5c25.2517,0 45.6,20.3483 45.6,45.6c0,25.2517 -20.3483,45.6 -45.6,45.6c-25.2517,0 -45.6,-20.3483 -45.6,-45.6c0,-25.2517 20.3483,-45.6 45.6,-45.6z"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -479,17 +479,18 @@ class TableRecap(tb.Table):
|
|||||||
for row in self.rows:
|
for row in self.rows:
|
||||||
etud = row.etud
|
etud = row.etud
|
||||||
admission = etud.admission.first()
|
admission = etud.admission.first()
|
||||||
first = True
|
if admission:
|
||||||
for cid, title in fields.items():
|
first = True
|
||||||
cell = row.add_cell(
|
for cid, title in fields.items():
|
||||||
cid,
|
cell = row.add_cell(
|
||||||
title,
|
cid,
|
||||||
getattr(admission, cid) or "",
|
title,
|
||||||
"admission",
|
getattr(admission, cid) or "",
|
||||||
)
|
"admission",
|
||||||
if first:
|
)
|
||||||
cell.classes.append("admission_first")
|
if first:
|
||||||
first = False
|
cell.classes.append("admission_first")
|
||||||
|
first = False
|
||||||
|
|
||||||
def add_cursus(self):
|
def add_cursus(self):
|
||||||
"""Ajoute colonne avec code cursus, eg 'S1 S2 S1'
|
"""Ajoute colonne avec code cursus, eg 'S1 S2 S1'
|
||||||
|
@ -85,7 +85,15 @@
|
|||||||
{{ super() }}
|
{{ super() }}
|
||||||
{{ moment.include_moment() }}
|
{{ moment.include_moment() }}
|
||||||
{{ moment.lang(g.locale) }}
|
{{ moment.lang(g.locale) }}
|
||||||
|
<script src="{{scu.STATIC_DIR}}/libjs/menu.js"></script>
|
||||||
|
<script src="{{scu.STATIC_DIR}}/libjs/bubble.js"></script>
|
||||||
|
<script src="{{scu.STATIC_DIR}}/jQuery/jquery.js"></script>
|
||||||
|
<script src="{{scu.STATIC_DIR}}/jQuery/jquery-migrate-1.2.0.min.js"></script>
|
||||||
|
<script src="{{scu.STATIC_DIR}}/libjs/jquery.field.min.js"></script>
|
||||||
|
<script src="{{scu.STATIC_DIR}}/libjs/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min.js"></script>
|
||||||
|
<script src="{{scu.STATIC_DIR}}/libjs/qtip/jquery.qtip-3.0.3.min.js"></script>
|
||||||
|
<script src="{{scu.STATIC_DIR}}/js/scodoc.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
var SCO_URL = "{{ url_for('scolar.index_html', scodoc_dept=g.scodoc_dept)[:-11] }}";
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -4,33 +4,27 @@
|
|||||||
|
|
||||||
{% block styles %}
|
{% block styles %}
|
||||||
{{super()}}
|
{{super()}}
|
||||||
<link type="text/css" rel="stylesheet" href="/ScoDoc/static/css/autosuggest_inquisitor.css" />
|
<link rel="stylesheet" href="/ScoDoc/static/libjs/autoComplete.js-10.2.7/dist/css/autoComplete.02.css">
|
||||||
<script src="/ScoDoc/static/libjs/AutoSuggest.js"></script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block app_content %}
|
{% block app_content %}
|
||||||
<h1>{{ title }}</h1>
|
<h1>{{ title }}</h1>
|
||||||
<br>
|
<br>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-3">
|
||||||
<p>
|
<p>
|
||||||
(*) champs requis
|
(*) champs requis
|
||||||
</p>
|
</p>
|
||||||
{{ wtf.quick_form(form, novalidate=True) }}
|
{{ wtf.quick_form(form, novalidate=True) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{{super()}}
|
||||||
|
<script src="/ScoDoc/static/libjs/autoComplete.js-10.2.7/dist/autoComplete.min.js"></script>
|
||||||
|
<script src="/ScoDoc/static/js/etud_autocomplete.js"></script>
|
||||||
<script>
|
<script>
|
||||||
window.onload = function (e) {
|
const autoCompleteJS = new autoComplete(etud_autocomplete_config(with_dept = true));
|
||||||
var etudiants_options = {
|
|
||||||
script: "/ScoDoc/entreprises/etudiants?",
|
|
||||||
varname: "term",
|
|
||||||
json: true,
|
|
||||||
noresults: "Valeur invalide !",
|
|
||||||
minchars: 2,
|
|
||||||
timeout: 60000
|
|
||||||
};
|
|
||||||
var as_etudiants = new bsn.AutoSuggest('etudiant', etudiants_options);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<h2>Erreur !</h2>
|
<h2>Erreur !</h2>
|
||||||
|
|
||||||
{{ exc }}
|
{{ exc | safe }}
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{% if g.scodoc_dept %}
|
{% if g.scodoc_dept %}
|
||||||
|
@ -496,7 +496,7 @@ sco_publish(
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@scodoc7func
|
@scodoc7func
|
||||||
def formation_table_recap(formation_id, format="html"):
|
def formation_table_recap(formation_id, format="html"):
|
||||||
return sco_formation_recap.formation_table_recap(formation_id, format="html")
|
return sco_formation_recap.formation_table_recap(formation_id, format=format)
|
||||||
|
|
||||||
|
|
||||||
sco_publish(
|
sco_publish(
|
||||||
@ -1874,12 +1874,6 @@ sco_publish(
|
|||||||
Permission.ScoEnsView,
|
Permission.ScoEnsView,
|
||||||
)
|
)
|
||||||
sco_publish("/saisie_notes", sco_saisie_notes.saisie_notes, Permission.ScoEnsView)
|
sco_publish("/saisie_notes", sco_saisie_notes.saisie_notes, Permission.ScoEnsView)
|
||||||
sco_publish(
|
|
||||||
"/save_note",
|
|
||||||
sco_saisie_notes.save_note,
|
|
||||||
Permission.ScoEnsView,
|
|
||||||
methods=["GET", "POST"],
|
|
||||||
)
|
|
||||||
sco_publish(
|
sco_publish(
|
||||||
"/do_evaluation_set_missing",
|
"/do_evaluation_set_missing",
|
||||||
sco_saisie_notes.do_evaluation_set_missing,
|
sco_saisie_notes.do_evaluation_set_missing,
|
||||||
|
@ -1306,6 +1306,8 @@ def _do_cancel_dem_or_def(
|
|||||||
db.session.delete(event)
|
db.session.delete(event)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre_id)
|
||||||
|
|
||||||
flash(f"{operation_name} annulée.")
|
flash(f"{operation_name} annulée.")
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||||
@ -1755,9 +1757,7 @@ def _etudident_create_or_edit_form(edit):
|
|||||||
# Inval semesters with this student:
|
# Inval semesters with this student:
|
||||||
to_inval = [s["formsemestre_id"] for s in etud["sems"]]
|
to_inval = [s["formsemestre_id"] for s in etud["sems"]]
|
||||||
for formsemestre_id in to_inval:
|
for formsemestre_id in to_inval:
|
||||||
sco_cache.invalidate_formsemestre(
|
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre_id)
|
||||||
formsemestre_id=formsemestre_id
|
|
||||||
) # > etudident_create_or_edit
|
|
||||||
#
|
#
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||||
@ -1833,7 +1833,7 @@ def etudident_delete(etudid, dialog_confirmed=False):
|
|||||||
# Inval semestres où il était inscrit:
|
# Inval semestres où il était inscrit:
|
||||||
to_inval = [s["formsemestre_id"] for s in etud["sems"]]
|
to_inval = [s["formsemestre_id"] for s in etud["sems"]]
|
||||||
for formsemestre_id in to_inval:
|
for formsemestre_id in to_inval:
|
||||||
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre_id) # >
|
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre_id)
|
||||||
flash("Étudiant supprimé !")
|
flash("Étudiant supprimé !")
|
||||||
return flask.redirect(scu.ScoURL())
|
return flask.redirect(scu.ScoURL())
|
||||||
|
|
||||||
|
77
migrations/versions/d84bc592584e_extension_unaccent.py
Normal file
77
migrations/versions/d84bc592584e_extension_unaccent.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
"""Extension unaccent
|
||||||
|
|
||||||
|
Revision ID: d84bc592584e
|
||||||
|
Revises: b8df1b913c79
|
||||||
|
Create Date: 2023-06-01 13:46:52.927951
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.orm import sessionmaker # added by ev
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = "d84bc592584e"
|
||||||
|
down_revision = "b8df1b913c79"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
Session = sessionmaker()
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
|
||||||
|
bind = op.get_bind()
|
||||||
|
session = Session(bind=bind)
|
||||||
|
# Ajout extension pour recherches sans accents:
|
||||||
|
session.execute(sa.text("""CREATE EXTENSION IF NOT EXISTS "unaccent";"""))
|
||||||
|
|
||||||
|
# Clé étrangère sur identite
|
||||||
|
session.execute(
|
||||||
|
sa.text(
|
||||||
|
"""UPDATE are_stages_apprentissages
|
||||||
|
SET etudid = NULL
|
||||||
|
WHERE are_stages_apprentissages.etudid NOT IN (
|
||||||
|
SELECT id
|
||||||
|
FROM Identite
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
with op.batch_alter_table("are_stages_apprentissages", schema=None) as batch_op:
|
||||||
|
batch_op.create_foreign_key(
|
||||||
|
"are_stages_apprentissages_etudid_fkey",
|
||||||
|
"identite",
|
||||||
|
["etudid"],
|
||||||
|
["id"],
|
||||||
|
ondelete="CASCADE",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Les montants de taxe en float:
|
||||||
|
with op.batch_alter_table("are_taxe_apprentissage", schema=None) as batch_op:
|
||||||
|
batch_op.alter_column(
|
||||||
|
"montant",
|
||||||
|
existing_type=sa.INTEGER(),
|
||||||
|
type_=sa.Float(),
|
||||||
|
existing_nullable=True,
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table("are_taxe_apprentissage", schema=None) as batch_op:
|
||||||
|
batch_op.alter_column(
|
||||||
|
"montant",
|
||||||
|
existing_type=sa.Float(),
|
||||||
|
type_=sa.INTEGER(),
|
||||||
|
existing_nullable=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
with op.batch_alter_table("are_stages_apprentissages", schema=None) as batch_op:
|
||||||
|
batch_op.drop_constraint(
|
||||||
|
"are_stages_apprentissages_etudid_fkey", type_="foreignkey"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
@ -1,7 +1,7 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
SCOVERSION = "9.4.77"
|
SCOVERSION = "9.4.82"
|
||||||
|
|
||||||
SCONAME = "ScoDoc"
|
SCONAME = "ScoDoc"
|
||||||
|
|
||||||
|
@ -224,6 +224,32 @@ def test_etudiants(api_headers):
|
|||||||
assert r.status_code == 404
|
assert r.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
|
def test_etudiants_by_name(api_headers):
|
||||||
|
"""
|
||||||
|
Route: /etudiants/name/<string:start>
|
||||||
|
"""
|
||||||
|
r = requests.get(
|
||||||
|
API_URL + "/etudiants/name/A",
|
||||||
|
headers=api_headers,
|
||||||
|
verify=CHECK_CERTIFICATE,
|
||||||
|
timeout=scu.SCO_TEST_API_TIMEOUT,
|
||||||
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
|
etuds = r.json()
|
||||||
|
assert etuds == []
|
||||||
|
#
|
||||||
|
r = requests.get(
|
||||||
|
API_URL + "/etudiants/name/REG",
|
||||||
|
headers=api_headers,
|
||||||
|
verify=CHECK_CERTIFICATE,
|
||||||
|
timeout=scu.SCO_TEST_API_TIMEOUT,
|
||||||
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
|
etuds = r.json()
|
||||||
|
assert len(etuds) == 1
|
||||||
|
assert etuds[0]["nom"] == "RÉGNIER"
|
||||||
|
|
||||||
|
|
||||||
def test_etudiant_formsemestres(api_headers):
|
def test_etudiant_formsemestres(api_headers):
|
||||||
"""
|
"""
|
||||||
Route: /etudiant/etudid/<etudid:int>/formsemestres
|
Route: /etudiant/etudid/<etudid:int>/formsemestres
|
||||||
|
@ -24,7 +24,7 @@ from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
|
|||||||
from tests.api.tools_test_api import (
|
from tests.api.tools_test_api import (
|
||||||
verify_fields,
|
verify_fields,
|
||||||
EVALUATIONS_FIELDS,
|
EVALUATIONS_FIELDS,
|
||||||
EVALUATION_FIELDS,
|
NOTES_FIELDS,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ def test_evaluations(api_headers):
|
|||||||
Route :
|
Route :
|
||||||
- /moduleimpl/<int:moduleimpl_id>/evaluations
|
- /moduleimpl/<int:moduleimpl_id>/evaluations
|
||||||
"""
|
"""
|
||||||
moduleimpl_id = 1
|
moduleimpl_id = 20
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
f"{API_URL}/moduleimpl/{moduleimpl_id}/evaluations",
|
f"{API_URL}/moduleimpl/{moduleimpl_id}/evaluations",
|
||||||
headers=api_headers,
|
headers=api_headers,
|
||||||
@ -44,6 +44,7 @@ def test_evaluations(api_headers):
|
|||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
list_eval = r.json()
|
list_eval = r.json()
|
||||||
|
assert list_eval
|
||||||
assert isinstance(list_eval, list)
|
assert isinstance(list_eval, list)
|
||||||
for eval in list_eval:
|
for eval in list_eval:
|
||||||
assert verify_fields(eval, EVALUATIONS_FIELDS) is True
|
assert verify_fields(eval, EVALUATIONS_FIELDS) is True
|
||||||
@ -63,16 +64,14 @@ def test_evaluations(api_headers):
|
|||||||
assert eval["moduleimpl_id"] == moduleimpl_id
|
assert eval["moduleimpl_id"] == moduleimpl_id
|
||||||
|
|
||||||
|
|
||||||
def test_evaluation_notes(
|
def test_evaluation_notes(api_headers):
|
||||||
api_headers,
|
|
||||||
): # XXX TODO changer la boucle pour parcourir le dict sans les indices
|
|
||||||
"""
|
"""
|
||||||
Test 'evaluation_notes'
|
Test 'evaluation_notes'
|
||||||
|
|
||||||
Route :
|
Route :
|
||||||
- /evaluation/<int:evaluation_id>/notes
|
- /evaluation/<int:evaluation_id>/notes
|
||||||
"""
|
"""
|
||||||
eval_id = 1
|
eval_id = 20
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
f"{API_URL}/evaluation/{eval_id}/notes",
|
f"{API_URL}/evaluation/{eval_id}/notes",
|
||||||
headers=api_headers,
|
headers=api_headers,
|
||||||
@ -81,14 +80,15 @@ def test_evaluation_notes(
|
|||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
eval_notes = r.json()
|
eval_notes = r.json()
|
||||||
for i in range(1, len(eval_notes)):
|
assert eval_notes
|
||||||
assert verify_fields(eval_notes[f"{i}"], EVALUATION_FIELDS)
|
for etudid, note in eval_notes.items():
|
||||||
assert isinstance(eval_notes[f"{i}"]["id"], int)
|
assert int(etudid) == note["etudid"]
|
||||||
assert isinstance(eval_notes[f"{i}"]["etudid"], int)
|
assert verify_fields(note, NOTES_FIELDS)
|
||||||
assert isinstance(eval_notes[f"{i}"]["evaluation_id"], int)
|
assert isinstance(note["etudid"], int)
|
||||||
assert isinstance(eval_notes[f"{i}"]["value"], float)
|
assert isinstance(note["evaluation_id"], int)
|
||||||
assert isinstance(eval_notes[f"{i}"]["comment"], str)
|
assert isinstance(note["value"], float)
|
||||||
assert isinstance(eval_notes[f"{i}"]["date"], str)
|
assert isinstance(note["comment"], str)
|
||||||
assert isinstance(eval_notes[f"{i}"]["uid"], int)
|
assert isinstance(note["date"], str)
|
||||||
|
assert isinstance(note["uid"], int)
|
||||||
|
|
||||||
assert eval_id == eval_notes[f"{i}"]["evaluation_id"]
|
assert eval_id == note["evaluation_id"]
|
||||||
|
@ -58,6 +58,7 @@ def test_permissions(api_headers):
|
|||||||
"nip": 1,
|
"nip": 1,
|
||||||
"partition_id": 1,
|
"partition_id": 1,
|
||||||
"role_name": "Ens",
|
"role_name": "Ens",
|
||||||
|
"start": "abc",
|
||||||
"uid": 1,
|
"uid": 1,
|
||||||
"version": "long",
|
"version": "long",
|
||||||
}
|
}
|
||||||
|
@ -568,8 +568,7 @@ EVALUATIONS_FIELDS = {
|
|||||||
"visi_bulletin",
|
"visi_bulletin",
|
||||||
}
|
}
|
||||||
|
|
||||||
EVALUATION_FIELDS = {
|
NOTES_FIELDS = {
|
||||||
"id",
|
|
||||||
"etudid",
|
"etudid",
|
||||||
"evaluation_id",
|
"evaluation_id",
|
||||||
"value",
|
"value",
|
||||||
|
554
tests/ressources/formations/scodoc_formation_BUT_INFO_v0514.xml
Normal file
554
tests/ressources/formations/scodoc_formation_BUT_INFO_v0514.xml
Normal file
@ -0,0 +1,554 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<formation acronyme="BUT INFO" titre_officiel="Bachelor Universitaire de Technologie" commentaire="Exemple incomplet. À partir du S4, seuls les parcours A et B ont des UEs." type_parcours="700" referentiel_competence_id="1" titre="BUT INFORMATIQUE" version="1" formation_code="FCOD49" code_specialite="" refcomp_version_orebut="2021-12-11 00:00:00" refcomp_specialite="INFO" refcomp_type_titre="B.U.T.">
|
||||||
|
<ue numero="0" code_apogee="V1INFU11" titre="Compétence 1 : Réaliser un développement d'application" coefficient="0.0" semestre_idx="1" coef_rcue="1.0" type="0" color="#b80004" ue_code="UCOD62" ects="5.0" is_external="0" acronyme="UE11" apc_niveau_libelle="Développer des applications informatiques simples " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="1">
|
||||||
|
<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="2" coef="12.0" />
|
||||||
|
<coefficients ue_reference="1" 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="1" coef="40.0" />
|
||||||
|
</module>
|
||||||
|
<module titre="Portfolio" abbrev="" code="P1" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="1" numero="700" code_apogee="" module_type="3">
|
||||||
|
<coefficients ue_reference="3" coef="1.0" />
|
||||||
|
<coefficients ue_reference="1" coef="1.0" />
|
||||||
|
<coefficients ue_reference="6" coef="1.0" />
|
||||||
|
<coefficients ue_reference="5" coef="1.0" />
|
||||||
|
<coefficients ue_reference="2" coef="1.0" />
|
||||||
|
<coefficients ue_reference="4" coef="1.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="1" code_apogee="V1INFU12" titre="Compétence 2 : Optimiser des applications informatiques" coefficient="0.0" semestre_idx="1" coef_rcue="1.0" type="0" color="#f97b3d" ue_code="UCOD61" ects="5.0" is_external="0" acronyme="UE12" apc_niveau_libelle="Appréhender et construire des algorithmes " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="2">
|
||||||
|
<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="2" coef="12.0" />
|
||||||
|
<coefficients ue_reference="1" 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="2" 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="1" coef="12.0" />
|
||||||
|
<coefficients ue_reference="5" coef="18.0" />
|
||||||
|
<coefficients ue_reference="6" 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="2" coef="15.0" />
|
||||||
|
<coefficients ue_reference="4" 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="2" coef="15.0" />
|
||||||
|
</module>
|
||||||
|
<module titre="Gestion de projet & 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="5" coef="27.0" />
|
||||||
|
<coefficients ue_reference="6" coef="11.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="2" code_apogee="V1INFU13" titre="Compétence 3 : Administrer des systèmes informatiques communicants" coefficient="0.0" semestre_idx="1" coef_rcue="1.0" type="0" color="#feb40b" ue_code="UCOD50" ects="5.0" is_external="0" acronyme="UE13" apc_niveau_libelle="Installer et configurer un poste de travail " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="3">
|
||||||
|
<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="3" 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="3" coef="21.0" />
|
||||||
|
<coefficients ue_reference="2" coef="6.0" />
|
||||||
|
</module>
|
||||||
|
<module titre="Introduction aux systèmes d’exploitation 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="3" coef="21.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="3" code_apogee="V1INFU14" titre="Compétence 4 : Gérer des données de l'information" coefficient="0.0" semestre_idx="1" coef_rcue="1.0" type="0" color="#80cb3f" ue_code="UCOD10" ects="5.0" is_external="0" acronyme="UE14" apc_niveau_libelle="Concevoir et mettre en place une base de données à partir d’un cahier des charges client " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="4">
|
||||||
|
<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="4" 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="4" coef="36.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="4" code_apogee="V1INFU15" titre="Compétence 5 : Conduire un projet" coefficient="0.0" semestre_idx="1" coef_rcue="1.0" type="0" color="#05162e" ue_code="UCOD15" ects="5.0" is_external="0" acronyme="UE15" apc_niveau_libelle="Identifier les besoins métiers des clients et des utilisateurs " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="5">
|
||||||
|
<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="5" 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="4" coef="6.0" />
|
||||||
|
<coefficients ue_reference="6" coef="11.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="5" code_apogee="V1INFU16" titre="Compétence 6 : Travailler dans une équipe informatique" coefficient="0.0" semestre_idx="1" coef_rcue="1.0" type="0" color="#548687" ue_code="UCOD16" ects="5.0" is_external="0" acronyme="UE16" apc_niveau_libelle="Identifier ses aptitudes pour travailler dans une équipe " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="6">
|
||||||
|
<matiere titre="Compétence 6 : Travailler dans une équipe informatique" numero="1">
|
||||||
|
<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="6" 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="3" coef="12.0" />
|
||||||
|
<coefficients ue_reference="1" coef="6.0" />
|
||||||
|
<coefficients ue_reference="6" 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="3" coef="6.0" />
|
||||||
|
<coefficients ue_reference="5" coef="15.0" />
|
||||||
|
<coefficients ue_reference="6" 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="6" coef="11.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="6" code_apogee="" titre="Sport/Culture" coefficient="0.0" semestre_idx="1" coef_rcue="1.0" type="1" color="#ff4500" ue_code="X7.2" ects="0.0" is_external="0" acronyme="UE17" reference="7">
|
||||||
|
<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 numero="7" code_apogee="V1INFU21" titre="Compétence 1: Réaliser un développement d'application" coefficient="0.0" semestre_idx="2" coef_rcue="1.0" type="0" color="#b80004" ue_code="UCOD17" ects="5.0" is_external="0" acronyme="UE21" apc_niveau_libelle="Développer des applications informatiques simples " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="8">
|
||||||
|
<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="8" coef="21.0" />
|
||||||
|
<coefficients ue_reference="9" 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="8" coef="21.0" />
|
||||||
|
<coefficients ue_reference="12" coef="3.0" />
|
||||||
|
<coefficients ue_reference="13" 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="8" coef="12.0" />
|
||||||
|
<coefficients ue_reference="12" 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="8" coef="40.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="8" code_apogee="V1INFU22" titre="Compétence 2 : Optimiser des applications informatiques" coefficient="0.0" semestre_idx="2" coef_rcue="1.0" type="0" color="#f97b3d" ue_code="UCOD18" ects="5.0" is_external="0" acronyme="UE22" apc_niveau_libelle="Appréhender et construire des algorithmes " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="9">
|
||||||
|
<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="9" coef="21.0" />
|
||||||
|
<coefficients ue_reference="12" 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="9" 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="9" coef="40.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="9" code_apogee="V1INFU23" titre="Compétence 3 : Administrer des systèmes informatiques communicants" coefficient="0.0" semestre_idx="2" coef_rcue="1.0" type="0" color="#feb40b" ue_code="UCOD19" ects="5.0" is_external="0" acronyme="UE23" apc_niveau_libelle="Installer et configurer un poste de travail " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="10">
|
||||||
|
<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="9" coef="6.0" />
|
||||||
|
<coefficients ue_reference="10" 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="9" coef="6.0" />
|
||||||
|
<coefficients ue_reference="10" 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="10" 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="10" coef="40.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="10" code_apogee="V1INFU24" titre="Compétence 4 : Gérer des données de l'information" coefficient="0.0" semestre_idx="2" coef_rcue="1.0" type="0" color="#80cb3f" ue_code="UCOD20" ects="5.0" is_external="0" acronyme="UE24" apc_niveau_libelle="Concevoir et mettre en place une base de données à partir d’un cahier des charges client " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="11">
|
||||||
|
<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="11" 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="11" 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="11" coef="40.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="11" code_apogee="V1INFU25" titre="Compétence 5 : Conduire un projet" coefficient="0.0" semestre_idx="2" coef_rcue="1.0" type="0" color="#05162e" ue_code="UCOD21" ects="5.0" is_external="0" acronyme="UE25" apc_niveau_libelle="Identifier les besoins métiers des clients et des utilisateurs " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="12">
|
||||||
|
<matiere titre="Compétence 5 : Conduire un projet" numero="1">
|
||||||
|
<module titre="Gestion de projet & des organisations" abbrev="Gestion de projet & 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="11" coef="12.0" />
|
||||||
|
<coefficients ue_reference="12" 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="10" coef="6.0" />
|
||||||
|
<coefficients ue_reference="11" coef="6.0" />
|
||||||
|
<coefficients ue_reference="12" coef="6.0" />
|
||||||
|
<coefficients ue_reference="13" 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="12" coef="40.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="12" code_apogee="V1INFU26" titre="Compétence 6 : Travailler dans une équipe informatique" coefficient="0.0" semestre_idx="2" coef_rcue="1.0" type="0" color="#548687" ue_code="UCOD22" ects="5.0" is_external="0" acronyme="UE26" apc_niveau_libelle="Identifier ses aptitudes pour travailler dans une équipe " apc_niveau_annee="BUT1" apc_niveau_ordre="1" reference="13">
|
||||||
|
<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="13" 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="8" coef="6.0" />
|
||||||
|
<coefficients ue_reference="10" coef="3.0" />
|
||||||
|
<coefficients ue_reference="12" coef="9.0" />
|
||||||
|
<coefficients ue_reference="13" 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="13" coef="40.0" />
|
||||||
|
</module>
|
||||||
|
<module titre="Portfolio" abbrev="Portfolio" code="P2" 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">
|
||||||
|
<coefficients ue_reference="8" coef="1.0" />
|
||||||
|
<coefficients ue_reference="9" coef="1.0" />
|
||||||
|
<coefficients ue_reference="10" coef="1.0" />
|
||||||
|
<coefficients ue_reference="11" coef="1.0" />
|
||||||
|
<coefficients ue_reference="12" coef="1.0" />
|
||||||
|
<coefficients ue_reference="13" coef="1.0" />
|
||||||
|
</module>
|
||||||
|
<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="13" coef="11.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="13" code_apogee="" titre="Compétence 1 : Réaliser un développement d'application" coefficient="0.0" semestre_idx="3" coef_rcue="1.0" type="0" color="#b80004" ue_code="UCOD71" ects="5.0" is_external="0" acronyme="UE31" apc_niveau_libelle="Partir des exigences et aller jusqu’à une application complète " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="14">
|
||||||
|
<matiere titre="Compétence 1 : Réaliser un développement d'application" numero="1" />
|
||||||
|
</ue>
|
||||||
|
<ue numero="14" code_apogee="" titre="Compétence 2 : Optimiser des applications informatiques" coefficient="0.0" semestre_idx="3" coef_rcue="1.0" type="0" color="#f97b3d" ue_code="UCOD79" ects="5.0" is_external="0" acronyme="UE32" 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="15">
|
||||||
|
<matiere titre="Compétence 2 : Optimiser des applications informatiques" numero="1" />
|
||||||
|
</ue>
|
||||||
|
<ue numero="15" code_apogee="" titre="Compétence 3 : Administrer des systèmes informatiques communicants" coefficient="0.0" semestre_idx="3" coef_rcue="1.0" type="0" color="#feb40b" ue_code="UCOD102" ects="5.0" is_external="0" acronyme="UE33" apc_niveau_libelle="Déployer des services dans une architecture réseau" apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="16">
|
||||||
|
<matiere titre="Compétence 3 : Administrer des systèmes informatiques communicants" numero="1" />
|
||||||
|
</ue>
|
||||||
|
<ue numero="16" code_apogee="" titre="Compétence 4 : Gérer des données de l'information" coefficient="0.0" semestre_idx="3" coef_rcue="1.0" type="0" color="#80cb3f" ue_code="UCOD103" ects="5.0" is_external="0" acronyme="UE34" 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="17">
|
||||||
|
<matiere titre="Compétence 4 : Gérer des données de l'information" numero="1" />
|
||||||
|
</ue>
|
||||||
|
<ue numero="17" code_apogee="" titre="Compétence 5 : Conduire un projet" coefficient="0.0" semestre_idx="3" coef_rcue="1.0" type="0" color="#05162e" ue_code="UCOD104" ects="5.0" is_external="0" acronyme="UE35" 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="18">
|
||||||
|
<matiere titre="Compétence 5 : Conduire un projet" numero="1" />
|
||||||
|
</ue>
|
||||||
|
<ue numero="18" code_apogee="" titre="Compétence 6 : Travailler dans une équipe informatique" coefficient="0.0" semestre_idx="3" coef_rcue="1.0" type="0" color="#548687" ue_code="UCOD105" ects="5.0" is_external="0" acronyme="UE36" apc_niveau_libelle="Situer son rôle et ses missions au sein d’une équipe informatique " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="19">
|
||||||
|
<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="14" coef="15.0" />
|
||||||
|
<coefficients ue_reference="15" coef="5.0" />
|
||||||
|
<coefficients ue_reference="16" coef="5.0" />
|
||||||
|
<coefficients ue_reference="17" 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="14" coef="10.0" />
|
||||||
|
<coefficients ue_reference="15" 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="14" coef="12.0" />
|
||||||
|
<coefficients ue_reference="15" coef="5.0" />
|
||||||
|
<coefficients ue_reference="18" 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="14" coef="15.0" />
|
||||||
|
<coefficients ue_reference="18" coef="8.0" />
|
||||||
|
<coefficients ue_reference="19" 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="16" 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="15" coef="5.0" />
|
||||||
|
<coefficients ue_reference="16" 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="17" 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="15" coef="17.0" />
|
||||||
|
<coefficients ue_reference="17" 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="15" coef="10.0" />
|
||||||
|
<coefficients ue_reference="16" coef="10.0" />
|
||||||
|
<coefficients ue_reference="17" 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="17" coef="10.0" />
|
||||||
|
<coefficients ue_reference="18" coef="18.0" />
|
||||||
|
<coefficients ue_reference="19" 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="14" coef="8.0" />
|
||||||
|
<coefficients ue_reference="17" coef="5.0" />
|
||||||
|
<coefficients ue_reference="18" 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="15" coef="5.0" />
|
||||||
|
<coefficients ue_reference="16" coef="5.0" />
|
||||||
|
<coefficients ue_reference="18" coef="7.0" />
|
||||||
|
<coefficients ue_reference="19" 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="18" coef="7.0" />
|
||||||
|
<coefficients ue_reference="19" 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="14" coef="40.0" />
|
||||||
|
<coefficients ue_reference="15" coef="40.0" />
|
||||||
|
<coefficients ue_reference="16" coef="40.0" />
|
||||||
|
<coefficients ue_reference="17" coef="40.0" />
|
||||||
|
<coefficients ue_reference="18" coef="40.0" />
|
||||||
|
<coefficients ue_reference="19" coef="40.0" />
|
||||||
|
</module>
|
||||||
|
<module titre="Portfolio" abbrev="Portfolio" code="P3" 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">
|
||||||
|
<coefficients ue_reference="14" coef="1.0" />
|
||||||
|
<coefficients ue_reference="19" coef="1.0" />
|
||||||
|
<coefficients ue_reference="15" coef="1.0" />
|
||||||
|
<coefficients ue_reference="16" coef="1.0" />
|
||||||
|
<coefficients ue_reference="17" coef="1.0" />
|
||||||
|
<coefficients ue_reference="18" coef="1.0" />
|
||||||
|
</module>
|
||||||
|
<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="19" coef="15.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="19" code_apogee="" titre="Sport / Culture" coefficient="0.0" semestre_idx="3" coef_rcue="1.0" type="1" color="#444054" ue_code="X7.1" ects="0.0" is_external="0" acronyme="UE37" reference="20">
|
||||||
|
<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 numero="20" code_apogee="" titre="Réaliser un développement d'application" coefficient="0.0" semestre_idx="4" coef_rcue="1.0" type="0" color="#b80004" ue_code="UCOD170" ects="5.0" is_external="0" acronyme="UE41-A" apc_niveau_libelle="Partir des exigences et aller jusqu’à une application complète " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="21">
|
||||||
|
<parcours code="A" numero="0" libelle="A : Réalisation d’applications : conception, développement, validation" />
|
||||||
|
<matiere titre="UE41 Compétence 1 : Réaliser un développement d'application" numero="1">
|
||||||
|
<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="0" code_apogee="" module_type="3">
|
||||||
|
<coefficients ue_reference="22" coef="15.0" />
|
||||||
|
<coefficients ue_reference="23" coef="15.0" />
|
||||||
|
<coefficients ue_reference="24" coef="15.0" />
|
||||||
|
<coefficients ue_reference="25" coef="15.0" />
|
||||||
|
<coefficients ue_reference="26" coef="15.0" />
|
||||||
|
<coefficients ue_reference="27" coef="15.0" />
|
||||||
|
<coefficients ue_reference="21" coef="15.0" />
|
||||||
|
</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="2" code_apogee="" module_type="3">
|
||||||
|
<coefficients ue_reference="22" coef="40.0" />
|
||||||
|
<coefficients ue_reference="23" coef="40.0" />
|
||||||
|
<coefficients ue_reference="24" coef="40.0" />
|
||||||
|
<coefficients ue_reference="25" coef="40.0" />
|
||||||
|
<coefficients ue_reference="26" coef="40.0" />
|
||||||
|
<coefficients ue_reference="27" coef="40.0" />
|
||||||
|
<coefficients ue_reference="21" coef="40.0" />
|
||||||
|
</module>
|
||||||
|
<module titre="Portfolio" abbrev="Portfolio" code="P4-A" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="4" code_apogee="" module_type="3">
|
||||||
|
<coefficients ue_reference="23" coef="5.0" />
|
||||||
|
<coefficients ue_reference="24" coef="5.0" />
|
||||||
|
<coefficients ue_reference="25" coef="5.0" />
|
||||||
|
<coefficients ue_reference="26" coef="5.0" />
|
||||||
|
<coefficients ue_reference="27" coef="5.0" />
|
||||||
|
<coefficients ue_reference="21" coef="5.0" />
|
||||||
|
<parcours code="A" numero="0" libelle="A : Réalisation d’applications : conception, développement, validation" />
|
||||||
|
</module>
|
||||||
|
<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="22" coef="23.0" />
|
||||||
|
<coefficients ue_reference="24" coef="12.0" />
|
||||||
|
<coefficients ue_reference="27" coef="4.0" />
|
||||||
|
<coefficients ue_reference="21" coef="14.0" />
|
||||||
|
</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="27" coef="10.0" />
|
||||||
|
</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="22" coef="8.0" />
|
||||||
|
<coefficients ue_reference="23" coef="4.0" />
|
||||||
|
<coefficients ue_reference="25" coef="8.0" />
|
||||||
|
<coefficients ue_reference="26" coef="4.0" />
|
||||||
|
<coefficients ue_reference="21" coef="10.0" />
|
||||||
|
</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="22" coef="8.0" />
|
||||||
|
<coefficients ue_reference="23" coef="4.0" />
|
||||||
|
<coefficients ue_reference="25" coef="8.0" />
|
||||||
|
<coefficients ue_reference="26" coef="4.0" />
|
||||||
|
<coefficients ue_reference="21" coef="10.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="21" code_apogee="" titre="Réaliser un développement d'application" coefficient="0.0" semestre_idx="4" coef_rcue="1.0" type="0" color="#b80004" ue_code="UCOD171" ects="5.0" is_external="0" acronyme="UE41-B" apc_niveau_libelle="Partir des exigences et aller jusqu’à une application complète " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="22">
|
||||||
|
<parcours code="B" numero="0" libelle="B : Déploiement d’applications communicantes et sécurisées" />
|
||||||
|
<matiere titre="UE41 Compétence 1 : Réaliser un développement d'application" numero="1">
|
||||||
|
<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="1" code_apogee="" module_type="3">
|
||||||
|
<coefficients ue_reference="22" coef="15.0" />
|
||||||
|
<coefficients ue_reference="23" coef="15.0" />
|
||||||
|
<coefficients ue_reference="24" coef="15.0" />
|
||||||
|
<coefficients ue_reference="25" coef="15.0" />
|
||||||
|
<coefficients ue_reference="26" coef="15.0" />
|
||||||
|
<coefficients ue_reference="27" coef="15.0" />
|
||||||
|
<coefficients ue_reference="21" coef="15.0" />
|
||||||
|
</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="3" code_apogee="" module_type="3">
|
||||||
|
<coefficients ue_reference="22" coef="40.0" />
|
||||||
|
<coefficients ue_reference="23" coef="40.0" />
|
||||||
|
<coefficients ue_reference="24" coef="40.0" />
|
||||||
|
<coefficients ue_reference="25" coef="40.0" />
|
||||||
|
<coefficients ue_reference="26" coef="40.0" />
|
||||||
|
<coefficients ue_reference="27" coef="40.0" />
|
||||||
|
<coefficients ue_reference="21" coef="40.0" />
|
||||||
|
</module>
|
||||||
|
<module titre="Portfolio" abbrev="Portfolio" code="P4-B" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="4" numero="5" code_apogee="" module_type="3">
|
||||||
|
<coefficients ue_reference="22" coef="5.0" />
|
||||||
|
<coefficients ue_reference="23" coef="5.0" />
|
||||||
|
<coefficients ue_reference="24" coef="5.0" />
|
||||||
|
<coefficients ue_reference="25" coef="5.0" />
|
||||||
|
<coefficients ue_reference="26" coef="5.0" />
|
||||||
|
<coefficients ue_reference="27" coef="5.0" />
|
||||||
|
<parcours code="B" numero="0" libelle="B : Déploiement d’applications communicantes et sécurisées" />
|
||||||
|
</module>
|
||||||
|
<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="22" coef="27.0" />
|
||||||
|
<coefficients ue_reference="24" coef="1.0" />
|
||||||
|
<coefficients ue_reference="27" coef="4.0" />
|
||||||
|
<coefficients ue_reference="21" coef="27.0" />
|
||||||
|
</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="27" coef="10.0" />
|
||||||
|
</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="22" coef="8.0" />
|
||||||
|
<coefficients ue_reference="23" coef="4.0" />
|
||||||
|
<coefficients ue_reference="25" coef="8.0" />
|
||||||
|
<coefficients ue_reference="26" coef="4.0" />
|
||||||
|
<coefficients ue_reference="21" coef="10.0" />
|
||||||
|
</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="22" coef="8.0" />
|
||||||
|
<coefficients ue_reference="23" coef="4.0" />
|
||||||
|
<coefficients ue_reference="25" coef="8.0" />
|
||||||
|
<coefficients ue_reference="26" coef="4.0" />
|
||||||
|
<coefficients ue_reference="21" coef="11.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="22" code_apogee="" titre="Optimiser des applications informatiques" coefficient="0.0" semestre_idx="4" coef_rcue="1.0" type="0" color="#f97b3d" ue_code="UCOD172" ects="5.0" is_external="0" acronyme="UE42" 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="23">
|
||||||
|
<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="23" coef="12.0" />
|
||||||
|
</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="23" coef="12.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="23" code_apogee="" titre="Administrer des systèmes informatiques communicants" coefficient="0.0" semestre_idx="4" coef_rcue="1.0" type="0" color="#feb40b" ue_code="UCOD173" ects="5.0" is_external="0" acronyme="UE43" apc_niveau_libelle="Déployer des services dans une architecture réseau" apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="24">
|
||||||
|
<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="24" coef="28.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="24" code_apogee="" titre="Gérer des données de l'information" coefficient="0.0" semestre_idx="4" coef_rcue="1.0" type="0" color="#80cb3f" ue_code="UCOD174" ects="5.0" is_external="0" acronyme="UE44" 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="25">
|
||||||
|
<matiere titre="UE44 Compétence 4 : Gérer des données de l'information" numero="1">
|
||||||
|
<module titre="Qualité & Non-relationnel" abbrev="Qualité & 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="25" coef="18.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="25" code_apogee="" titre="Conduire un projet" coefficient="0.0" semestre_idx="4" coef_rcue="1.0" type="0" color="#05162e" ue_code="UCOD175" ects="5.0" is_external="0" acronyme="UE45" 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="26">
|
||||||
|
<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="22" coef="8.0" />
|
||||||
|
<coefficients ue_reference="26" coef="10.0" />
|
||||||
|
<coefficients ue_reference="21" coef="8.0" />
|
||||||
|
</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="23" coef="4.0" />
|
||||||
|
<coefficients ue_reference="26" coef="22.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="26" code_apogee="" titre="Travailler dans une équipe informatique" coefficient="0.0" semestre_idx="4" coef_rcue="1.0" type="0" color="#0c0c0c" ue_code="UCOD176" ects="5.0" is_external="0" acronyme="UE46" apc_niveau_libelle="Situer son rôle et ses missions au sein d’une équipe informatique " apc_niveau_annee="BUT2" apc_niveau_ordre="2" reference="27">
|
||||||
|
<parcours code="C" numero="0" libelle="C : Administration, gestion et exploitation des données" />
|
||||||
|
<parcours code="B" numero="0" libelle="B : Déploiement d’applications communicantes et sécurisées" ects="4.0" />
|
||||||
|
<parcours code="D" numero="0" libelle="D : Intégration d’applications et management du système d’information" ects="6.0" />
|
||||||
|
<parcours code="A" numero="0" libelle="A : Réalisation d’applications : conception, développement, validation" ects="3.0" />
|
||||||
|
<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="23" coef="4.0" />
|
||||||
|
<coefficients ue_reference="27" coef="13.0" />
|
||||||
|
</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="25" coef="6.0" />
|
||||||
|
<coefficients ue_reference="27" coef="13.0" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="27" code_apogee="" titre="Sport, culture, engagement" coefficient="0.0" semestre_idx="4" coef_rcue="1.0" type="1" color="#444054" ue_code="OPT4" ects="1.0" is_external="0" acronyme="Sport, culture, engagement" reference="28">
|
||||||
|
<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 numero="28" code_apogee="" titre="Réaliser S5" coefficient="0.0" semestre_idx="5" coef_rcue="1.0" type="0" color="#b80004" ue_code="UCOD177" ects="5.0" is_external="0" acronyme="UE51" apc_niveau_libelle="Adapter des applications sur un ensemble de supports (embarqué, web, mobile, IoT…) " apc_niveau_annee="BUT3" apc_niveau_ordre="3" reference="29">
|
||||||
|
<matiere titre="Réaliser S5" numero="1">
|
||||||
|
<module titre="Portfolio" abbrev="" code="P5-A" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="5" numero="710" code_apogee="" module_type="3">
|
||||||
|
<parcours code="A" numero="0" libelle="A : Réalisation d’applications : conception, développement, validation" />
|
||||||
|
</module>
|
||||||
|
<module titre="Portfolio" abbrev="" code="P5-B" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="5" numero="720" code_apogee="" module_type="3">
|
||||||
|
<parcours code="B" numero="0" libelle="B : Déploiement d’applications communicantes et sécurisées" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="29" code_apogee="" titre="Optimiser" coefficient="0.0" semestre_idx="5" coef_rcue="1.0" type="0" color="#f97b3d" ue_code="UCOD178" ects="5.0" is_external="0" acronyme="UE52" apc_niveau_libelle="Analyser et optimiser des applications" apc_niveau_annee="BUT3" apc_niveau_ordre="3" reference="30">
|
||||||
|
<parcours code="A" numero="0" libelle="A : Réalisation d’applications : conception, développement, validation" />
|
||||||
|
<matiere titre="Optimiser S5" numero="1" />
|
||||||
|
</ue>
|
||||||
|
<ue numero="31" code_apogee="" titre="Administrer" coefficient="0.0" semestre_idx="5" coef_rcue="1.0" type="0" color="#dfb03f" ue_code="UCOD180" ects="5.0" is_external="0" acronyme="UE53" apc_niveau_libelle="Faire évoluer et maintenir un système informatique communicant en conditions opérationnelles " apc_niveau_annee="BUT3" apc_niveau_ordre="3" reference="31">
|
||||||
|
<parcours code="B" numero="0" libelle="B : Déploiement d’applications communicantes et sécurisées" />
|
||||||
|
<matiere titre="Administrer S5" numero="1" />
|
||||||
|
</ue>
|
||||||
|
<ue numero="32" code_apogee="" titre="Gérer" coefficient="0.0" semestre_idx="5" coef_rcue="1.0" type="0" color="#8abd53" ue_code="UCOD181" ects="5.0" is_external="0" acronyme="UE54" apc_niveau_libelle="Administrer une base de données, concevoir et réaliser des systèmes d’informations décisionnels " apc_niveau_annee="BUT3" apc_niveau_ordre="3" reference="32">
|
||||||
|
<parcours code="C" numero="0" libelle="C : Administration, gestion et exploitation des données" />
|
||||||
|
<matiere titre="Gérer S5 - TC" numero="1" />
|
||||||
|
</ue>
|
||||||
|
<ue numero="34" code_apogee="" titre="Conduire" coefficient="0.0" semestre_idx="5" coef_rcue="1.0" type="0" color="#0c1548" ue_code="UCOD185" ects="5.0" is_external="0" acronyme="UE55" apc_niveau_libelle="Participer à la conception et à la mise en œuvre d’un projet système d’information " apc_niveau_annee="BUT3" apc_niveau_ordre="3" reference="33">
|
||||||
|
<parcours code="D" numero="0" libelle="D : Intégration d’applications et management du système d’information" ects="6.0" />
|
||||||
|
<parcours code="C" numero="0" libelle="C : Administration, gestion et exploitation des données" ects="6.666" />
|
||||||
|
<matiere titre="Conduire S5 - TC" numero="1" />
|
||||||
|
</ue>
|
||||||
|
<ue numero="35" code_apogee="" titre="Collaborer S5" coefficient="0.0" semestre_idx="5" coef_rcue="1.0" type="0" color="#0c0c0c" ue_code="UCOD186" ects="5.0" is_external="0" acronyme="UE56" apc_niveau_libelle="Manager une équipe informatique" apc_niveau_annee="BUT3" apc_niveau_ordre="3" reference="34">
|
||||||
|
<matiere titre="Collaborer S5" numero="1" />
|
||||||
|
</ue>
|
||||||
|
<ue numero="36" code_apogee="" titre="Réaliser" coefficient="0.0" semestre_idx="6" coef_rcue="1.0" type="0" color="#b80004" ue_code="UCOD600" ects="5.0" is_external="0" acronyme="UE61" apc_niveau_libelle="Adapter des applications sur un ensemble de supports (embarqué, web, mobile, IoT…) " apc_niveau_annee="BUT3" apc_niveau_ordre="3" reference="35">
|
||||||
|
<matiere titre="Réaliser S6" numero="1">
|
||||||
|
<module titre="Portfolio" abbrev="" code="P6-A" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="6" numero="730" code_apogee="" module_type="3">
|
||||||
|
<coefficients ue_reference="35" coef="1.0" />
|
||||||
|
<coefficients ue_reference="36" coef="1.0" />
|
||||||
|
<coefficients ue_reference="40" coef="1.0" />
|
||||||
|
<parcours code="A" numero="0" libelle="A : Réalisation d’applications : conception, développement, validation" />
|
||||||
|
</module>
|
||||||
|
<module titre="Portfolio" abbrev="" code="P6-B" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="0.0" ects="" semestre_id="6" numero="740" code_apogee="" module_type="3">
|
||||||
|
<coefficients ue_reference="35" coef="1.0" />
|
||||||
|
<coefficients ue_reference="37" coef="1.0" />
|
||||||
|
<coefficients ue_reference="40" coef="1.0" />
|
||||||
|
<parcours code="B" numero="0" libelle="B : Déploiement d’applications communicantes et sécurisées" />
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue numero="37" code_apogee="" titre="Optimiser" coefficient="0.0" semestre_idx="6" coef_rcue="1.0" type="0" color="#f97b3d" ue_code="UCOD601" ects="5.0" is_external="0" acronyme="UE62" apc_niveau_libelle="Analyser et optimiser des applications" apc_niveau_annee="BUT3" apc_niveau_ordre="3" reference="36">
|
||||||
|
<parcours code="A" numero="0" libelle="A : Réalisation d’applications : conception, développement, validation" />
|
||||||
|
<matiere titre="Optimiser S6" numero="1" />
|
||||||
|
</ue>
|
||||||
|
<ue numero="39" code_apogee="" titre="Administrer" coefficient="0.0" semestre_idx="6" coef_rcue="1.0" type="0" color="#dfb03f" ue_code="UCOD603" ects="5.0" is_external="0" acronyme="UE63" apc_niveau_libelle="Faire évoluer et maintenir un système informatique communicant en conditions opérationnelles " apc_niveau_annee="BUT3" apc_niveau_ordre="3" reference="37">
|
||||||
|
<parcours code="B" numero="0" libelle="B : Déploiement d’applications communicantes et sécurisées" />
|
||||||
|
<matiere titre="Administrer S6" numero="1" />
|
||||||
|
</ue>
|
||||||
|
<ue numero="40" code_apogee="" titre="Gérer" coefficient="0.0" semestre_idx="6" coef_rcue="1.0" type="0" color="#8abd53" ue_code="UCOD604" ects="5.0" is_external="0" acronyme="UE64" apc_niveau_libelle="Administrer une base de données, concevoir et réaliser des systèmes d’informations décisionnels " apc_niveau_annee="BUT3" apc_niveau_ordre="3" reference="38">
|
||||||
|
<parcours code="C" numero="0" libelle="C : Administration, gestion et exploitation des données" />
|
||||||
|
<matiere titre="Gérer S6 - TC" numero="1" />
|
||||||
|
</ue>
|
||||||
|
<ue numero="41" code_apogee="" titre="Conduire" coefficient="0.0" semestre_idx="6" coef_rcue="1.0" type="0" color="#0c1548" ue_code="UCOD605" ects="5.0" is_external="0" acronyme="UE65" apc_niveau_libelle="Participer à la conception et à la mise en œuvre d’un projet système d’information " apc_niveau_annee="BUT3" apc_niveau_ordre="3" reference="39">
|
||||||
|
<parcours code="C" numero="0" libelle="C : Administration, gestion et exploitation des données" />
|
||||||
|
<parcours code="D" numero="0" libelle="D : Intégration d’applications et management du système d’information" />
|
||||||
|
<matiere titre="Gérer Semestre 5 - C" numero="1" />
|
||||||
|
</ue>
|
||||||
|
<ue numero="43" code_apogee="" titre="Collaborer" coefficient="0.0" semestre_idx="6" coef_rcue="1.0" type="0" color="#0c0c0c" ue_code="UCOD607" ects="5.0" is_external="0" acronyme="UE66" apc_niveau_libelle="Manager une équipe informatique" apc_niveau_annee="BUT3" apc_niveau_ordre="3" reference="40">
|
||||||
|
<matiere titre="Collaborer S6" numero="1" />
|
||||||
|
</ue>
|
||||||
|
</formation>
|
@ -103,14 +103,14 @@ def run_sco_basic(verbose=False) -> FormSemestre:
|
|||||||
|
|
||||||
# --- Saisie toutes les notes de l'évaluation
|
# --- Saisie toutes les notes de l'évaluation
|
||||||
for idx, etud in enumerate(etuds):
|
for idx, etud in enumerate(etuds):
|
||||||
nb_changed, nb_suppress, existing_decisions = G.create_note(
|
etudids_changed, nb_suppress, existing_decisions = G.create_note(
|
||||||
evaluation_id=e["id"],
|
evaluation_id=e["id"],
|
||||||
etudid=etud["etudid"],
|
etudid=etud["etudid"],
|
||||||
note=NOTES_T[idx % len(NOTES_T)],
|
note=NOTES_T[idx % len(NOTES_T)],
|
||||||
)
|
)
|
||||||
assert not existing_decisions
|
assert not existing_decisions
|
||||||
assert nb_suppress == 0
|
assert nb_suppress == 0
|
||||||
assert nb_changed == 1
|
assert len(etudids_changed) == 1
|
||||||
|
|
||||||
# --- Vérifie que les notes sont prises en compte:
|
# --- Vérifie que les notes sont prises en compte:
|
||||||
b = sco_bulletins.formsemestre_bulletinetud_dict(formsemestre_id, etud["etudid"])
|
b = sco_bulletins.formsemestre_bulletinetud_dict(formsemestre_id, etud["etudid"])
|
||||||
@ -136,7 +136,7 @@ def run_sco_basic(verbose=False) -> FormSemestre:
|
|||||||
)
|
)
|
||||||
# Saisie les notes des 5 premiers étudiants:
|
# Saisie les notes des 5 premiers étudiants:
|
||||||
for idx, etud in enumerate(etuds[:5]):
|
for idx, etud in enumerate(etuds[:5]):
|
||||||
nb_changed, nb_suppress, existing_decisions = G.create_note(
|
etudids_changed, nb_suppress, existing_decisions = G.create_note(
|
||||||
evaluation_id=e2["id"],
|
evaluation_id=e2["id"],
|
||||||
etudid=etud["etudid"],
|
etudid=etud["etudid"],
|
||||||
note=NOTES_T[idx % len(NOTES_T)],
|
note=NOTES_T[idx % len(NOTES_T)],
|
||||||
@ -158,7 +158,7 @@ def run_sco_basic(verbose=False) -> FormSemestre:
|
|||||||
|
|
||||||
# Saisie des notes qui manquent:
|
# Saisie des notes qui manquent:
|
||||||
for idx, etud in enumerate(etuds[5:]):
|
for idx, etud in enumerate(etuds[5:]):
|
||||||
nb_changed, nb_suppress, existing_decisions = G.create_note(
|
etudids_changed, nb_suppress, existing_decisions = G.create_note(
|
||||||
evaluation_id=e2["id"],
|
evaluation_id=e2["id"],
|
||||||
etudid=etud["etudid"],
|
etudid=etud["etudid"],
|
||||||
note=NOTES_T[idx % len(NOTES_T)],
|
note=NOTES_T[idx % len(NOTES_T)],
|
||||||
|
@ -239,7 +239,7 @@ def create_evaluations(formsemestre: FormSemestre):
|
|||||||
"jour": datetime.date(2022, 3, 1) + datetime.timedelta(days=modimpl.id),
|
"jour": datetime.date(2022, 3, 1) + datetime.timedelta(days=modimpl.id),
|
||||||
"heure_debut": "8h00",
|
"heure_debut": "8h00",
|
||||||
"heure_fin": "9h00",
|
"heure_fin": "9h00",
|
||||||
"description": None,
|
"description": f"Evaluation-{modimpl.module.code}",
|
||||||
"note_max": 20,
|
"note_max": 20,
|
||||||
"coefficient": 1.0,
|
"coefficient": 1.0,
|
||||||
"visibulletin": True,
|
"visibulletin": True,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user