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

This commit is contained in:
Emmanuel Viennet 2022-02-13 15:52:18 +01:00
commit b196675f9e
10 changed files with 177 additions and 140 deletions

View File

@ -182,7 +182,10 @@ def bulletin_but_xml_compat(
# Liste ici uniquement les modules rattachés à cette UE
if modimpl.module.ue.id == ue.id:
# mod_moy = scu.fmt_note(results.etud_moy_ue[ue.id][etud.id])
try:
coef = results.modimpl_coefs_df[modimpl.id][ue.id]
except KeyError:
coef = 0.0
x_mod = Element(
"module",
id=str(modimpl.id),

View File

@ -48,14 +48,15 @@ def compute_sem_moys_apc(
return moy_gen
def comp_ranks_series(notes: pd.Series) -> dict[int, str]:
def comp_ranks_series(notes: pd.Series) -> (pd.Series, pd.Series):
"""Calcul rangs à partir d'une séries ("vecteur") de notes (index etudid, valeur
numérique) en tenant compte des ex-aequos.
Result: { etudid : rang:str } rang est une chaine decrivant le rang.
Result: Series { etudid : rang:str } rang est une chaine decrivant le rang.
"""
notes = notes.sort_values(ascending=False) # Serie, tri par ordre décroissant
rangs = pd.Series(index=notes.index, dtype=str) # le rang est une chaîne
rangs_str = pd.Series(index=notes.index, dtype=str) # le rang est une chaîne
rangs_int = pd.Series(index=notes.index, dtype=int) # le rang numérique pour tris
N = len(notes)
nb_ex = 0 # nb d'ex-aequo consécutifs en cours
notes_i = notes.iat
@ -67,6 +68,7 @@ def comp_ranks_series(notes: pd.Series) -> dict[int, str]:
next = None
val = notes_i[i]
if nb_ex:
rangs_int[etudid] = i + 1 - nb_ex
srang = "%d ex" % (i + 1 - nb_ex)
if val == next:
nb_ex += 1
@ -74,9 +76,11 @@ def comp_ranks_series(notes: pd.Series) -> dict[int, str]:
nb_ex = 0
else:
if val == next:
rangs_int[etudid] = i + 1 - nb_ex
srang = "%d ex" % (i + 1 - nb_ex)
nb_ex = 1
else:
rangs_int[etudid] = i + 1
srang = "%d" % (i + 1)
rangs[etudid] = srang
return rangs
rangs_str[etudid] = srang
return rangs_str, rangs_int

View File

@ -51,6 +51,7 @@ class ResultatsSemestre(ResultatsCache):
"etud_moy_ue: DataFrame columns UE, rows etudid"
self.etud_moy_gen = {}
self.etud_moy_gen_ranks = {}
self.etud_moy_gen_ranks_int = {}
self.modimpls_results: ModuleImplResults = None
"Résultats de chaque modimpl: dict { modimpl.id : ModuleImplResults(Classique ou BUT) }"
self.etud_coef_ue_df = None
@ -308,6 +309,7 @@ class NotesTableCompat(ResultatsSemestre):
"bonus_ues",
"malus",
"etud_moy_gen_ranks",
"etud_moy_gen_ranks_int",
"ue_rangs",
)
@ -325,17 +327,33 @@ class NotesTableCompat(ResultatsSemestre):
self.expr_diagnostics = ""
self.parcours = self.formsemestre.formation.get_parcours()
def get_etudids(self, sorted=False) -> list[int]:
"""Liste des etudids inscrits, incluant les démissionnaires.
Si sorted, triée par moy. générale décroissante
Sinon, triée par ordre alphabetique de NOM
def get_inscrits(self, include_demdef=True, order_by=False) -> list[Identite]:
"""Liste des étudiants inscrits
order_by = False|'nom'|'moy' tri sur nom ou sur moyenne générale (indicative)
Note: pour récupérer les etudids des inscrits, non triés, il est plus efficace
d'utiliser `[ ins.etudid for ins in nt.formsemestre.inscriptions ]`
"""
etuds = self.formsemestre.get_inscrits(
include_demdef=include_demdef, order=(order_by == "nom")
)
if order_by == "moy":
etuds.sort(
key=lambda e: (
self.etud_moy_gen_ranks_int.get(e.id, 100000),
e.sort_key,
)
)
return etuds
def get_etudids(self) -> list[int]:
"""(deprecated)
Liste des etudids inscrits, incluant les démissionnaires.
triée par ordre alphabetique de NOM
(à éviter: renvoie les etudids, mais est moins efficace que get_inscrits)
"""
# Note: pour avoir les inscrits non triés,
# utiliser [ ins.etudid for ins in self.formsemestre.inscriptions ]
if sorted:
# Tri par moy. generale décroissante
return [x[-1] for x in self.T]
return [x["etudid"] for x in self.inscrlist]
@cached_property
@ -391,11 +409,14 @@ class NotesTableCompat(ResultatsSemestre):
Moyenne générale: etud_moy_gen_ranks
Par UE (sauf ue bonus)
"""
self.etud_moy_gen_ranks = moy_sem.comp_ranks_series(self.etud_moy_gen)
(
self.etud_moy_gen_ranks,
self.etud_moy_gen_ranks_int,
) = moy_sem.comp_ranks_series(self.etud_moy_gen)
for ue in self.formsemestre.query_ues():
moy_ue = self.etud_moy_ue[ue.id]
self.ue_rangs[ue.id] = (
moy_sem.comp_ranks_series(moy_ue),
moy_sem.comp_ranks_series(moy_ue)[0], # juste en chaine
int(moy_ue.count()),
)
# .count() -> nb of non NaN values

View File

@ -134,6 +134,7 @@ class Identite(db.Model):
# ScoDoc7 output_formators: (backward compat)
e["etudid"] = self.id
e["date_naissance"] = ndb.DateISOtoDMY(e["date_naissance"])
e["ne"] = {"M": "", "F": "ne"}.get(self.civilite, "(e)")
return {k: e[k] or "" for k in e} # convert_null_outputs_to_empty
def to_dict_bul(self, include_urls=True):

View File

@ -363,17 +363,17 @@ class FormSemestre(db.Model):
etudid, self.date_debut.isoformat(), self.date_fin.isoformat()
)
def get_inscrits(self, include_demdef=False, sorted=False) -> list[Identite]:
def get_inscrits(self, include_demdef=False, order=False) -> list[Identite]:
"""Liste des étudiants inscrits à ce semestre
Si include_demdef, tous les étudiants, avec les démissionnaires
et défaillants.
Si sorted, tri par clé sort_key
Si order, tri par clé sort_key
"""
if include_demdef:
etuds = [ins.etud for ins in self.inscriptions]
else:
etuds = [ins.etud for ins in self.inscriptions if ins.etat == scu.INSCRIT]
if sorted:
if order:
etuds.sort(key=lambda e: e.sort_key)
return etuds

View File

@ -188,7 +188,7 @@ def get_formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"):
bookmarks = {}
filigrannes = {}
i = 1
for etud in formsemestre.get_inscrits(include_demdef=True, sorted=True):
for etud in formsemestre.get_inscrits(include_demdef=True, order=True):
frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud(
formsemestre_id,
etud.id,

View File

@ -867,11 +867,7 @@ def fill_etuds_info(etuds, add_admission=True):
Si add_admission: ajoute au dict le schamps "admission" s'il n'y sont pas déjà.
"""
from app.scodoc import sco_formsemestre
from app.scodoc import sco_formsemestre_inscriptions
cnx = ndb.GetDBConnexion()
# open('/tmp/t','w').write( str(etuds) )
for etud in etuds:
etudid = etud["etudid"]
etud["dept"] = g.scodoc_dept
@ -894,49 +890,7 @@ def fill_etuds_info(etuds, add_admission=True):
etud.update(adr)
format_etud_ident(etud)
# Semestres dans lesquel il est inscrit
ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
{"etudid": etudid}
)
etud["ins"] = ins
sems = []
cursem = None # semestre "courant" ou il est inscrit
for i in ins:
sem = sco_formsemestre.get_formsemestre(i["formsemestre_id"])
if sco_formsemestre.sem_est_courant(sem):
cursem = sem
curi = i
sem["ins"] = i
sems.append(sem)
# trie les semestres par date de debut, le plus recent d'abord
# (important, ne pas changer (suivi cohortes))
sems.sort(key=itemgetter("dateord"), reverse=True)
etud["sems"] = sems
etud["cursem"] = cursem
if cursem:
etud["inscription"] = cursem["titremois"]
etud["inscriptionstr"] = "Inscrit en " + cursem["titremois"]
etud["inscription_formsemestre_id"] = cursem["formsemestre_id"]
etud["etatincursem"] = curi["etat"]
etud["situation"] = descr_situation_etud(etudid, etud["ne"])
# XXX est-ce utile ? sco_groups.etud_add_group_infos( etud, cursem)
else:
if etud["sems"]:
if etud["sems"][0]["dateord"] > time.strftime(
"%Y-%m-%d", time.localtime()
):
etud["inscription"] = "futur"
etud["situation"] = "futur élève"
else:
etud["inscription"] = "ancien"
etud["situation"] = "ancien élève"
else:
etud["inscription"] = "non inscrit"
etud["situation"] = etud["inscription"]
etud["inscriptionstr"] = etud["inscription"]
etud["inscription_formsemestre_id"] = None
# XXXetud['partitions'] = {} # ne va pas chercher les groupes des anciens semestres
etud["etatincursem"] = "?"
etud.update(etud_inscriptions_infos(etudid, etud["ne"]))
# nettoyage champs souvent vides
if etud.get("nomlycee"):
@ -974,8 +928,56 @@ def fill_etuds_info(etuds, add_admission=True):
etud["telephonemobilestr"] = ""
def descr_situation_etud(etudid, ne=""):
"""chaine decrivant la situation actuelle de l'etudiant"""
def etud_inscriptions_infos(etudid: int, ne="") -> dict:
"""Dict avec les informations sur les semestres passés et courant"""
from app.scodoc import sco_formsemestre
from app.scodoc import sco_formsemestre_inscriptions
etud = {}
# Semestres dans lesquel il est inscrit
ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
{"etudid": etudid}
)
etud["ins"] = ins
sems = []
cursem = None # semestre "courant" ou il est inscrit
for i in ins:
sem = sco_formsemestre.get_formsemestre(i["formsemestre_id"])
if sco_formsemestre.sem_est_courant(sem):
cursem = sem
curi = i
sem["ins"] = i
sems.append(sem)
# trie les semestres par date de debut, le plus recent d'abord
# (important, ne pas changer (suivi cohortes))
sems.sort(key=itemgetter("dateord"), reverse=True)
etud["sems"] = sems
etud["cursem"] = cursem
if cursem:
etud["inscription"] = cursem["titremois"]
etud["inscriptionstr"] = "Inscrit en " + cursem["titremois"]
etud["inscription_formsemestre_id"] = cursem["formsemestre_id"]
etud["etatincursem"] = curi["etat"]
etud["situation"] = descr_situation_etud(etudid, ne)
else:
if etud["sems"]:
if etud["sems"][0]["dateord"] > time.strftime("%Y-%m-%d", time.localtime()):
etud["inscription"] = "futur"
etud["situation"] = "futur élève"
else:
etud["inscription"] = "ancien"
etud["situation"] = "ancien élève"
else:
etud["inscription"] = "non inscrit"
etud["situation"] = etud["inscription"]
etud["inscriptionstr"] = etud["inscription"]
etud["inscription_formsemestre_id"] = None
etud["etatincursem"] = "?"
return etud
def descr_situation_etud(etudid: int, ne="") -> str:
"""chaîne décrivant la situation actuelle de l'étudiant"""
from app.scodoc import sco_formsemestre
cnx = ndb.GetDBConnexion()
@ -992,7 +994,7 @@ def descr_situation_etud(etudid, ne=""):
)
r = cursor.dictfetchone()
if not r:
situation = "non inscrit"
situation = "non inscrit" + ne
else:
sem = sco_formsemestre.get_formsemestre(r["formsemestre_id"])
if r["etat"] == "I":

View File

@ -36,7 +36,7 @@ import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app import log
from app.scodoc.scolog import logdb
from app.scodoc import sco_cache
from app.scodoc import sco_cache, sco_etud
from app.scodoc import sco_formsemestre
from app.scodoc import sco_formations
from app.scodoc.sco_codes_parcours import (
@ -319,9 +319,6 @@ class SituationEtudParcoursGeneric(object):
sont validés. En sortie, sem_idx_set contient ceux qui n'ont pas été validés."""
for sem in self.get_semestres():
if sem["formation_code"] == self.formation.formation_code:
# nt = sco_cache.NotesTableCache.get(
# sem["formsemestre_id"]
# ) # > get_etud_decision_sem
formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"])
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
decision = nt.get_etud_decision_sem(self.etudid)
@ -333,15 +330,16 @@ class SituationEtudParcoursGeneric(object):
def _comp_semestres(self):
# etud['sems'] est trie par date decroissante (voir fill_etuds_info)
if not "sems" in self.etud:
self.etud["sems"] = sco_etud.etud_inscriptions_infos(
self.etud["etudid"], self.etud["ne"]
)["sems"]
sems = self.etud["sems"][:] # copy
sems.reverse()
# Nb max d'UE et acronymes
ue_acros = {} # acronyme ue : 1
nb_max_ue = 0
for sem in sems:
# nt = sco_cache.NotesTableCache.get(
# sem["formsemestre_id"]
# ) # > get_ues_stat_dict
formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"])
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
ues = nt.get_ues_stat_dict(filter_sport=True)

View File

@ -31,26 +31,30 @@ import time
from openpyxl.styles.numbers import FORMAT_NUMBER_00
from flask import flash
from flask import request
from flask_login import current_user
import app.scodoc.sco_utils as scu
from app.comp import res_sem
from app.comp.res_common import NotesTableCompat
from app.models import FormSemestre, Identite
from app.scodoc import sco_abs
from app.scodoc import sco_codes_parcours
from app.scodoc import sco_groups
from app.scodoc import sco_cache
from app.scodoc import sco_etud
from app.scodoc import sco_excel
from app.scodoc import sco_formsemestre
from app.scodoc import sco_parcours_dut
from app.scodoc import sco_codes_parcours
import sco_version
from app.scodoc import sco_etud
from app.scodoc import sco_preferences
import app.scodoc.sco_utils as scu
import sco_version
def feuille_preparation_jury(formsemestre_id):
"Feuille excel pour preparation des jurys"
nt = sco_cache.NotesTableCache.get(formsemestre_id)
etudids = nt.get_etudids(sorted=True) # tri par moy gen
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
etuds: Identite = nt.get_inscrits(order_by="moy") # tri par moy gen
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
etud_groups = sco_groups.formsemestre_get_etud_groupnames(formsemestre_id)
@ -73,64 +77,65 @@ def feuille_preparation_jury(formsemestre_id):
groupestd = {} # etudid : nom groupe principal
nbabs = {}
nbabsjust = {}
for etudid in etudids:
info = sco_etud.get_etud_info(etudid=etudid, filled=True)
if not info:
continue # should not occur...
etud = info[0]
Se = sco_parcours_dut.SituationEtudParcours(etud, formsemestre_id)
for etud in etuds:
Se = sco_parcours_dut.SituationEtudParcours(
etud.to_dict_scodoc7(), formsemestre_id
)
if Se.prev:
ntp = sco_cache.NotesTableCache.get(Se.prev["formsemestre_id"])
formsemestre_prev = FormSemestre.query.get_or_404(
Se.prev["formsemestre_id"]
)
ntp: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre_prev)
for ue in ntp.get_ues_stat_dict(filter_sport=True):
ue_status = ntp.get_etud_ue_status(etudid, ue["ue_id"])
ue_status = ntp.get_etud_ue_status(etud.id, ue["ue_id"])
ue_code_s = (
ue["ue_code"] + "_%s" % ntp.sem["semestre_id"]
) # code indentifiant l'UE
prev_moy_ue[ue_code_s][etudid] = ue_status["moy"] if ue_status else ""
prev_moy_ue[ue_code_s][etud.id] = ue_status["moy"] if ue_status else ""
prev_ue_acro[ue_code_s] = (ue["numero"], ue["acronyme"], ue["titre"])
prev_moy[etudid] = ntp.get_etud_moy_gen(etudid)
prev_decision = ntp.get_etud_decision_sem(etudid)
prev_moy[etud.id] = ntp.get_etud_moy_gen(etud.id)
prev_decision = ntp.get_etud_decision_sem(etud.id)
if prev_decision:
prev_code[etudid] = prev_decision["code"]
prev_code[etud.id] = prev_decision["code"]
if prev_decision["compense_formsemestre_id"]:
prev_code[etudid] += "+" # indique qu'il a servi a compenser
prev_code[etud.id] += "+" # indique qu'il a servi a compenser
moy[etudid] = nt.get_etud_moy_gen(etudid)
moy[etud.id] = nt.get_etud_moy_gen(etud.id)
for ue in nt.get_ues_stat_dict(filter_sport=True):
ue_status = nt.get_etud_ue_status(etudid, ue["ue_id"])
ue_status = nt.get_etud_ue_status(etud.id, ue["ue_id"])
ue_code_s = ue["ue_code"] + "_%s" % nt.sem["semestre_id"]
moy_ue[ue_code_s][etudid] = ue_status["moy"] if ue_status else ""
moy_ue[ue_code_s][etud.id] = ue_status["moy"] if ue_status else ""
ue_acro[ue_code_s] = (ue["numero"], ue["acronyme"], ue["titre"])
if Se.prev:
try:
moy_inter[etudid] = (moy[etudid] + prev_moy[etudid]) / 2.0
moy_inter[etud.id] = (moy[etud.id] + prev_moy[etud.id]) / 2.0
except:
pass
decision = nt.get_etud_decision_sem(etudid)
decision = nt.get_etud_decision_sem(etud.id)
if decision:
code[etudid] = decision["code"]
code[etud.id] = decision["code"]
if decision["compense_formsemestre_id"]:
code[etudid] += "+" # indique qu'il a servi a compenser
assidu[etudid] = {False: "Non", True: "Oui"}.get(decision["assidu"], "")
code[etud.id] += "+" # indique qu'il a servi a compenser
assidu[etud.id] = {False: "Non", True: "Oui"}.get(decision["assidu"], "")
aut_list = sco_parcours_dut.formsemestre_get_autorisation_inscription(
etudid, formsemestre_id
etud.id, formsemestre_id
)
autorisations[etudid] = ", ".join(["S%s" % x["semestre_id"] for x in aut_list])
autorisations[etud.id] = ", ".join(["S%s" % x["semestre_id"] for x in aut_list])
# parcours:
parcours[etudid] = Se.get_parcours_descr()
parcours[etud.id] = Se.get_parcours_descr()
# groupe principal (td)
groupestd[etudid] = ""
for s in etud["sems"]:
groupestd[etud.id] = ""
for s in Se.etud["sems"]:
if s["formsemestre_id"] == formsemestre_id:
groupestd[etudid] = etud_groups.get(etudid, {}).get(
groupestd[etud.id] = etud_groups.get(etud.id, {}).get(
main_partition_id, ""
)
# absences:
e_nbabs, e_nbabsjust = sco_abs.get_abs_count(etudid, sem)
nbabs[etudid] = e_nbabs
nbabsjust[etudid] = e_nbabs - e_nbabsjust
e_nbabs, e_nbabsjust = sco_abs.get_abs_count(etud.id, sem)
nbabs[etud.id] = e_nbabs
nbabsjust[etud.id] = e_nbabs - e_nbabsjust
# Codes des UE "semestre précédent":
ue_prev_codes = list(prev_moy_ue.keys())
@ -222,26 +227,26 @@ def feuille_preparation_jury(formsemestre_id):
return x
i = 1 # numero etudiant
for etudid in etudids:
for etud in etuds:
cells = []
etud = nt.identdict[etudid]
cells.append(ws.make_cell(str(i)))
if sco_preferences.get_preference("prepa_jury_nip"):
cells.append(ws.make_cell(etud["code_nip"]))
cells.append(ws.make_cell(etud.code_nip))
if sco_preferences.get_preference("prepa_jury_ine"):
cells.append(ws.make_cell(etud["code_ine"]))
cells.append(ws.make_cell(etud.code_ine))
admission = etud.admission.first()
cells += ws.make_row(
[
etudid,
etud["civilite_str"],
sco_etud.format_nom(etud["nom"]),
sco_etud.format_prenom(etud["prenom"]),
etud["date_naissance"],
etud["bac"],
etud["specialite"],
etud["classement"],
parcours[etudid],
groupestd[etudid],
etud.id,
etud.civilite_str,
sco_etud.format_nom(etud.nom),
sco_etud.format_prenom(etud.prenom),
etud.date_naissance,
admission.bac,
admission.specialite,
admission.classement,
parcours[etud.id],
groupestd[etud.id],
]
)
co = len(cells)
@ -249,33 +254,35 @@ def feuille_preparation_jury(formsemestre_id):
for ue_acro in ue_prev_codes:
cells.append(
ws.make_cell(
fmt(prev_moy_ue.get(ue_acro, {}).get(etudid, "")), style_note
fmt(prev_moy_ue.get(ue_acro, {}).get(etud.id, "")), style_note
)
)
co += 1
cells.append(
ws.make_cell(fmt(prev_moy.get(etudid, "")), style_bold)
ws.make_cell(fmt(prev_moy.get(etud.id, "")), style_bold)
) # moy gen prev
cells.append(
ws.make_cell(fmt(prev_code.get(etudid, "")), style_moy)
ws.make_cell(fmt(prev_code.get(etud.id, "")), style_moy)
) # decision prev
co += 2
for ue_acro in ue_codes:
cells.append(
ws.make_cell(fmt(moy_ue.get(ue_acro, {}).get(etudid, "")), style_note)
ws.make_cell(fmt(moy_ue.get(ue_acro, {}).get(etud.id, "")), style_note)
)
co += 1
cells.append(ws.make_cell(fmt(moy.get(etudid, "")), style_note_bold)) # moy gen
cells.append(
ws.make_cell(fmt(moy.get(etud.id, "")), style_note_bold)
) # moy gen
co += 1
if moy_inter:
cells.append(ws.make_cell(fmt(moy_inter.get(etudid, "")), style_note))
cells.append(ws.make_cell(str(nbabs.get(etudid, "")), style_center))
cells.append(ws.make_cell(str(nbabsjust.get(etudid, "")), style_center))
cells.append(ws.make_cell(fmt(moy_inter.get(etud.id, "")), style_note))
cells.append(ws.make_cell(str(nbabs.get(etud.id, "")), style_center))
cells.append(ws.make_cell(str(nbabsjust.get(etud.id, "")), style_center))
if code:
cells.append(ws.make_cell(code.get(etudid, ""), style_moy))
cells.append(ws.make_cell(autorisations.get(etudid, ""), style_moy))
# l.append(assidu.get(etudid, ''))
cells.append(ws.make_cell(code.get(etud.id, ""), style_moy))
cells.append(ws.make_cell(autorisations.get(etud.id, ""), style_moy))
# l.append(assidu.get(etud.id, ''))
ws.append_row(cells)
i += 1
#
@ -319,6 +326,7 @@ def feuille_preparation_jury(formsemestre_id):
)
)
xls = ws.generate()
flash("Feuille préparation jury générée")
return scu.send_file(
xls,
f"PrepaJury{sn}",

View File

@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION = "9.2a-54"
SCOVERSION = "9.1.54"
SCONAME = "ScoDoc"