1
0
forked from ScoDoc/ScoDoc

préparatifs/refactoring

This commit is contained in:
Emmanuel Viennet 2021-12-04 21:04:09 +01:00
parent c455f6261f
commit b1bc8b3f41
11 changed files with 182 additions and 67 deletions

View File

@ -49,6 +49,32 @@ class Identite(db.Model):
def __repr__(self):
return f"<Etud {self.id} {self.nom} {self.prenom}>"
def civilite_str(self):
"""returns 'M.' ou 'Mme' ou '' (pour le genre neutre,
personnes ne souhaitant pas d'affichage).
"""
return {"M": "M.", "F": "Mme", "X": ""}[self.civilite]
def nom_disp(self):
"nom à afficher"
if self.nom_usuel:
return (
(self.nom_usuel + " (" + self.nom + ")") if self.nom else self.nom_usuel
)
else:
return self.nom
def inscription_courante(self):
"""La première inscription à un formsemestre _actuellement_ en cours.
None s'il n'y en a pas (ou plus, ou pas encore).
"""
r = [
ins
for ins in self.formsemestre_inscriptions
if ins.formsemestre.est_courant()
]
return r[0] if r else None
class Adresse(db.Model):
"""Adresse d'un étudiant

View File

@ -241,7 +241,7 @@ class Module(db.Model):
def is_apc(self):
"True si module SAÉ ou Ressource"
return scu.ModuleType(self.module_type) in {
return self.module_type and scu.ModuleType(self.module_type) in {
scu.ModuleType.RESSOURCE,
scu.ModuleType.SAE,
}

View File

@ -2,6 +2,7 @@
"""ScoDoc models: formsemestre
"""
import datetime
from typing import Any
import flask_sqlalchemy
@ -13,11 +14,14 @@ from app.models import CODE_STR_LEN
from app.models import UniteEns
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
from app.scodoc import sco_evaluation_db
from app.models.formations import UniteEns, Module
from app.models.moduleimpls import ModuleImpl
from app.models.etudiants import Identite
from app.scodoc import sco_codes_parcours
from app.scodoc import sco_preferences
from app.scodoc.sco_vdi import ApoEtapeVDI
class FormSemestre(db.Model):
@ -40,7 +44,7 @@ class FormSemestre(db.Model):
) # False si verrouillé
modalite = db.Column(
db.String(SHORT_STR_LEN), db.ForeignKey("notes_form_modalites.modalite")
)
) # "FI", "FAP", "FC", ...
# gestion compensation sem DUT:
gestion_compensation = db.Column(
db.Boolean(), nullable=False, default=False, server_default="false"
@ -89,7 +93,12 @@ class FormSemestre(db.Model):
viewonly=True,
lazy="dynamic",
)
responsables = db.relationship(
"User",
secondary="notes_formsemestre_responsables",
lazy=True,
backref=db.backref("formsemestres", lazy=True),
)
# Ancien id ScoDoc7 pour les migrations de bases anciennes
# ne pas utiliser après migrate_scodoc7_dept_archives
scodoc7_id = db.Column(db.Text(), nullable=True)
@ -99,6 +108,18 @@ class FormSemestre(db.Model):
if self.modalite is None:
self.modalite = FormationModalite.DEFAULT_MODALITE
def to_dict(self):
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
# ScoDoc7 output_formators: (backward compat)
d["formsemestre_id"] = self.id
d["date_debut"] = (
self.date_debut.strftime("%d/%m/%Y") if self.date_debut else ""
)
d["date_fin"] = self.date_fin.strftime("%d/%m/%Y") if self.date_fin else ""
d["responsables"] = [u.id for u in self.responsables]
return d
def query_ues(self, with_sport=False) -> flask_sqlalchemy.BaseQuery:
"""UE des modules de ce semestre.
- Formations classiques: les UEs auxquelles appartiennent
@ -120,6 +141,76 @@ class FormSemestre(db.Model):
sem_ues = sem_ues.filter(UniteEns.type != sco_codes_parcours.UE_SPORT)
return sem_ues
def est_courant(self) -> bool:
"""Vrai si la date actuelle (now) est dans le semestre
(les dates de début et fin sont incluses)
"""
today = datetime.date.today()
return (self.date_debut <= today) and (today <= self.date_fin)
def est_decale(self):
"""Vrai si semestre "décalé"
c'est à dire semestres impairs commençant entre janvier et juin
et les pairs entre juillet et decembre
"""
if self.semestre_id <= 0:
return False # formations sans semestres
return (self.semestre_id % 2 and self.date_debut.month <= 6) or (
not self.semestre_id % 2 and self.date_debut.month > 6
)
def etapes_apo_str(self) -> str:
"""Chaine décrivant les étapes de ce semestre
ex: "V1RT, V1RT3, V1RT4"
"""
if not self.etapes:
return ""
return ", ".join([str(x.etape_apo) for x in self.etapes])
def responsables_str(self, abbrev_prenom=True) -> str:
"""chaîne "J. Dupond, X. Martin"
ou "Jacques Dupond, Xavier Martin"
"""
if not self.responsables:
return ""
if abbrev_prenom:
return ", ".join([u.get_prenomnom() for u in self.responsables])
else:
return ", ".join([u.get_nomcomplet() for u in self.responsables])
def session_id(self) -> str:
"""identifiant externe de semestre de formation
Exemple: RT-DUT-FI-S1-ANNEE
DEPT-TYPE-MODALITE+-S?|SPECIALITE
TYPE=DUT|LP*|M*
MODALITE=FC|FI|FA (si plusieurs, en inverse alpha)
SPECIALITE=[A-Z]+ EON,ASSUR, ... (si pas Sn ou SnD)
ANNEE=annee universitaire de debut (exemple: un S2 de 2013-2014 sera S2-2013)
"""
imputation_dept = sco_preferences.get_preference("ImputationDept", self.id)
if not imputation_dept:
imputation_dept = sco_preferences.get_preference("DeptName")
imputation_dept = imputation_dept.upper()
parcours_name = self.formation.get_parcours().NAME
modalite = self.modalite
# exception pour code Apprentissage:
modalite = (modalite or "").replace("FAP", "FA").replace("APP", "FA")
if self.semestre_id > 0:
decale = "D" if self.est_decale() else ""
semestre_id = f"S{self.semestre_id}{decale}"
else:
semestre_id = self.formation.code_specialite or ""
annee_sco = str(
scu.annee_scolaire_debut(self.date_debut.year, self.date_debut.month)
)
return scu.sanitize_string(
"-".join((imputation_dept, parcours_name, modalite, semestre_id, annee_sco))
)
# Association id des utilisateurs responsables (aka directeurs des etudes) du semestre
notes_formsemestre_responsables = db.Table(
@ -144,6 +235,12 @@ class FormsemestreEtape(db.Model):
)
etape_apo = db.Column(db.String(APO_CODE_STR_LEN))
def __repr__(self):
return f"<Etape {self.id} apo={self.etape_apo}>"
def as_apovdi(self):
return ApoEtapeVDI(self.etape_apo)
class FormationModalite(db.Model):
"""Modalités de formation, utilisées pour la présentation

View File

@ -1028,20 +1028,26 @@ def get_abs_count(etudid, sem):
tuple (nb abs non justifiées, nb abs justifiées)
Utilise un cache.
"""
date_debut = sem["date_debut_iso"]
date_fin = sem["date_fin_iso"]
key = str(etudid) + "_" + date_debut + "_" + date_fin
return get_abs_count_in_interval(etudid, sem["date_debut_iso"], sem["date_fin_iso"])
def get_abs_count_in_interval(etudid, date_debut_iso, date_fin_iso):
"""Les comptes d'absences de cet étudiant entre ces deux dates, incluses:
tuple (nb abs non justifiées, nb abs justifiées)
Utilise un cache.
"""
key = str(etudid) + "_" + date_debut_iso + "_" + date_fin_iso
r = sco_cache.AbsSemEtudCache.get(key)
if not r:
nb_abs = count_abs( # was CountAbs XXX
nb_abs = count_abs(
etudid=etudid,
debut=date_debut,
fin=date_fin,
debut=date_debut_iso,
fin=date_fin_iso,
)
nb_abs_just = count_abs_just( # XXX was CountAbsJust
nb_abs_just = count_abs_just(
etudid=etudid,
debut=date_debut,
fin=date_fin,
debut=date_debut_iso,
fin=date_fin_iso,
)
r = (nb_abs, nb_abs_just)
ans = sco_cache.AbsSemEtudCache.set(key, r)

View File

@ -584,7 +584,7 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
H.append(
f"""
<ul>
<li>{descr_refcomp} <a class="stdlink" href="{url_for('notes.refcomp_assoc',
<li>{descr_refcomp} <a class="stdlink" href="{url_for('notes.refcomp_assoc_formation',
scodoc_dept=g.scodoc_dept, formation_id=formation_id)
}">{msg_refcomp}</a>
</li>

View File

@ -47,36 +47,6 @@ from app.scodoc import sco_preferences
from app.scodoc.scolog import logdb
from app.scodoc.TrivialFormulator import TrivialFormulator
MONTH_NAMES_ABBREV = [
"Jan ",
"Fév ",
"Mars",
"Avr ",
"Mai ",
"Juin",
"Jul ",
"Août",
"Sept",
"Oct ",
"Nov ",
"Déc ",
]
MONTH_NAMES = [
"janvier",
"février",
"mars",
"avril",
"mai",
"juin",
"juillet",
"août",
"septembre",
"octobre",
"novembre",
"décembre",
]
def format_etud_ident(etud):
"""Format identite de l'étudiant (modifié en place)

View File

@ -194,7 +194,7 @@ def _formsemestre_enrich(sem):
sem["titreannee"] += "-" + annee_fin
sem["annee"] += "-" + annee_fin
# et les dates sous la forme "oct 2007 - fev 2008"
months = sco_etud.MONTH_NAMES_ABBREV
months = scu.MONTH_NAMES_ABBREV
if mois_debut:
mois_debut = months[int(mois_debut) - 1]
if mois_fin:
@ -470,7 +470,7 @@ def sem_une_annee(sem):
return debut == fin
def sem_est_courant(sem):
def sem_est_courant(sem): # -> FormSemestre.est_courant
"""Vrai si la date actuelle (now) est dans le semestre (les dates de début et fin sont incluses)"""
now = time.strftime("%Y-%m-%d")
debut = ndb.DateDMYtoISO(sem["date_debut"])

View File

@ -1621,28 +1621,14 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None):
# ----- identification externe des sessions (pour SOJA et autres logiciels)
def get_formsemestre_session_id(sem, F, parcours):
"""Identifiant de session pour ce semestre
Exemple: RT-DUT-FI-S1-ANNEE
DEPT-TYPE-MODALITE+-S?|SPECIALITE
TYPE=DUT|LP*|M*
MODALITE=FC|FI|FA (si plusieurs, en inverse alpha)
SPECIALITE=[A-Z]+ EON,ASSUR, ... (si pas Sn ou SnD)
ANNEE=annee universitaire de debut (exemple: un S2 de 2013-2014 sera S2-2013)
Obsolete: vooir FormSemestre.session_id() #sco7
"""
# sem = sco_formsemestre.get_formsemestre( formsemestre_id)
# F = sco_formations.formation_list( args={ 'formation_id' : sem['formation_id'] } )[0]
# parcours = sco_codes_parcours.get_parcours_from_code(F['type_parcours'])
ImputationDept = sco_preferences.get_preference(
imputation_dept = sco_preferences.get_preference(
"ImputationDept", sem["formsemestre_id"]
)
if not ImputationDept:
ImputationDept = sco_preferences.get_preference("DeptName")
ImputationDept = ImputationDept.upper()
if not imputation_dept:
imputation_dept = sco_preferences.get_preference("DeptName")
imputation_dept = imputation_dept.upper()
parcours_type = parcours.NAME
modalite = sem["modalite"]
modalite = (
@ -1656,5 +1642,5 @@ def get_formsemestre_session_id(sem, F, parcours):
annee_sco = str(scu.annee_scolaire_debut(sem["annee_debut"], sem["mois_debut_ord"]))
return scu.sanitize_string(
"-".join((ImputationDept, parcours_type, modalite, semestre_id, annee_sco))
"-".join((imputation_dept, parcours_type, modalite, semestre_id, annee_sco))
)

View File

@ -151,7 +151,7 @@ def scolar_news_summary(n=5):
n[k] = _scolar_news_editor.output_formators[k](n[k])
# date resumee
j, m = n["date"].split("/")[:2]
mois = sco_etud.MONTH_NAMES_ABBREV[int(m) - 1]
mois = scu.MONTH_NAMES_ABBREV[int(m) - 1]
n["formatted_date"] = "%s %s %s" % (j, mois, n["hm"])
# indication semestre si ajout notes:
infos = _get_formsemestre_infos_from_news(n)

View File

@ -212,7 +212,7 @@ def placement_eval_selectetuds(evaluation_id):
)
return runner.exec_placement() # calcul et generation du fichier
htmls = [
html_sco_header.sco_header(init_jquery_ui=True),
html_sco_header.sco_header(),
sco_evaluations.evaluation_describe(evaluation_id=evaluation_id),
"<h3>Placement et émargement des étudiants</h3>",
render_template("scodoc/forms/placement.html", form=form),

View File

@ -127,6 +127,36 @@ EVALUATION_NORMALE = 0
EVALUATION_RATTRAPAGE = 1
EVALUATION_SESSION2 = 2
MONTH_NAMES_ABBREV = (
"Jan ",
"Fév ",
"Mars",
"Avr ",
"Mai ",
"Juin",
"Jul ",
"Août",
"Sept",
"Oct ",
"Nov ",
"Déc ",
)
MONTH_NAMES = (
"janvier",
"février",
"mars",
"avril",
"mai",
"juin",
"juillet",
"août",
"septembre",
"octobre",
"novembre",
"décembre",
)
def fmt_note(val, note_max=None, keep_numeric=False):
"""conversion note en str pour affichage dans tables HTML ou PDF.