forked from ScoDoc/ScoDoc
Prise en compte UE capitalisées lorsque non inscrit dans le sem. courant. Affichage sur bulletins classiques. Capitalisation en BUT avec ECTS.
This commit is contained in:
parent
6b8410e43b
commit
29b5d54d22
@ -9,18 +9,22 @@ from functools import cached_property
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
|
from flask import g, url_for
|
||||||
|
|
||||||
from app import log
|
from app import log
|
||||||
from app.comp.aux_stats import StatsMoyenne
|
from app.comp.aux_stats import StatsMoyenne
|
||||||
from app.comp import moy_sem
|
from app.comp import moy_sem
|
||||||
from app.comp.res_cache import ResultatsCache
|
from app.comp.res_cache import ResultatsCache
|
||||||
from app.comp import res_sem
|
from app.comp import res_sem
|
||||||
from app.comp.moy_mod import ModuleImplResults
|
from app.comp.moy_mod import ModuleImplResults
|
||||||
from app.models import FormSemestre, Identite, ModuleImpl
|
from app.models import FormSemestre, FormSemestreUECoef
|
||||||
from app.models import FormSemestreUECoef
|
from app.models import Identite
|
||||||
|
from app.models import ModuleImpl, ModuleImplInscription
|
||||||
from app.models.ues import UniteEns
|
from app.models.ues import UniteEns
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from app.scodoc.sco_cache import ResultatsSemestreCache
|
from app.scodoc.sco_cache import ResultatsSemestreCache
|
||||||
from app.scodoc.sco_codes_parcours import UE_SPORT, DEF
|
from app.scodoc.sco_codes_parcours import UE_SPORT, DEF
|
||||||
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
|
|
||||||
# Il faut bien distinguer
|
# Il faut bien distinguer
|
||||||
# - ce qui est caché de façon persistente (via redis):
|
# - ce qui est caché de façon persistente (via redis):
|
||||||
@ -206,12 +210,18 @@ class ResultatsSemestre(ResultatsCache):
|
|||||||
0.0, min(self.etud_moy_gen[etudid], 20.0)
|
0.0, min(self.etud_moy_gen[etudid], 20.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_etud_ue_cap(self, etudid, ue):
|
def _get_etud_ue_cap(self, etudid: int, ue: UniteEns) -> dict:
|
||||||
""""""
|
"""Donne les informations sur la capitalisation de l'UE ue pour cet étudiant.
|
||||||
|
Résultat:
|
||||||
|
Si pas capitalisée: None
|
||||||
|
Si capitalisée: un dict, avec les colonnes de validation.
|
||||||
|
"""
|
||||||
capitalisations = self.validations.ue_capitalisees.loc[etudid]
|
capitalisations = self.validations.ue_capitalisees.loc[etudid]
|
||||||
if isinstance(capitalisations, pd.DataFrame):
|
if isinstance(capitalisations, pd.DataFrame):
|
||||||
ue_cap = capitalisations[capitalisations["ue_code"] == ue.ue_code]
|
ue_cap = capitalisations[capitalisations["ue_code"] == ue.ue_code]
|
||||||
if isinstance(ue_cap, pd.DataFrame) and not ue_cap.empty:
|
if ue_cap.empty:
|
||||||
|
return None
|
||||||
|
if isinstance(ue_cap, pd.DataFrame):
|
||||||
# si plusieurs fois capitalisée, prend le max
|
# si plusieurs fois capitalisée, prend le max
|
||||||
cap_idx = ue_cap["moy_ue"].values.argmax()
|
cap_idx = ue_cap["moy_ue"].values.argmax()
|
||||||
ue_cap = ue_cap.iloc[cap_idx]
|
ue_cap = ue_cap.iloc[cap_idx]
|
||||||
@ -219,8 +229,9 @@ class ResultatsSemestre(ResultatsCache):
|
|||||||
if capitalisations["ue_code"] == ue.ue_code:
|
if capitalisations["ue_code"] == ue.ue_code:
|
||||||
ue_cap = capitalisations
|
ue_cap = capitalisations
|
||||||
else:
|
else:
|
||||||
ue_cap = None
|
return None
|
||||||
return ue_cap
|
# converti la Series en dict, afin que les np.int64 reviennent en int
|
||||||
|
return ue_cap.to_dict()
|
||||||
|
|
||||||
def get_etud_ue_status(self, etudid: int, ue_id: int) -> dict:
|
def get_etud_ue_status(self, etudid: int, ue_id: int) -> dict:
|
||||||
"""L'état de l'UE pour cet étudiant.
|
"""L'état de l'UE pour cet étudiant.
|
||||||
@ -253,17 +264,41 @@ class ResultatsSemestre(ResultatsCache):
|
|||||||
)
|
)
|
||||||
if etudid in self.validations.ue_capitalisees.index:
|
if etudid in self.validations.ue_capitalisees.index:
|
||||||
ue_cap = self._get_etud_ue_cap(etudid, ue)
|
ue_cap = self._get_etud_ue_cap(etudid, ue)
|
||||||
if (
|
if ue_cap and not np.isnan(ue_cap["moy_ue"]):
|
||||||
ue_cap is not None
|
|
||||||
and not ue_cap.empty
|
|
||||||
and not np.isnan(ue_cap["moy_ue"])
|
|
||||||
):
|
|
||||||
was_capitalized = True
|
was_capitalized = True
|
||||||
if ue_cap["moy_ue"] > cur_moy_ue or np.isnan(cur_moy_ue):
|
if ue_cap["moy_ue"] > cur_moy_ue or np.isnan(cur_moy_ue):
|
||||||
moy_ue = ue_cap["moy_ue"]
|
moy_ue = ue_cap["moy_ue"]
|
||||||
is_capitalized = True
|
is_capitalized = True
|
||||||
|
|
||||||
coef_ue = self.etud_coef_ue_df[ue_id][etudid]
|
# Coef l'UE dans le semestre courant:
|
||||||
|
if self.is_apc:
|
||||||
|
# utilise les ECTS comme coef.
|
||||||
|
coef_ue = ue.ects
|
||||||
|
else:
|
||||||
|
# formations classiques
|
||||||
|
coef_ue = self.etud_coef_ue_df[ue_id][etudid]
|
||||||
|
if (not coef_ue) and is_capitalized: # étudiant non inscrit dans l'UE courante
|
||||||
|
if self.is_apc:
|
||||||
|
# Coefs de l'UE capitalisée en formation APC: donné par ses ECTS
|
||||||
|
ue_capitalized = UniteEns.query.get(ue_cap["ue_id"])
|
||||||
|
coef_ue = ue_capitalized.ects
|
||||||
|
if coef_ue is None:
|
||||||
|
orig_sem = FormSemestre.query.get(ue_cap["formsemestre_id"])
|
||||||
|
raise ScoValueError(
|
||||||
|
f"""L'UE capitalisée {ue_capitalized.acronyme}
|
||||||
|
du semestre {orig_sem.titre_annee()}
|
||||||
|
n'a pas d'indication d'ECTS.
|
||||||
|
Corrigez ou faite corriger le programme
|
||||||
|
<a href="{url_for('notes.ue_table', scodoc_dept=g.scodoc_dept,
|
||||||
|
formation_id=ue_capitalized.formation_id)}">via cette page</a>.
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Coefs de l'UE capitalisée en formation classique:
|
||||||
|
# va chercher le coef dans le semestre d'origine
|
||||||
|
coef_ue = ModuleImplInscription.sum_coefs_modimpl_ue(
|
||||||
|
ue_cap["formsemestre_id"], etudid, ue_cap["ue_id"]
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"is_capitalized": is_capitalized,
|
"is_capitalized": is_capitalized,
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
"""ScoDoc models: moduleimpls
|
"""ScoDoc models: moduleimpls
|
||||||
"""
|
"""
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
import flask_sqlalchemy
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.comp import df_cache
|
from app.comp import df_cache
|
||||||
@ -129,14 +130,36 @@ class ModuleImplInscription(db.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def nb_inscriptions_dans_ue(
|
def etud_modimpls_in_ue(
|
||||||
cls, formsemestre_id: int, etudid: int, ue_id: int
|
cls, formsemestre_id: int, etudid: int, ue_id: int
|
||||||
) -> int:
|
) -> flask_sqlalchemy.BaseQuery:
|
||||||
"""Nombre de moduleimpls de l'UE auxquels l'étudiant est inscrit"""
|
"""moduleimpls de l'UE auxquels l'étudiant est inscrit"""
|
||||||
return ModuleImplInscription.query.filter(
|
return ModuleImplInscription.query.filter(
|
||||||
ModuleImplInscription.etudid == etudid,
|
ModuleImplInscription.etudid == etudid,
|
||||||
ModuleImplInscription.moduleimpl_id == ModuleImpl.id,
|
ModuleImplInscription.moduleimpl_id == ModuleImpl.id,
|
||||||
ModuleImpl.formsemestre_id == formsemestre_id,
|
ModuleImpl.formsemestre_id == formsemestre_id,
|
||||||
ModuleImpl.module_id == Module.id,
|
ModuleImpl.module_id == Module.id,
|
||||||
Module.ue_id == ue_id,
|
Module.ue_id == ue_id,
|
||||||
).count()
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def nb_inscriptions_dans_ue(
|
||||||
|
cls, formsemestre_id: int, etudid: int, ue_id: int
|
||||||
|
) -> int:
|
||||||
|
"""Nombre de moduleimpls de l'UE auxquels l'étudiant est inscrit"""
|
||||||
|
return cls.etud_modimpls_in_ue(formsemestre_id, etudid, ue_id).count()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def sum_coefs_modimpl_ue(
|
||||||
|
cls, formsemestre_id: int, etudid: int, ue_id: int
|
||||||
|
) -> float:
|
||||||
|
"""Somme des coefficients des modules auxquels l'étudiant est inscrit
|
||||||
|
dans l'UE du semestre indiqué.
|
||||||
|
N'utilise que les coefficients, donc inadapté aux formations APC.
|
||||||
|
"""
|
||||||
|
return sum(
|
||||||
|
[
|
||||||
|
inscr.modimpl.module.coefficient
|
||||||
|
for inscr in cls.etud_modimpls_in_ue(formsemestre_id, etudid, ue_id)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@ -291,15 +291,17 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
|||||||
I["matieres_modules"] = {}
|
I["matieres_modules"] = {}
|
||||||
I["matieres_modules_capitalized"] = {}
|
I["matieres_modules_capitalized"] = {}
|
||||||
for ue in ues:
|
for ue in ues:
|
||||||
|
u = ue.copy()
|
||||||
|
ue_status = nt.get_etud_ue_status(etudid, ue["ue_id"])
|
||||||
if (
|
if (
|
||||||
ModuleImplInscription.nb_inscriptions_dans_ue(
|
ModuleImplInscription.nb_inscriptions_dans_ue(
|
||||||
formsemestre_id, etudid, ue["ue_id"]
|
formsemestre_id, etudid, ue["ue_id"]
|
||||||
)
|
)
|
||||||
== 0
|
== 0
|
||||||
):
|
) and not ue_status["is_capitalized"]:
|
||||||
|
# saute les UE où l'on est pas inscrit et n'avons pas de capitalisation
|
||||||
continue
|
continue
|
||||||
u = ue.copy()
|
|
||||||
ue_status = nt.get_etud_ue_status(etudid, ue["ue_id"])
|
|
||||||
u["ue_status"] = ue_status # { 'moy', 'coef_ue', ...}
|
u["ue_status"] = ue_status # { 'moy', 'coef_ue', ...}
|
||||||
if ue["type"] != sco_codes_parcours.UE_SPORT:
|
if ue["type"] != sco_codes_parcours.UE_SPORT:
|
||||||
u["cur_moy_ue_txt"] = scu.fmt_note(ue_status["cur_moy_ue"])
|
u["cur_moy_ue_txt"] = scu.fmt_note(ue_status["cur_moy_ue"])
|
||||||
|
@ -282,7 +282,7 @@ class TypeParcours(object):
|
|||||||
return [
|
return [
|
||||||
ue_status
|
ue_status
|
||||||
for ue_status in ues_status
|
for ue_status in ues_status
|
||||||
if ue_status["coef_ue"] > 0
|
if ue_status["coef_ue"]
|
||||||
and isinstance(ue_status["moy"], float)
|
and isinstance(ue_status["moy"], float)
|
||||||
and ue_status["moy"] < self.get_barre_ue(ue_status["ue"]["type"])
|
and ue_status["moy"] < self.get_barre_ue(ue_status["ue"]["type"])
|
||||||
]
|
]
|
||||||
|
@ -1078,7 +1078,7 @@ def formsemestre_status(formsemestre_id=None):
|
|||||||
"</p>",
|
"</p>",
|
||||||
]
|
]
|
||||||
|
|
||||||
if use_ue_coefs:
|
if use_ue_coefs and not formsemestre.formation.is_apc():
|
||||||
H.append(
|
H.append(
|
||||||
"""
|
"""
|
||||||
<p class="infop">utilise les coefficients d'UE pour calculer la moyenne générale.</p>
|
<p class="infop">utilise les coefficients d'UE pour calculer la moyenne générale.</p>
|
||||||
|
@ -585,15 +585,17 @@ def formsemestre_recap_parcours_table(
|
|||||||
else:
|
else:
|
||||||
H.append('<td colspan="%d"><em>en cours</em></td>')
|
H.append('<td colspan="%d"><em>en cours</em></td>')
|
||||||
H.append('<td class="rcp_nonass">%s</td>' % ass) # abs
|
H.append('<td class="rcp_nonass">%s</td>' % ass) # abs
|
||||||
# acronymes UEs auxquelles l'étudiant est inscrit:
|
# acronymes UEs auxquelles l'étudiant est inscrit (ou capitalisé)
|
||||||
# XXX il est probable que l'on doive ici ajouter les
|
|
||||||
# XXX UE capitalisées
|
|
||||||
ues = nt.get_ues_stat_dict(filter_sport=True)
|
ues = nt.get_ues_stat_dict(filter_sport=True)
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
|
etud_ue_status = {
|
||||||
|
ue["ue_id"]: nt.get_etud_ue_status(etudid, ue["ue_id"]) for ue in ues
|
||||||
|
}
|
||||||
ues = [
|
ues = [
|
||||||
ue
|
ue
|
||||||
for ue in ues
|
for ue in ues
|
||||||
if etud_est_inscrit_ue(cnx, etudid, sem["formsemestre_id"], ue["ue_id"])
|
if etud_est_inscrit_ue(cnx, etudid, sem["formsemestre_id"], ue["ue_id"])
|
||||||
|
or etud_ue_status[ue["ue_id"]]["is_capitalized"]
|
||||||
]
|
]
|
||||||
|
|
||||||
for ue in ues:
|
for ue in ues:
|
||||||
@ -644,7 +646,7 @@ def formsemestre_recap_parcours_table(
|
|||||||
code = decisions_ue[ue["ue_id"]]["code"]
|
code = decisions_ue[ue["ue_id"]]["code"]
|
||||||
else:
|
else:
|
||||||
code = ""
|
code = ""
|
||||||
ue_status = nt.get_etud_ue_status(etudid, ue["ue_id"])
|
ue_status = etud_ue_status[ue["ue_id"]]
|
||||||
moy_ue = ue_status["moy"] if ue_status else ""
|
moy_ue = ue_status["moy"] if ue_status else ""
|
||||||
explanation_ue = [] # list of strings
|
explanation_ue = [] # list of strings
|
||||||
if code == ADM:
|
if code == ADM:
|
||||||
|
@ -139,9 +139,7 @@ class SituationEtudParcoursGeneric(object):
|
|||||||
# pour le DUT, le dernier est toujours S4.
|
# pour le DUT, le dernier est toujours S4.
|
||||||
# Ici: terminal si semestre == NB_SEM ou bien semestre_id==-1
|
# Ici: terminal si semestre == NB_SEM ou bien semestre_id==-1
|
||||||
# (licences et autres formations en 1 seule session))
|
# (licences et autres formations en 1 seule session))
|
||||||
self.semestre_non_terminal = (
|
self.semestre_non_terminal = self.sem["semestre_id"] != self.parcours.NB_SEM
|
||||||
self.sem["semestre_id"] != self.parcours.NB_SEM
|
|
||||||
) # True | False
|
|
||||||
if self.sem["semestre_id"] == NO_SEMESTRE_ID:
|
if self.sem["semestre_id"] == NO_SEMESTRE_ID:
|
||||||
self.semestre_non_terminal = False
|
self.semestre_non_terminal = False
|
||||||
# Liste des semestres du parcours de cet étudiant:
|
# Liste des semestres du parcours de cet étudiant:
|
||||||
|
@ -287,15 +287,15 @@ div.logo-insidebar {
|
|||||||
width: 75px; /* la marge fait 130px */
|
width: 75px; /* la marge fait 130px */
|
||||||
}
|
}
|
||||||
div.logo-logo {
|
div.logo-logo {
|
||||||
|
margin-left: -5px;
|
||||||
text-align: center ;
|
text-align: center ;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.logo-logo img {
|
div.logo-logo img {
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
margin-top: -10px;
|
margin-top: 10px; /* -10px */
|
||||||
width: 128px;
|
width: 135px; /* 128px */
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
margin-left: -75px;
|
|
||||||
}
|
}
|
||||||
div.sidebar-bottom {
|
div.sidebar-bottom {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 37 KiB |
@ -35,7 +35,7 @@ from operator import itemgetter
|
|||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
from flask import flash, jsonify, render_template, url_for
|
from flask import abort, flash, jsonify, render_template, url_for
|
||||||
from flask import current_app, g, request
|
from flask import current_app, g, request
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from werkzeug.utils import redirect
|
from werkzeug.utils import redirect
|
||||||
@ -68,10 +68,14 @@ from app.scodoc import sco_utils as scu
|
|||||||
from app.scodoc import notesdb as ndb
|
from app.scodoc import notesdb as ndb
|
||||||
from app import log, send_scodoc_alarm
|
from app import log, send_scodoc_alarm
|
||||||
|
|
||||||
from app.scodoc import scolog
|
|
||||||
from app.scodoc.scolog import logdb
|
from app.scodoc.scolog import logdb
|
||||||
|
|
||||||
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError, ScoInvalidIdType
|
from app.scodoc.sco_exceptions import (
|
||||||
|
AccessDenied,
|
||||||
|
ScoException,
|
||||||
|
ScoValueError,
|
||||||
|
ScoInvalidIdType,
|
||||||
|
)
|
||||||
from app.scodoc import html_sco_header
|
from app.scodoc import html_sco_header
|
||||||
from app.pe import pe_view
|
from app.pe import pe_view
|
||||||
from app.scodoc import sco_abs
|
from app.scodoc import sco_abs
|
||||||
@ -2672,12 +2676,15 @@ def check_integrity_all():
|
|||||||
def moduleimpl_list(
|
def moduleimpl_list(
|
||||||
moduleimpl_id=None, formsemestre_id=None, module_id=None, format="json"
|
moduleimpl_id=None, formsemestre_id=None, module_id=None, format="json"
|
||||||
):
|
):
|
||||||
data = sco_moduleimpl.moduleimpl_list(
|
try:
|
||||||
moduleimpl_id=moduleimpl_id,
|
data = sco_moduleimpl.moduleimpl_list(
|
||||||
formsemestre_id=formsemestre_id,
|
moduleimpl_id=moduleimpl_id,
|
||||||
module_id=module_id,
|
formsemestre_id=formsemestre_id,
|
||||||
)
|
module_id=module_id,
|
||||||
return scu.sendResult(data, format=format)
|
)
|
||||||
|
return scu.sendResult(data, format=format)
|
||||||
|
except ScoException:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/do_moduleimpl_withmodule_list") # ancien nom
|
@bp.route("/do_moduleimpl_withmodule_list") # ancien nom
|
||||||
|
Loading…
Reference in New Issue
Block a user