forked from ScoDoc/ScoDoc
Compare commits
20 Commits
b7ab10bf4e
...
e44a5ee55d
Author | SHA1 | Date |
---|---|---|
Emmanuel Viennet | e44a5ee55d | |
Emmanuel Viennet | a747ed22e2 | |
Emmanuel Viennet | 5d0a932634 | |
Emmanuel Viennet | 2b150cf521 | |
Emmanuel Viennet | 5a5ddcacd7 | |
Emmanuel Viennet | 3f6e65b9da | |
Emmanuel Viennet | 5eba6170a5 | |
Emmanuel Viennet | bd9bf87112 | |
Emmanuel Viennet | a0e2af481f | |
Emmanuel Viennet | 42e8f97441 | |
Emmanuel Viennet | 8ec0171ca0 | |
Emmanuel Viennet | 6dfab2d843 | |
Emmanuel Viennet | 523ec59833 | |
Emmanuel Viennet | bde6325391 | |
Emmanuel Viennet | 0577347622 | |
Emmanuel Viennet | 28d46e413d | |
Emmanuel Viennet | 126ea0741a | |
Emmanuel Viennet | a5b5f49f76 | |
Emmanuel Viennet | 3e0b19c4a8 | |
Emmanuel Viennet | 1dd5187fae |
|
@ -315,12 +315,6 @@ def create_app(config_class=DevConfig):
|
|||
app.register_error_handler(503, postgresql_server_error)
|
||||
app.register_error_handler(APIInvalidParams, handle_invalid_usage)
|
||||
|
||||
# Add some globals
|
||||
# previously in Flask-Bootstrap:
|
||||
app.jinja_env.globals["bootstrap_is_hidden_field"] = lambda field: isinstance(
|
||||
field, HiddenField
|
||||
)
|
||||
|
||||
from app.auth import bp as auth_bp
|
||||
|
||||
app.register_blueprint(auth_bp, url_prefix="/auth")
|
||||
|
@ -338,8 +332,15 @@ def create_app(config_class=DevConfig):
|
|||
from app.api import api_bp
|
||||
from app.api import api_web_bp
|
||||
|
||||
# Jinja2 configuration
|
||||
# Enable autoescaping of all templates, including .j2
|
||||
app.jinja_env.autoescape = select_autoescape(default_for_string=True, default=True)
|
||||
app.jinja_env.trim_blocks = True
|
||||
app.jinja_env.lstrip_blocks = True
|
||||
# previously in Flask-Bootstrap:
|
||||
app.jinja_env.globals["bootstrap_is_hidden_field"] = lambda field: isinstance(
|
||||
field, HiddenField
|
||||
)
|
||||
|
||||
# https://scodoc.fr/ScoDoc
|
||||
app.register_blueprint(scodoc_bp)
|
||||
|
|
|
@ -329,6 +329,8 @@ def desassoc_ue_niveau(ue_id: int):
|
|||
ue.niveau_competence = None
|
||||
db.session.add(ue)
|
||||
db.session.commit()
|
||||
# Invalidation du cache
|
||||
ue.formation.invalidate_cached_sems()
|
||||
log(f"desassoc_ue_niveau: {ue}")
|
||||
if g.scodoc_dept:
|
||||
# "usage web"
|
||||
|
|
|
@ -21,7 +21,7 @@ def form_ue_choix_parcours(ue: UniteEns) -> str:
|
|||
return ""
|
||||
ref_comp = ue.formation.referentiel_competence
|
||||
if ref_comp is None:
|
||||
return f"""<div class="ue_advanced">
|
||||
return f"""<div class="scobox ue_advanced">
|
||||
<div class="warning">Pas de référentiel de compétence associé à cette formation !</div>
|
||||
<div><a class="stdlink" href="{ url_for('notes.refcomp_assoc_formation',
|
||||
scodoc_dept=g.scodoc_dept, formation_id=ue.formation.id)
|
||||
|
@ -31,8 +31,8 @@ def form_ue_choix_parcours(ue: UniteEns) -> str:
|
|||
|
||||
H = [
|
||||
"""
|
||||
<div class="ue_advanced">
|
||||
<h3>Parcours du BUT</h3>
|
||||
<div class="scobox ue_advanced">
|
||||
<div class="scobox-title">Parcours du BUT</div>
|
||||
"""
|
||||
]
|
||||
# Choix des parcours
|
||||
|
@ -43,7 +43,6 @@ def form_ue_choix_parcours(ue: UniteEns) -> str:
|
|||
ue.get_ects(parcour, only_parcours=True) for parcour in ref_comp.parcours
|
||||
} != {None}
|
||||
for parcour in ref_comp.parcours:
|
||||
ects_parcour = ue.get_ects(parcour)
|
||||
ects_parcour_txt = (
|
||||
f" ({ue.get_ects(parcour):.3g} ects)" if ects_differents else ""
|
||||
)
|
||||
|
|
|
@ -229,7 +229,7 @@ class BulletinBUT:
|
|||
if res.modimpl_inscr_df[modimpl.id][etud.id]: # si inscrit
|
||||
d[modimpl.module.code] = {
|
||||
"id": modimpl.id,
|
||||
"titre": modimpl.module.titre,
|
||||
"titre": modimpl.module.titre_str(),
|
||||
"code_apogee": modimpl.module.code_apogee,
|
||||
"url": (
|
||||
url_for(
|
||||
|
@ -540,9 +540,9 @@ class BulletinBUT:
|
|||
|
||||
d.update(infos)
|
||||
# --- Rangs
|
||||
d[
|
||||
"rang_nt"
|
||||
] = f"{d['semestre']['rang']['value']} / {d['semestre']['rang']['total']}"
|
||||
d["rang_nt"] = (
|
||||
f"{d['semestre']['rang']['value']} / {d['semestre']['rang']['total']}"
|
||||
)
|
||||
d["rang_txt"] = "Rang " + d["rang_nt"]
|
||||
|
||||
d.update(sco_bulletins.make_context_dict(self.res.formsemestre, d["etud"]))
|
||||
|
|
|
@ -31,6 +31,7 @@ from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard
|
|||
from app.scodoc.sco_logos import Logo
|
||||
from app.scodoc.sco_pdf import PDFLOCK, SU
|
||||
from app.scodoc.sco_preferences import SemPreferences
|
||||
from app.scodoc import sco_utils as scu
|
||||
|
||||
|
||||
def make_bulletin_but_court_pdf(
|
||||
|
@ -343,9 +344,11 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||
for mod in self.bul[mod_type]:
|
||||
row = [mod, bul[mod_type][mod]["titre"]]
|
||||
row += [
|
||||
bul["ues"][ue][mod_type][mod]["moyenne"]
|
||||
if mod in bul["ues"][ue][mod_type]
|
||||
else ""
|
||||
(
|
||||
bul["ues"][ue][mod_type][mod]["moyenne"]
|
||||
if mod in bul["ues"][ue][mod_type]
|
||||
else ""
|
||||
)
|
||||
for ue in self.ues_acronyms
|
||||
]
|
||||
rows.append(row)
|
||||
|
@ -523,7 +526,7 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||
if self.bul["semestre"].get("decision_annee", None):
|
||||
txt += f"""
|
||||
Décision saisie le {
|
||||
datetime.datetime.fromisoformat(self.bul["semestre"]["decision_annee"]["date"]).strftime("%d/%m/%Y")
|
||||
datetime.datetime.fromisoformat(self.bul["semestre"]["decision_annee"]["date"]).strftime(scu.DATE_FMT)
|
||||
}, année BUT{self.bul["semestre"]["decision_annee"]["ordre"]}
|
||||
<b>{self.bul["semestre"]["decision_annee"]["code"]}</b>.
|
||||
<br/>
|
||||
|
|
|
@ -269,7 +269,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
|
|||
date_capitalisation = ue.get("date_capitalisation")
|
||||
if date_capitalisation:
|
||||
fields_bmr.append(
|
||||
f"""Capitalisée le {date_capitalisation.strftime("%d/%m/%Y")}"""
|
||||
f"""Capitalisée le {date_capitalisation.strftime(scu.DATE_FMT)}"""
|
||||
)
|
||||
t = {
|
||||
"titre": " - ".join(fields_bmr),
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
##############################################################################
|
||||
# ScoDoc
|
||||
# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved.
|
||||
# See LICENSE
|
||||
##############################################################################
|
||||
|
||||
"""Code expérimental: si deux référentiel sont presques identiques
|
||||
(mêmes compétences, niveaux, parcours)
|
||||
essaie de changer une formation de référentiel.
|
||||
"""
|
||||
|
||||
from app import clear_scodoc_cache, db
|
||||
|
||||
from app.models import (
|
||||
ApcParcours,
|
||||
ApcReferentielCompetences,
|
||||
ApcValidationRCUE,
|
||||
Formation,
|
||||
FormSemestreInscription,
|
||||
UniteEns,
|
||||
)
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
|
||||
|
||||
def formation_change_referentiel(
|
||||
formation: Formation, new_ref: ApcReferentielCompetences
|
||||
):
|
||||
"""Try to change ref."""
|
||||
if not formation.referentiel_competence:
|
||||
raise ScoValueError("formation non associée à un référentiel")
|
||||
if not isinstance(new_ref, ApcReferentielCompetences):
|
||||
raise ScoValueError("nouveau référentiel invalide")
|
||||
|
||||
r = formation.referentiel_competence.map_to_other_referentiel(new_ref)
|
||||
if isinstance(r, str):
|
||||
raise ScoValueError(f"référentiels incompatibles: {r}")
|
||||
parcours_map, competences_map, niveaux_map = r
|
||||
|
||||
formation.referentiel_competence = new_ref
|
||||
db.session.add(formation)
|
||||
# UEs - Niveaux et UEs - parcours
|
||||
for ue in formation.ues:
|
||||
if ue.niveau_competence:
|
||||
ue.niveau_competence_id = niveaux_map[ue.niveau_competence_id]
|
||||
db.session.add(ue)
|
||||
if ue.parcours:
|
||||
new_list = [ApcParcours.query.get(parcours_map[p.id]) for p in ue.parcours]
|
||||
ue.parcours.clear()
|
||||
ue.parcours.extend(new_list)
|
||||
db.session.add(ue)
|
||||
# Modules / parcours et app_critiques
|
||||
for module in formation.modules:
|
||||
if module.parcours:
|
||||
new_list = [
|
||||
ApcParcours.query.get(parcours_map[p.id]) for p in module.parcours
|
||||
]
|
||||
module.parcours.clear()
|
||||
module.parcours.extend(new_list)
|
||||
db.session.add(module)
|
||||
if module.app_critiques: # efface les apprentissages critiques
|
||||
module.app_critiques.clear()
|
||||
db.session.add(module)
|
||||
# ApcValidationRCUE
|
||||
for valid_rcue in ApcValidationRCUE.query.join(
|
||||
UniteEns, UniteEns.id == ApcValidationRCUE.ue1_id
|
||||
).filter_by(formation_id=formation.id):
|
||||
if valid_rcue.parcour:
|
||||
valid_rcue.parcour_id = parcours_map[valid_rcue.parcour.id]
|
||||
db.session.add(valid_rcue)
|
||||
for valid_rcue in ApcValidationRCUE.query.join(
|
||||
UniteEns, UniteEns.id == ApcValidationRCUE.ue2_id
|
||||
).filter_by(formation_id=formation.id):
|
||||
if valid_rcue.parcour:
|
||||
valid_rcue.parcour_id = parcours_map[valid_rcue.parcour.id]
|
||||
db.session.add(valid_rcue)
|
||||
# FormSemestre / parcours_formsemestre
|
||||
for formsemestre in formation.formsemestres:
|
||||
new_list = [
|
||||
ApcParcours.query.get(parcours_map[p.id]) for p in formsemestre.parcours
|
||||
]
|
||||
formsemestre.parcours.clear()
|
||||
formsemestre.parcours.extend(new_list)
|
||||
db.session.add(formsemestre)
|
||||
# FormSemestreInscription.parcour_id
|
||||
for inscr in FormSemestreInscription.query.filter_by(
|
||||
formsemestre_id=formsemestre.id
|
||||
).filter(FormSemestreInscription.parcour_id != None):
|
||||
if inscr.parcour_id is not None:
|
||||
inscr.parcour_id = parcours_map[inscr.parcour_id]
|
||||
#
|
||||
db.session.commit()
|
||||
clear_scodoc_cache()
|
|
@ -10,9 +10,11 @@
|
|||
from flask_wtf import FlaskForm
|
||||
from flask_wtf.file import FileField, FileAllowed
|
||||
from wtforms import SelectField, SubmitField
|
||||
from wtforms.validators import DataRequired
|
||||
|
||||
|
||||
class FormationRefCompForm(FlaskForm):
|
||||
"Choix d'un référentiel"
|
||||
referentiel_competence = SelectField(
|
||||
"Choisir parmi les référentiels déjà chargés :"
|
||||
)
|
||||
|
@ -21,6 +23,7 @@ class FormationRefCompForm(FlaskForm):
|
|||
|
||||
|
||||
class RefCompLoadForm(FlaskForm):
|
||||
"Upload d'un référentiel"
|
||||
referentiel_standard = SelectField(
|
||||
"Choisir un référentiel de compétences officiel BUT"
|
||||
)
|
||||
|
@ -47,3 +50,12 @@ class RefCompLoadForm(FlaskForm):
|
|||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class FormationChangeRefCompForm(FlaskForm):
|
||||
"choix d'un nouveau ref. comp. pour une formation"
|
||||
object_select = SelectField(
|
||||
"Choisir le nouveau référentiel", validators=[DataRequired()]
|
||||
)
|
||||
submit = SubmitField("Changer le référentiel de la formation")
|
||||
cancel = SubmitField("Annuler")
|
||||
|
|
|
@ -55,11 +55,21 @@ def pvjury_page_but(formsemestre_id: int, fmt="html"):
|
|||
else:
|
||||
line_sep = "\n"
|
||||
rows, titles = pvjury_table_but(formsemestre, line_sep=line_sep)
|
||||
|
||||
if fmt.startswith("xls"):
|
||||
titles.update(
|
||||
{
|
||||
"etudid": "etudid",
|
||||
"code_nip": "nip",
|
||||
"code_ine": "ine",
|
||||
"ects_but": "Total ECTS BUT",
|
||||
"civilite": "Civ.",
|
||||
"nom": "Nom",
|
||||
"prenom": "Prénom",
|
||||
}
|
||||
)
|
||||
# Style excel... passages à la ligne sur \n
|
||||
xls_style_base = sco_excel.excel_make_style()
|
||||
xls_style_base["alignment"] = Alignment(wrapText=True, vertical="top")
|
||||
|
||||
tab = GenTable(
|
||||
base_url=f"{request.base_url}?formsemestre_id={formsemestre_id}",
|
||||
caption=title,
|
||||
|
@ -116,7 +126,7 @@ def pvjury_table_but(
|
|||
"pas de référentiel de compétences associé à la formation de ce semestre !"
|
||||
)
|
||||
titles = {
|
||||
"nom": "Code" if anonymous else "Nom",
|
||||
"nom_pv": "Code" if anonymous else "Nom",
|
||||
"cursus": "Cursus",
|
||||
"ects": "ECTS",
|
||||
"ues": "UE validées",
|
||||
|
@ -144,33 +154,47 @@ def pvjury_table_but(
|
|||
except ScoValueError:
|
||||
deca = None
|
||||
|
||||
ects_but_valides = but_ects_valides(etud, referentiel_competence_id)
|
||||
row = {
|
||||
"nom": etud.code_ine or etud.code_nip or etud.id
|
||||
if anonymous # Mode anonyme: affiche INE ou sinon NIP, ou id
|
||||
else etud.etat_civil_pv(
|
||||
line_sep=line_sep, with_paragraph=with_paragraph_nom
|
||||
"nom_pv": (
|
||||
etud.code_ine or etud.code_nip or etud.id
|
||||
if anonymous # Mode anonyme: affiche INE ou sinon NIP, ou id
|
||||
else etud.etat_civil_pv(
|
||||
line_sep=line_sep, with_paragraph=with_paragraph_nom
|
||||
)
|
||||
),
|
||||
"_nom_order": etud.sort_key,
|
||||
"_nom_target_attrs": f'class="etudinfo" id="{etud.id}"',
|
||||
"_nom_td_attrs": f'id="{etud.id}" class="etudinfo"',
|
||||
"_nom_target": url_for(
|
||||
"_nom_pv_order": etud.sort_key,
|
||||
"_nom_pv_target_attrs": f'class="etudinfo" id="{etud.id}"',
|
||||
"_nom_pv_td_attrs": f'id="{etud.id}" class="etudinfo"',
|
||||
"_nom_pv_target": url_for(
|
||||
"scolar.fiche_etud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
etudid=etud.id,
|
||||
),
|
||||
"cursus": _descr_cursus_but(etud),
|
||||
"ects": f"""{deca.ects_annee():g}<br><br>Tot. {but_ects_valides(etud, referentiel_competence_id ):g}""",
|
||||
"ects": f"""{deca.ects_annee():g}<br><br>Tot. {ects_but_valides:g}""",
|
||||
"_ects_xls": deca.ects_annee(),
|
||||
"ects_but": ects_but_valides,
|
||||
"ues": deca.descr_ues_validation(line_sep=line_sep) if deca else "-",
|
||||
"niveaux": deca.descr_niveaux_validation(line_sep=line_sep)
|
||||
if deca
|
||||
else "-",
|
||||
"niveaux": (
|
||||
deca.descr_niveaux_validation(line_sep=line_sep) if deca else "-"
|
||||
),
|
||||
"decision_but": deca.code_valide if deca else "",
|
||||
"devenir": ", ".join([f"S{i}" for i in deca.get_autorisations_passage()])
|
||||
if deca
|
||||
else "",
|
||||
"devenir": (
|
||||
", ".join([f"S{i}" for i in deca.get_autorisations_passage()])
|
||||
if deca
|
||||
else ""
|
||||
),
|
||||
# pour exports excel seulement:
|
||||
"civilite": etud.civilite_etat_civil_str,
|
||||
"nom": etud.nom,
|
||||
"prenom": etud.prenom_etat_civil or etud.prenom or "",
|
||||
"etudid": etud.id,
|
||||
"code_nip": etud.code_nip,
|
||||
"code_ine": etud.code_ine,
|
||||
}
|
||||
if deca.valide_diplome() or not only_diplome:
|
||||
rows.append(row)
|
||||
|
||||
rows.sort(key=lambda x: x["_nom_order"])
|
||||
rows.sort(key=lambda x: x["_nom_pv_order"])
|
||||
return rows, titles
|
||||
|
|
|
@ -21,8 +21,6 @@ from app.but.jury_but import (
|
|||
DecisionsProposeesRCUE,
|
||||
DecisionsProposeesUE,
|
||||
)
|
||||
from app.comp import res_sem
|
||||
from app.comp.res_but import ResultatsSemestreBUT
|
||||
from app.models import (
|
||||
ApcNiveau,
|
||||
FormSemestre,
|
||||
|
@ -33,11 +31,8 @@ from app.models import (
|
|||
ScolarFormSemestreValidation,
|
||||
ScolarNews,
|
||||
)
|
||||
from app.models.config import ScoDocSiteConfig
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import codes_cursus as sco_codes
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
from app.scodoc import sco_preferences
|
||||
from app.scodoc import sco_utils as scu
|
||||
|
||||
|
||||
|
@ -217,7 +212,7 @@ def _gen_but_niveau_ue(
|
|||
scoplement = f"""<div class="scoplement">
|
||||
<div>
|
||||
<b>UE {ue.acronyme} capitalisée </b>
|
||||
<span>le {dec_ue.ue_status["event_date"].strftime("%d/%m/%Y")}
|
||||
<span>le {dec_ue.ue_status["event_date"].strftime(scu.DATE_FMT)}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
|
@ -233,7 +228,7 @@ def _gen_but_niveau_ue(
|
|||
<div>
|
||||
<b>UE {ue.acronyme} antérieure </b>
|
||||
<span>validée {dec_ue.validation.code}
|
||||
le {dec_ue.validation.event_date.strftime("%d/%m/%Y")}
|
||||
le {dec_ue.validation.event_date.strftime(scu.DATE_FMT)}
|
||||
</span>
|
||||
</div>
|
||||
<div>Non reprise dans l'année en cours</div>
|
||||
|
@ -251,9 +246,7 @@ def _gen_but_niveau_ue(
|
|||
moy_ue_str = f"""<span>{scu.fmt_note(dec_ue.moy_ue)}</span>"""
|
||||
if dec_ue.code_valide:
|
||||
date_str = (
|
||||
f"""enregistré le {dec_ue.validation.event_date.strftime("%d/%m/%Y")}
|
||||
à {dec_ue.validation.event_date.strftime("%Hh%M")}
|
||||
"""
|
||||
f"""enregistré le {dec_ue.validation.event_date.strftime(scu.DATEATIME_FMT)}"""
|
||||
if dec_ue.validation and dec_ue.validation.event_date
|
||||
else ""
|
||||
)
|
||||
|
|
|
@ -23,6 +23,7 @@ from app.models import (
|
|||
)
|
||||
from app.scodoc import sco_cache
|
||||
from app.scodoc import codes_cursus
|
||||
from app.scodoc import sco_utils as scu
|
||||
|
||||
|
||||
class ValidationsSemestre(ResultatsCache):
|
||||
|
@ -84,7 +85,7 @@ class ValidationsSemestre(ResultatsCache):
|
|||
"code": decision.code,
|
||||
"assidu": decision.assidu,
|
||||
"compense_formsemestre_id": decision.compense_formsemestre_id,
|
||||
"event_date": decision.event_date.strftime("%d/%m/%Y"),
|
||||
"event_date": decision.event_date.strftime(scu.DATE_FMT),
|
||||
}
|
||||
self.decisions_jury = decisions_jury
|
||||
|
||||
|
@ -107,7 +108,7 @@ class ValidationsSemestre(ResultatsCache):
|
|||
decisions_jury_ues[decision.etudid][decision.ue.id] = {
|
||||
"code": decision.code,
|
||||
"ects": ects, # 0. si UE non validée
|
||||
"event_date": decision.event_date.strftime("%d/%m/%Y"),
|
||||
"event_date": decision.event_date.strftime(scu.DATE_FMT),
|
||||
}
|
||||
|
||||
self.decisions_jury_ues = decisions_jury_ues
|
||||
|
|
|
@ -21,6 +21,7 @@ from app.scodoc import sco_abs_notification
|
|||
from app.scodoc.sco_archives_justificatifs import JustificatifArchiver
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc.sco_utils import (
|
||||
EtatAssiduite,
|
||||
EtatJustificatif,
|
||||
|
@ -113,9 +114,9 @@ class Assiduite(ScoDocModel):
|
|||
"entry_date": self.entry_date,
|
||||
"user_id": None if user is None else user.id, # l'uid
|
||||
"user_name": None if user is None else user.user_name, # le login
|
||||
"user_nom_complet": None
|
||||
if user is None
|
||||
else user.get_nomcomplet(), # "Marie Dupont"
|
||||
"user_nom_complet": (
|
||||
None if user is None else user.get_nomcomplet()
|
||||
), # "Marie Dupont"
|
||||
"est_just": self.est_just,
|
||||
"external_data": self.external_data,
|
||||
}
|
||||
|
@ -364,7 +365,7 @@ class Assiduite(ScoDocModel):
|
|||
retourne le texte "saisie le <date> par <User>"
|
||||
"""
|
||||
|
||||
date: str = self.entry_date.strftime("%d/%m/%Y à %H:%M")
|
||||
date: str = self.entry_date.strftime(scu.DATEATIME_FMT)
|
||||
utilisateur: str = ""
|
||||
if self.user is not None:
|
||||
self.user: User
|
||||
|
|
|
@ -8,16 +8,19 @@
|
|||
from datetime import datetime
|
||||
import functools
|
||||
from operator import attrgetter
|
||||
import yaml
|
||||
|
||||
from flask import g
|
||||
from flask_sqlalchemy.query import Query
|
||||
from sqlalchemy.orm import class_mapper
|
||||
import sqlalchemy
|
||||
|
||||
from app import db
|
||||
from app import db, log
|
||||
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
from app.scodoc.sco_exceptions import ScoNoReferentielCompetences
|
||||
from app.scodoc.sco_exceptions import ScoNoReferentielCompetences, ScoValueError
|
||||
|
||||
REFCOMP_EQUIVALENCE_FILENAME = "ressources/referentiels/equivalences.yaml"
|
||||
|
||||
|
||||
# from https://stackoverflow.com/questions/2537471/method-of-iterating-over-sqlalchemy-models-defined-columns
|
||||
|
@ -104,6 +107,11 @@ class ApcReferentielCompetences(db.Model, XMLModel):
|
|||
def __repr__(self):
|
||||
return f"<ApcReferentielCompetences {self.id} {self.specialite!r} {self.departement!r}>"
|
||||
|
||||
def get_title(self) -> str:
|
||||
"Titre affichable"
|
||||
# utilise type_titre (B.U.T.), spécialité, version
|
||||
return f"{self.type_titre} {self.specialite} {self.get_version()}"
|
||||
|
||||
def get_version(self) -> str:
|
||||
"La version, normalement sous forme de date iso yyy-mm-dd"
|
||||
if not self.version_orebut:
|
||||
|
@ -124,9 +132,11 @@ class ApcReferentielCompetences(db.Model, XMLModel):
|
|||
"type_departement": self.type_departement,
|
||||
"type_titre": self.type_titre,
|
||||
"version_orebut": self.version_orebut,
|
||||
"scodoc_date_loaded": self.scodoc_date_loaded.isoformat() + "Z"
|
||||
if self.scodoc_date_loaded
|
||||
else "",
|
||||
"scodoc_date_loaded": (
|
||||
self.scodoc_date_loaded.isoformat() + "Z"
|
||||
if self.scodoc_date_loaded
|
||||
else ""
|
||||
),
|
||||
"scodoc_orig_filename": self.scodoc_orig_filename,
|
||||
"competences": {
|
||||
x.titre: x.to_dict(with_app_critiques=with_app_critiques)
|
||||
|
@ -234,6 +244,92 @@ class ApcReferentielCompetences(db.Model, XMLModel):
|
|||
|
||||
return parcours_info
|
||||
|
||||
def equivalents(self) -> set["ApcReferentielCompetences"]:
|
||||
"""Ensemble des référentiels du même département
|
||||
qui peuvent être considérés comme "équivalents", au sens
|
||||
une formation de ce référentiel pourrait changer vers un équivalent,
|
||||
en ignorant les apprentissages critiques.
|
||||
Pour cela, il faut avoir le même type, etc et les mêmes compétences,
|
||||
niveaux et parcours (voir map_to_other_referentiel).
|
||||
"""
|
||||
candidats = ApcReferentielCompetences.query.filter_by(
|
||||
dept_id=self.dept_id
|
||||
).filter(ApcReferentielCompetences.id != self.id)
|
||||
return {
|
||||
referentiel
|
||||
for referentiel in candidats
|
||||
if not isinstance(self.map_to_other_referentiel(referentiel), str)
|
||||
}
|
||||
|
||||
def map_to_other_referentiel(
|
||||
self, other: "ApcReferentielCompetences"
|
||||
) -> str | tuple[dict[int, int], dict[int, int], dict[int, int]]:
|
||||
"""Build mapping between this referentiel and ref2.
|
||||
If successful, returns 3 dicts mapping self ids to other ids.
|
||||
Else return a string, error message.
|
||||
"""
|
||||
if self.type_structure != other.type_structure:
|
||||
return "type_structure mismatch"
|
||||
if self.type_departement != other.type_departement:
|
||||
return "type_departement mismatch"
|
||||
# Table d'équivalences entre refs:
|
||||
equiv = self._load_config_equivalences()
|
||||
# mêmes parcours ?
|
||||
eq_parcours = equiv.get("parcours", {})
|
||||
parcours_by_code_1 = {eq_parcours.get(p.code, p.code): p for p in self.parcours}
|
||||
parcours_by_code_2 = {
|
||||
eq_parcours.get(p.code, p.code): p for p in other.parcours
|
||||
}
|
||||
if parcours_by_code_1.keys() != parcours_by_code_2.keys():
|
||||
return "parcours mismatch"
|
||||
parcours_map = {
|
||||
parcours_by_code_1[eq_parcours.get(code, code)]
|
||||
.id: parcours_by_code_2[eq_parcours.get(code, code)]
|
||||
.id
|
||||
for code in parcours_by_code_1
|
||||
}
|
||||
# mêmes compétences ?
|
||||
competence_by_code_1 = {c.titre: c for c in self.competences}
|
||||
competence_by_code_2 = {c.titre: c for c in other.competences}
|
||||
if competence_by_code_1.keys() != competence_by_code_2.keys():
|
||||
return "competences mismatch"
|
||||
competences_map = {
|
||||
competence_by_code_1[titre].id: competence_by_code_2[titre].id
|
||||
for titre in competence_by_code_1
|
||||
}
|
||||
# mêmes niveaux (dans chaque compétence) ?
|
||||
niveaux_map = {}
|
||||
for titre in competence_by_code_1:
|
||||
c1 = competence_by_code_1[titre]
|
||||
c2 = competence_by_code_2[titre]
|
||||
niveau_by_attr_1 = {(n.annee, n.ordre, n.libelle): n for n in c1.niveaux}
|
||||
niveau_by_attr_2 = {(n.annee, n.ordre, n.libelle): n for n in c2.niveaux}
|
||||
if niveau_by_attr_1.keys() != niveau_by_attr_2.keys():
|
||||
return f"niveaux mismatch in comp. '{titre}'"
|
||||
niveaux_map.update(
|
||||
{
|
||||
niveau_by_attr_1[a].id: niveau_by_attr_2[a].id
|
||||
for a in niveau_by_attr_1
|
||||
}
|
||||
)
|
||||
return parcours_map, competences_map, niveaux_map
|
||||
|
||||
def _load_config_equivalences(self) -> dict:
|
||||
"""Load config file ressources/referentiels/equivalences.yaml
|
||||
used to define equivalences between distinct referentiels
|
||||
"""
|
||||
try:
|
||||
with open(REFCOMP_EQUIVALENCE_FILENAME, encoding="utf-8") as f:
|
||||
doc = yaml.safe_load(f.read())
|
||||
except FileNotFoundError:
|
||||
log(f"_load_config_equivalences: {REFCOMP_EQUIVALENCE_FILENAME} not found")
|
||||
return {}
|
||||
except yaml.parser.ParserError as exc:
|
||||
raise ScoValueError(
|
||||
f"erreur dans le fichier {REFCOMP_EQUIVALENCE_FILENAME}"
|
||||
) from exc
|
||||
return doc.get(self.specialite, {})
|
||||
|
||||
|
||||
class ApcCompetence(db.Model, XMLModel):
|
||||
"Compétence"
|
||||
|
@ -374,9 +470,11 @@ class ApcNiveau(db.Model, XMLModel):
|
|||
"libelle": self.libelle,
|
||||
"annee": self.annee,
|
||||
"ordre": self.ordre,
|
||||
"app_critiques": {x.code: x.to_dict() for x in self.app_critiques}
|
||||
if with_app_critiques
|
||||
else {},
|
||||
"app_critiques": (
|
||||
{x.code: x.to_dict() for x in self.app_critiques}
|
||||
if with_app_critiques
|
||||
else {}
|
||||
),
|
||||
}
|
||||
|
||||
def to_dict_bul(self):
|
||||
|
@ -464,9 +562,9 @@ class ApcNiveau(db.Model, XMLModel):
|
|||
return []
|
||||
|
||||
if competence is None:
|
||||
parcour_niveaux: list[
|
||||
ApcParcoursNiveauCompetence
|
||||
] = annee_parcour.niveaux_competences
|
||||
parcour_niveaux: list[ApcParcoursNiveauCompetence] = (
|
||||
annee_parcour.niveaux_competences
|
||||
)
|
||||
niveaux: list[ApcNiveau] = [
|
||||
pn.competence.niveaux.filter_by(ordre=pn.niveau).first()
|
||||
for pn in parcour_niveaux
|
||||
|
|
|
@ -10,6 +10,7 @@ from app.models.etudiants import Identite
|
|||
from app.models.formsemestre import FormSemestre
|
||||
from app.models.ues import UniteEns
|
||||
from app.scodoc import sco_preferences
|
||||
from app.scodoc import sco_utils as scu
|
||||
|
||||
|
||||
class ApcValidationRCUE(db.Model):
|
||||
|
@ -63,14 +64,13 @@ class ApcValidationRCUE(db.Model):
|
|||
|
||||
def __str__(self):
|
||||
return f"""Décision sur RCUE {self.ue1.acronyme}/{self.ue2.acronyme}: {
|
||||
self.code} enregistrée le {self.date.strftime("%d/%m/%Y")}"""
|
||||
self.code} enregistrée le {self.date.strftime(scu.DATE_FMT)}"""
|
||||
|
||||
def html(self) -> str:
|
||||
"description en HTML"
|
||||
return f"""Décision sur RCUE {self.ue1.acronyme}/{self.ue2.acronyme}:
|
||||
<b>{self.code}</b>
|
||||
<em>enregistrée le {self.date.strftime("%d/%m/%Y")}
|
||||
à {self.date.strftime("%Hh%M")}</em>"""
|
||||
<em>enregistrée le {self.date.strftime(scu.DATEATIME_FMT)}</em>"""
|
||||
|
||||
def annee(self) -> str:
|
||||
"""l'année BUT concernée: "BUT1", "BUT2" ou "BUT3" """
|
||||
|
@ -164,7 +164,7 @@ class ApcValidationAnnee(db.Model):
|
|||
def html(self) -> str:
|
||||
"Affichage html"
|
||||
date_str = (
|
||||
f"""le {self.date.strftime("%d/%m/%Y")} à {self.date.strftime("%Hh%M")}"""
|
||||
f"""le {self.date.strftime(scu.DATEATIME_FMT)}"""
|
||||
if self.date
|
||||
else "(sans date)"
|
||||
)
|
||||
|
|
|
@ -297,7 +297,7 @@ class Identite(models.ScoDocModel):
|
|||
else:
|
||||
return self.nom
|
||||
|
||||
@cached_property
|
||||
@property
|
||||
def nomprenom(self, reverse=False) -> str:
|
||||
"""Civilité/nom/prenom pour affichages: "M. Pierre Dupont"
|
||||
Si reverse, "Dupont Pierre", sans civilité.
|
||||
|
@ -481,7 +481,9 @@ class Identite(models.ScoDocModel):
|
|||
"code_ine": self.code_ine or "",
|
||||
"code_nip": self.code_nip or "",
|
||||
"date_naissance": (
|
||||
self.date_naissance.strftime("%d/%m/%Y") if self.date_naissance else ""
|
||||
self.date_naissance.strftime(scu.DATE_FMT)
|
||||
if self.date_naissance
|
||||
else ""
|
||||
),
|
||||
"dept_acronym": self.departement.acronym,
|
||||
"dept_id": self.dept_id,
|
||||
|
@ -733,7 +735,7 @@ class Identite(models.ScoDocModel):
|
|||
"""
|
||||
if with_paragraph:
|
||||
return f"""{self.etat_civil}{line_sep}n°{self.code_nip or ""}{line_sep}né{self.e} le {
|
||||
self.date_naissance.strftime("%d/%m/%Y") if self.date_naissance else ""}{
|
||||
self.date_naissance.strftime(scu.DATE_FMT) if self.date_naissance else ""}{
|
||||
line_sep}à {self.lieu_naissance or ""}"""
|
||||
return self.etat_civil
|
||||
|
||||
|
|
|
@ -207,7 +207,9 @@ class Evaluation(models.ScoDocModel):
|
|||
e_dict["poids"] = self.get_ue_poids_dict() # { ue_id : poids }
|
||||
|
||||
# Deprecated
|
||||
e_dict["jour"] = self.date_debut.strftime("%d/%m/%Y") if self.date_debut else ""
|
||||
e_dict["jour"] = (
|
||||
self.date_debut.strftime(scu.DATE_FMT) if self.date_debut else ""
|
||||
)
|
||||
|
||||
return evaluation_enrich_dict(self, e_dict)
|
||||
|
||||
|
@ -315,10 +317,10 @@ class Evaluation(models.ScoDocModel):
|
|||
def descr_heure(self) -> str:
|
||||
"Description de la plage horaire pour affichages ('de 13h00 à 14h00')"
|
||||
if self.date_debut and (not self.date_fin or self.date_fin == self.date_debut):
|
||||
return f"""à {self.date_debut.strftime("%Hh%M")}"""
|
||||
return f"""à {self.date_debut.strftime(scu.TIME_FMT)}"""
|
||||
elif self.date_debut and self.date_fin:
|
||||
return f"""de {self.date_debut.strftime("%Hh%M")
|
||||
} à {self.date_fin.strftime("%Hh%M")}"""
|
||||
return f"""de {self.date_debut.strftime(scu.TIME_FMT)
|
||||
} à {self.date_fin.strftime(scu.TIME_FMT)}"""
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
@ -345,7 +347,7 @@ class Evaluation(models.ScoDocModel):
|
|||
|
||||
def _h(dt: datetime.datetime) -> str:
|
||||
if dt.minute:
|
||||
return dt.strftime("%Hh%M")
|
||||
return dt.strftime(scu.TIME_FMT)
|
||||
return f"{dt.hour}h"
|
||||
|
||||
if self.date_fin is None:
|
||||
|
@ -539,8 +541,8 @@ class EvaluationUEPoids(db.Model):
|
|||
def evaluation_enrich_dict(e: Evaluation, e_dict: dict):
|
||||
"""add or convert some fields in an evaluation dict"""
|
||||
# For ScoDoc7 compat
|
||||
e_dict["heure_debut"] = e.date_debut.strftime("%Hh%M") if e.date_debut else ""
|
||||
e_dict["heure_fin"] = e.date_fin.strftime("%Hh%M") if e.date_fin else ""
|
||||
e_dict["heure_debut"] = e.date_debut.strftime(scu.TIME_FMT) if e.date_debut else ""
|
||||
e_dict["heure_fin"] = e.date_fin.strftime(scu.TIME_FMT) if e.date_fin else ""
|
||||
e_dict["jour_iso"] = e.date_debut.isoformat() if e.date_debut else ""
|
||||
# Calcule durée en minutes
|
||||
e_dict["descrheure"] = e.descr_heure()
|
||||
|
@ -614,7 +616,7 @@ def check_and_convert_evaluation_args(data: dict, moduleimpl: "ModuleImpl"):
|
|||
):
|
||||
raise ScoValueError(
|
||||
f"""La date de début de l'évaluation ({
|
||||
data["date_debut"].strftime("%d/%m/%Y")
|
||||
data["date_debut"].strftime(scu.DATE_FMT)
|
||||
}) n'est pas dans le semestre !""",
|
||||
dest_url="javascript:history.back();",
|
||||
)
|
||||
|
@ -629,7 +631,7 @@ def check_and_convert_evaluation_args(data: dict, moduleimpl: "ModuleImpl"):
|
|||
):
|
||||
raise ScoValueError(
|
||||
f"""La date de fin de l'évaluation ({
|
||||
data["date_fin"].strftime("%d/%m/%Y")
|
||||
data["date_fin"].strftime(scu.DATE_FMT)
|
||||
}) n'est pas dans le semestre !""",
|
||||
dest_url="javascript:history.back();",
|
||||
)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
"""ScoDoc 9 models : Formations
|
||||
"""
|
||||
|
||||
from flask import abort, g
|
||||
from flask_sqlalchemy.query import Query
|
||||
|
||||
import app
|
||||
|
@ -64,6 +66,21 @@ class Formation(db.Model):
|
|||
"titre complet pour affichage"
|
||||
return f"""Formation {self.titre} ({self.acronyme}) [version {self.version}] code {self.formation_code}"""
|
||||
|
||||
@classmethod
|
||||
def get_formation(cls, formation_id: int | str, dept_id: int = None) -> "Formation":
|
||||
"""Formation ou 404, cherche uniquement dans le département spécifié
|
||||
ou le courant (g.scodoc_dept)"""
|
||||
if not isinstance(formation_id, int):
|
||||
try:
|
||||
formation_id = int(formation_id)
|
||||
except (TypeError, ValueError):
|
||||
abort(404, "formation_id invalide")
|
||||
if g.scodoc_dept:
|
||||
dept_id = dept_id if dept_id is not None else g.scodoc_dept_id
|
||||
if dept_id is not None:
|
||||
return cls.query.filter_by(id=formation_id, dept_id=dept_id).first_or_404()
|
||||
return cls.query.filter_by(id=formation_id).first_or_404()
|
||||
|
||||
def to_dict(self, with_refcomp_attrs=False, with_departement=True):
|
||||
"""As a dict.
|
||||
Si with_refcomp_attrs, ajoute attributs permettant de retrouver le ref. de comp.
|
||||
|
|
|
@ -224,12 +224,12 @@ class FormSemestre(models.ScoDocModel):
|
|||
d["formsemestre_id"] = self.id
|
||||
d["titre_num"] = self.titre_num()
|
||||
if self.date_debut:
|
||||
d["date_debut"] = self.date_debut.strftime("%d/%m/%Y")
|
||||
d["date_debut"] = self.date_debut.strftime(scu.DATE_FMT)
|
||||
d["date_debut_iso"] = self.date_debut.isoformat()
|
||||
else:
|
||||
d["date_debut"] = d["date_debut_iso"] = ""
|
||||
if self.date_fin:
|
||||
d["date_fin"] = self.date_fin.strftime("%d/%m/%Y")
|
||||
d["date_fin"] = self.date_fin.strftime(scu.DATE_FMT)
|
||||
d["date_fin_iso"] = self.date_fin.isoformat()
|
||||
else:
|
||||
d["date_fin"] = d["date_fin_iso"] = ""
|
||||
|
@ -255,12 +255,12 @@ class FormSemestre(models.ScoDocModel):
|
|||
d["annee_scolaire"] = self.annee_scolaire()
|
||||
d["bul_hide_xml"] = self.bul_hide_xml
|
||||
if self.date_debut:
|
||||
d["date_debut"] = self.date_debut.strftime("%d/%m/%Y")
|
||||
d["date_debut"] = self.date_debut.strftime(scu.DATE_FMT)
|
||||
d["date_debut_iso"] = self.date_debut.isoformat()
|
||||
else:
|
||||
d["date_debut"] = d["date_debut_iso"] = ""
|
||||
if self.date_fin:
|
||||
d["date_fin"] = self.date_fin.strftime("%d/%m/%Y")
|
||||
d["date_fin"] = self.date_fin.strftime(scu.DATE_FMT)
|
||||
d["date_fin_iso"] = self.date_fin.isoformat()
|
||||
else:
|
||||
d["date_fin"] = d["date_fin_iso"] = ""
|
||||
|
@ -945,7 +945,7 @@ class FormSemestre(models.ScoDocModel):
|
|||
ins.etudid for ins in self.inscriptions if ins.etat == scu.INSCRIT
|
||||
}
|
||||
|
||||
@cached_property
|
||||
@property
|
||||
def etuds_inscriptions(self) -> dict:
|
||||
"""Map { etudid : inscription } (incluant DEM et DEF)"""
|
||||
return {ins.etud.id: ins for ins in self.inscriptions}
|
||||
|
|
|
@ -72,7 +72,7 @@ class ScolarFormSemestreValidation(db.Model):
|
|||
return f"""décision sur UE {self.ue.acronyme if self.ue else self.ue_id
|
||||
} ({self.ue_id}): {self.code}"""
|
||||
return f"""décision sur semestre {self.formsemestre.titre_mois()} du {
|
||||
self.event_date.strftime("%d/%m/%Y")}"""
|
||||
self.event_date.strftime(scu.DATE_FMT)}"""
|
||||
|
||||
def delete(self):
|
||||
"Efface cette validation"
|
||||
|
@ -113,14 +113,14 @@ class ScolarFormSemestreValidation(db.Model):
|
|||
if self.ue.parcours else ""}
|
||||
{("émise par " + link)}
|
||||
: <b>{self.code}</b>{moyenne}
|
||||
le {self.event_date.strftime("%d/%m/%Y")} à {self.event_date.strftime("%Hh%M")}
|
||||
"""
|
||||
le {self.event_date.strftime(scu.DATEATIME_FMT)}
|
||||
"""
|
||||
else:
|
||||
return f"""Validation du semestre S{
|
||||
self.formsemestre.semestre_id if self.formsemestre else "?"}
|
||||
{self.formsemestre.html_link_status() if self.formsemestre else ""}
|
||||
: <b>{self.code}</b>
|
||||
le {self.event_date.strftime("%d/%m/%Y")} à {self.event_date.strftime("%Hh%M")}
|
||||
le {self.event_date.strftime(scu.DATEATIME_FMT)}
|
||||
"""
|
||||
|
||||
def ects(self) -> float:
|
||||
|
@ -175,8 +175,8 @@ class ScolarAutorisationInscription(db.Model):
|
|||
)
|
||||
return f"""Autorisation de passage vers <b>S{self.semestre_id}</b> émise par
|
||||
{link}
|
||||
le {self.date.strftime("%d/%m/%Y")} à {self.date.strftime("%Hh%M")}
|
||||
"""
|
||||
le {self.date.strftime(scu.DATEATIME_FMT)}
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def autorise_etud(
|
||||
|
|
|
@ -263,16 +263,16 @@ class GenTable:
|
|||
colspan_count -= 1
|
||||
# if colspan_count > 0:
|
||||
# continue # skip cells after a span
|
||||
if pdf_mode:
|
||||
content = row.get(f"_{cid}_pdf", False) or row.get(cid, "")
|
||||
elif xls_mode:
|
||||
content = row.get(f"_{cid}_xls", False) or row.get(cid, "")
|
||||
if pdf_mode and f"_{cid}_pdf" in row:
|
||||
content = row[f"_{cid}_pdf"]
|
||||
elif xls_mode and f"_{cid}_xls" in row:
|
||||
content = row[f"_{cid}_xls"]
|
||||
else:
|
||||
content = row.get(cid, "")
|
||||
# Convert None to empty string ""
|
||||
content = "" if content is None else content
|
||||
|
||||
colspan = row.get("_%s_colspan" % cid, 0)
|
||||
colspan = row.get(f"_{cid}_colspan", 0)
|
||||
if colspan > 1:
|
||||
pdf_style_list.append(
|
||||
(
|
||||
|
@ -676,6 +676,7 @@ class GenTable:
|
|||
fmt="html",
|
||||
page_title="",
|
||||
filename=None,
|
||||
cssstyles=[],
|
||||
javascripts=[],
|
||||
with_html_headers=True,
|
||||
publish=True,
|
||||
|
@ -696,6 +697,7 @@ class GenTable:
|
|||
H.append(
|
||||
self.html_header
|
||||
or html_sco_header.sco_header(
|
||||
cssstyles=cssstyles,
|
||||
page_title=page_title,
|
||||
javascripts=javascripts,
|
||||
init_qtip=init_qtip,
|
||||
|
@ -721,7 +723,7 @@ class GenTable:
|
|||
)
|
||||
else:
|
||||
return pdf_doc
|
||||
elif fmt == "xls" or fmt == "xlsx": # dans les 2 cas retourne du xlsx
|
||||
elif fmt in ("xls", "xlsx"): # dans les 2 cas retourne du xlsx
|
||||
xls = self.excel()
|
||||
if publish:
|
||||
return scu.send_file(
|
||||
|
@ -730,8 +732,7 @@ class GenTable:
|
|||
suffix=scu.XLSX_SUFFIX,
|
||||
mime=scu.XLSX_MIMETYPE,
|
||||
)
|
||||
else:
|
||||
return xls
|
||||
return xls
|
||||
elif fmt == "text":
|
||||
return self.text()
|
||||
elif fmt == "csv":
|
||||
|
|
|
@ -179,6 +179,7 @@ def sco_header(
|
|||
|
||||
H = [
|
||||
"""<!DOCTYPE html><html lang="fr">
|
||||
<!-- ScoDoc legacy -->
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>%(page_title)s</title>
|
||||
|
|
|
@ -141,7 +141,9 @@ def sidebar(etudid: int = None):
|
|||
params = {}
|
||||
|
||||
H = [
|
||||
f"""<div class="sidebar">
|
||||
f"""
|
||||
<!-- sidebar py -->
|
||||
<div class="sidebar">
|
||||
{ sidebar_common() }
|
||||
<div class="box-chercheetud">Chercher étudiant:<br>
|
||||
<form method="get" id="form-chercheetud"
|
||||
|
@ -180,9 +182,9 @@ def sidebar(etudid: int = None):
|
|||
)
|
||||
H.append(
|
||||
f"""<span title="absences du {
|
||||
formsemestre.date_debut.strftime("%d/%m/%Y")
|
||||
formsemestre.date_debut.strftime(scu.DATE_FMT)
|
||||
} au {
|
||||
formsemestre.date_fin.strftime("%d/%m/%Y")
|
||||
formsemestre.date_fin.strftime(scu.DATE_FMT)
|
||||
}">({
|
||||
sco_preferences.get_preference("assi_metrique", None)})
|
||||
<br>{nbabsjust:1g} J., {nbabsnj:1g} N.J.</span>"""
|
||||
|
|
|
@ -12,6 +12,7 @@ import psycopg2.extras
|
|||
|
||||
from app import log
|
||||
from app.scodoc.sco_exceptions import ScoException, ScoValueError, NoteProcessError
|
||||
from app.scodoc import sco_utils as scu
|
||||
|
||||
quote_html = html.escape
|
||||
|
||||
|
@ -475,7 +476,7 @@ def DateDMYtoISO(dmy: str, null_is_empty=False) -> str | None:
|
|||
if not isinstance(dmy, str):
|
||||
raise ScoValueError(f'Date (j/m/a) invalide: "{dmy}"')
|
||||
try:
|
||||
dt = datetime.datetime.strptime(dmy, "%d/%m/%Y")
|
||||
dt = datetime.datetime.strptime(dmy, scu.DATE_FMT)
|
||||
except ValueError:
|
||||
try:
|
||||
dt = datetime.datetime.fromisoformat(dmy)
|
||||
|
|
|
@ -34,6 +34,7 @@ from app.models.absences import BilletAbsence
|
|||
from app.models.etudiants import Identite
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
from app.scodoc import sco_preferences
|
||||
from app.scodoc import sco_utils as scu
|
||||
|
||||
|
||||
def query_billets_etud(etudid: int = None, etat: bool = None) -> Query:
|
||||
|
@ -89,12 +90,12 @@ def table_billets(
|
|||
m = " matin"
|
||||
else:
|
||||
m = " après-midi"
|
||||
billet_dict["abs_begin_str"] = billet.abs_begin.strftime("%d/%m/%Y") + m
|
||||
billet_dict["abs_begin_str"] = billet.abs_begin.strftime(scu.DATE_FMT) + m
|
||||
if billet.abs_end.hour < 12:
|
||||
m = " matin"
|
||||
else:
|
||||
m = " après-midi"
|
||||
billet_dict["abs_end_str"] = billet.abs_end.strftime("%d/%m/%Y") + m
|
||||
billet_dict["abs_end_str"] = billet.abs_end.strftime(scu.DATE_FMT) + m
|
||||
if billet.etat == 0:
|
||||
if billet.justified:
|
||||
billet_dict["etat_str"] = "à traiter"
|
||||
|
|
|
@ -515,11 +515,13 @@ class ApoEtud(dict):
|
|||
# ne trouve pas de semestre impair
|
||||
self.validation_annee_but = None
|
||||
return
|
||||
self.validation_annee_but: ApcValidationAnnee = ApcValidationAnnee.query.filter_by(
|
||||
formsemestre_id=formsemestre.id,
|
||||
etudid=self.etud["etudid"],
|
||||
referentiel_competence_id=self.cur_res.formsemestre.formation.referentiel_competence_id,
|
||||
).first()
|
||||
self.validation_annee_but: ApcValidationAnnee = (
|
||||
ApcValidationAnnee.query.filter_by(
|
||||
formsemestre_id=formsemestre.id,
|
||||
etudid=self.etud["etudid"],
|
||||
referentiel_competence_id=self.cur_res.formsemestre.formation.referentiel_competence_id,
|
||||
).first()
|
||||
)
|
||||
self.is_nar = (
|
||||
self.validation_annee_but and self.validation_annee_but.code == NAR
|
||||
)
|
||||
|
@ -1003,7 +1005,7 @@ def comp_apo_sems(etape_apogee, annee_scolaire: int) -> list[dict]:
|
|||
def nar_etuds_table(apo_data, nar_etuds):
|
||||
"""Liste les NAR -> excel table"""
|
||||
code_etape = apo_data.etape_apogee
|
||||
today = datetime.datetime.today().strftime("%d/%m/%y")
|
||||
today = datetime.datetime.today().strftime(scu.DATE_FMT)
|
||||
rows = []
|
||||
nar_etuds.sort(key=lambda k: k["nom"])
|
||||
for e in nar_etuds:
|
||||
|
|
|
@ -62,7 +62,7 @@ from flask import g
|
|||
import app.scodoc.sco_utils as scu
|
||||
from config import Config
|
||||
from app import log
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
from app.scodoc.sco_exceptions import ScoException, ScoValueError
|
||||
|
||||
|
||||
class BaseArchiver:
|
||||
|
|
|
@ -98,7 +98,7 @@ def do_formsemestre_archive(
|
|||
archive_id = PV_ARCHIVER.create_obj_archive(
|
||||
sem_archive_id, description, formsemestre.dept_id
|
||||
)
|
||||
date = PV_ARCHIVER.get_archive_date(archive_id).strftime("%d/%m/%Y à %H:%M")
|
||||
date = PV_ARCHIVER.get_archive_date(archive_id).strftime(scu.DATEATIME_FMT)
|
||||
|
||||
if not group_ids:
|
||||
# tous les inscrits du semestre
|
||||
|
|
|
@ -126,7 +126,7 @@ def make_context_dict(formsemestre: FormSemestre, etud: dict) -> dict:
|
|||
|
||||
# ajoute date courante
|
||||
t = time.localtime()
|
||||
C["date_dmy"] = time.strftime("%d/%m/%Y", t)
|
||||
C["date_dmy"] = time.strftime(scu.DATE_FMT, t)
|
||||
C["date_iso"] = time.strftime("%Y-%m-%d", t)
|
||||
|
||||
return C
|
||||
|
|
|
@ -352,7 +352,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
|
|||
H.append(
|
||||
f"""<p>
|
||||
<span class="bull_appreciations_date">{
|
||||
appreciation.date.strftime("%d/%m/%y") if appreciation.date else ""
|
||||
appreciation.date.strftime(scu.DATE_FMT) if appreciation.date else ""
|
||||
}</span>
|
||||
{appreciation.comment_safe()}
|
||||
<span class="bull_appreciations_link">{mlink}</span>
|
||||
|
|
|
@ -182,7 +182,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
|
|||
H.append(
|
||||
f"""<p>
|
||||
<span class="bull_appreciations_date">{
|
||||
appreciation.date.strftime("%d/%m/%Y")
|
||||
appreciation.date.strftime(scu.DATE_FMT)
|
||||
if appreciation.date else ""}</span>
|
||||
{appreciation.comment_safe()}
|
||||
<span class="bull_appreciations_link">{mlink}</span>
|
||||
|
|
|
@ -55,7 +55,6 @@ from flask import g
|
|||
|
||||
import app
|
||||
from app import db, log
|
||||
from app.scodoc import notesdb as ndb
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc.sco_exceptions import ScoException
|
||||
|
||||
|
@ -174,17 +173,15 @@ class EvaluationCache(ScoDocCache):
|
|||
@classmethod
|
||||
def invalidate_all_sems(cls):
|
||||
"delete all evaluations in current dept from cache"
|
||||
from app.models.evaluations import Evaluation
|
||||
from app.models.formsemestre import FormSemestre
|
||||
from app.models.moduleimpls import ModuleImpl
|
||||
|
||||
evaluation_ids = [
|
||||
x[0]
|
||||
for x in ndb.SimpleQuery(
|
||||
"""SELECT e.id
|
||||
FROM notes_evaluation e, notes_moduleimpl mi, notes_formsemestre s
|
||||
WHERE s.dept_id=%(dept_id)s
|
||||
AND s.id = mi.formsemestre_id
|
||||
AND mi.id = e.moduleimpl_id;
|
||||
""",
|
||||
{"dept_id": g.scodoc_dept_id},
|
||||
)
|
||||
e.id
|
||||
for e in Evaluation.query.join(ModuleImpl)
|
||||
.join(FormSemestre)
|
||||
.filter_by(dept_id=g.scodoc_dept_id)
|
||||
]
|
||||
cls.delete_many(evaluation_ids)
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
"""
|
||||
|
||||
import calendar
|
||||
import datetime
|
||||
import html
|
||||
import time
|
||||
|
||||
|
@ -357,7 +356,7 @@ def MonthTableBody(
|
|||
if not halfday:
|
||||
for d in range(1, nbdays + 1):
|
||||
weeknum = time.strftime(
|
||||
"%U", time.strptime("%d/%d/%d" % (d, month, year), "%d/%m/%Y")
|
||||
"%U", time.strptime("%d/%d/%d" % (d, month, year), scu.DATE_FMT)
|
||||
)
|
||||
day = DAYNAMES_ABREV[(firstday + d - 1) % 7]
|
||||
if day in weekend:
|
||||
|
@ -432,7 +431,7 @@ def MonthTableBody(
|
|||
# Calendar with 2 cells / day
|
||||
for d in range(1, nbdays + 1):
|
||||
weeknum = time.strftime(
|
||||
"%U", time.strptime("%d/%d/%d" % (d, month, year), "%d/%m/%Y")
|
||||
"%U", time.strptime("%d/%d/%d" % (d, month, year), scu.DATE_FMT)
|
||||
)
|
||||
day = DAYNAMES_ABREV[(firstday + d - 1) % 7]
|
||||
if day in weekend:
|
||||
|
|
|
@ -148,7 +148,7 @@ def _convert_formsemestres_to_dicts(
|
|||
),
|
||||
"formsemestre_id": formsemestre.id,
|
||||
"groupicon": groupicon if nb_inscrits > 0 else emptygroupicon,
|
||||
"lockimg": lockicon,
|
||||
"lockimg": "" if formsemestre.etat else lockicon,
|
||||
"modalite": formsemestre.modalite,
|
||||
"mois_debut": formsemestre.mois_debut(),
|
||||
"mois_fin": formsemestre.mois_fin(),
|
||||
|
|
|
@ -448,7 +448,7 @@ def module_edit(
|
|||
(
|
||||
"titre",
|
||||
{
|
||||
"size": 30,
|
||||
"size": 64,
|
||||
"explanation": """nom du module. Exemple:
|
||||
<em>Introduction à la démarche ergonomique</em>""",
|
||||
},
|
||||
|
@ -456,8 +456,8 @@ def module_edit(
|
|||
(
|
||||
"abbrev",
|
||||
{
|
||||
"size": 20,
|
||||
"explanation": """nom abrégé (pour bulletins).
|
||||
"size": 32,
|
||||
"explanation": """(optionnel) nom abrégé pour bulletins.
|
||||
Exemple: <em>Intro. à l'ergonomie</em>""",
|
||||
},
|
||||
),
|
||||
|
|
|
@ -298,27 +298,6 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||
cursus = formation.get_cursus()
|
||||
is_apc = cursus.APC_SAE
|
||||
semestres_indices = list(range(1, cursus.NB_SEM + 1))
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title=title, javascripts=["js/edit_ue.js"]),
|
||||
"<h2>" + title,
|
||||
f" (formation {formation.acronyme}, version {formation.version})</h2>",
|
||||
"""
|
||||
<p class="help">Les UE sont des groupes de modules dans une formation donnée,
|
||||
utilisés pour la validation (on calcule des moyennes par UE et applique des
|
||||
seuils ("barres")).
|
||||
</p>
|
||||
|
||||
<p class="help">Note: sauf exception, l'UE n'a pas de coefficient associé.
|
||||
Seuls les <em>modules</em> ont des coefficients.
|
||||
</p>""",
|
||||
(
|
||||
f"""
|
||||
<h4>UE du semestre S{ue.semestre_idx}</h4>
|
||||
"""
|
||||
if is_apc and ue
|
||||
else ""
|
||||
),
|
||||
]
|
||||
|
||||
ue_types = cursus.ALLOWED_UE_TYPES
|
||||
ue_types.sort()
|
||||
|
@ -489,7 +468,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||
if ue and is_apc:
|
||||
ue_parcours_div = apc_edit_ue.form_ue_choix_parcours(ue)
|
||||
if ue and ue.modules.count() and ue.semestre_idx is not None:
|
||||
modules_div = f"""<div id="ue_list_modules">
|
||||
modules_div = f"""<div class="scobox" id="ue_list_modules">
|
||||
<div><b>{ue.modules.count()} modules sont rattachés
|
||||
à cette UE</b> du semestre S{ue.semestre_idx},
|
||||
elle ne peut donc pas être changée de semestre.</div>
|
||||
|
@ -511,18 +490,34 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||
"""
|
||||
else:
|
||||
clone_form = ""
|
||||
bonus_div = """<div id="bonus_description"></div>"""
|
||||
ue_div = """<div id="ue_list_code" class="sco_box sco_green_bg"></div>"""
|
||||
return (
|
||||
"\n".join(H)
|
||||
+ tf[1]
|
||||
+ clone_form
|
||||
+ ue_parcours_div
|
||||
+ modules_div
|
||||
+ bonus_div
|
||||
+ ue_div
|
||||
+ html_sco_header.sco_footer()
|
||||
)
|
||||
|
||||
return f"""
|
||||
{html_sco_header.sco_header(page_title=title, javascripts=["js/edit_ue.js"])}
|
||||
<h2>{title}, (formation {formation.acronyme}, version {formation.version})</h2>
|
||||
<p class="help">Les UEs sont des groupes de modules dans une formation donnée,
|
||||
utilisés pour la validation (on calcule des moyennes par UE et applique des
|
||||
seuils ("barres")).
|
||||
</p>
|
||||
|
||||
<p class="help">Note: sauf exception, l'UE n'a pas de coefficient associé.
|
||||
Seuls les <em>modules</em> ont des coefficients.
|
||||
</p>
|
||||
|
||||
<div class="scobox">
|
||||
<div class="scobox-title">
|
||||
Édition de l'UE {('du semestre S'+str(ue.semestre_idx)) if is_apc and ue else ''}
|
||||
</div>
|
||||
{tf[1]}
|
||||
</div>
|
||||
{clone_form}
|
||||
{ue_parcours_div}
|
||||
{modules_div}
|
||||
|
||||
<div id="bonus_description"></div>
|
||||
<div id="ue_list_code" class="sco_box sco_green_bg"></div>
|
||||
|
||||
{html_sco_header.sco_footer()}
|
||||
"""
|
||||
elif tf[0] == 1:
|
||||
if create:
|
||||
if not tf[2]["ue_code"]:
|
||||
|
@ -842,8 +837,7 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
|
|||
<a href="{url_for('notes.refcomp_show',
|
||||
scodoc_dept=g.scodoc_dept, refcomp_id=formation.referentiel_competence.id)}"
|
||||
class="stdlink">
|
||||
{formation.referentiel_competence.type_titre}
|
||||
{formation.referentiel_competence.specialite_long}
|
||||
{formation.referentiel_competence.get_title()}
|
||||
</a> """
|
||||
msg_refcomp = "changer"
|
||||
H.append(f"""<ul><li>{descr_refcomp}""")
|
||||
|
@ -1170,14 +1164,17 @@ def _ue_table_ues(
|
|||
if has_perm_change:
|
||||
H.append(
|
||||
f"""<a class="stdlink" href="{
|
||||
url_for("notes.ue_set_internal", scodoc_dept=g.scodoc_dept, ue_id=ue["ue_id"])
|
||||
url_for("notes.ue_set_internal",
|
||||
scodoc_dept=g.scodoc_dept, ue_id=ue["ue_id"])
|
||||
}">transformer en UE ordinaire</a> """
|
||||
)
|
||||
H.append("</span>")
|
||||
ue_editable = editable and not ue_is_locked(ue["ue_id"])
|
||||
if ue_editable:
|
||||
H.append(
|
||||
'<a class="stdlink" href="ue_edit?ue_id=%(ue_id)s">modifier</a>' % ue
|
||||
f"""<a class="stdlink" href="{
|
||||
url_for("notes.ue_edit", scodoc_dept=g.scodoc_dept, ue_id=ue["ue_id"])
|
||||
}">modifier</a>"""
|
||||
)
|
||||
else:
|
||||
H.append('<span class="locked">[verrouillé]</span>')
|
||||
|
|
|
@ -478,11 +478,11 @@ def convert_ics(
|
|||
"heure_deb": event.decoded("dtstart")
|
||||
.replace(tzinfo=timezone.utc)
|
||||
.astimezone(tz=None)
|
||||
.strftime("%H:%M"),
|
||||
.strftime(scu.TIME_FMT),
|
||||
"heure_fin": event.decoded("dtend")
|
||||
.replace(tzinfo=timezone.utc)
|
||||
.astimezone(tz=None)
|
||||
.strftime("%H:%M"),
|
||||
.strftime(scu.TIME_FMT),
|
||||
"jour": event.decoded("dtstart").date().isoformat(),
|
||||
"start": event.decoded("dtstart").isoformat(),
|
||||
"end": event.decoded("dtend").isoformat(),
|
||||
|
|
|
@ -452,7 +452,7 @@ def table_apo_csv_list(semset):
|
|||
apo_data = sco_apogee_csv.ApoData(csv_data, periode=semset["sem_id"])
|
||||
t["filename"] = apo_data.apo_csv.titles["apoC_Fichier_Exp"]
|
||||
t["nb_etuds"] = len(apo_data.etuds)
|
||||
t["date_str"] = t["date"].strftime("%d/%m/%Y à %H:%M")
|
||||
t["date_str"] = t["date"].strftime(scu.DATEATIME_FMT)
|
||||
view_link = url_for(
|
||||
"notes.view_apo_csv",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
|
|
|
@ -135,7 +135,7 @@ def evaluation_check_absences_html(
|
|||
f"""<h2 class="eval_check_absences">{
|
||||
evaluation.description or "évaluation"
|
||||
} du {
|
||||
evaluation.date_debut.strftime("%d/%m/%Y") if evaluation.date_debut else ""
|
||||
evaluation.date_debut.strftime(scu.DATE_FMT) if evaluation.date_debut else ""
|
||||
} """
|
||||
]
|
||||
if (
|
||||
|
|
|
@ -90,7 +90,7 @@ def evaluation_create_form(
|
|||
raise ValueError("missing moduleimpl_id parameter")
|
||||
numeros = [(e.numero or 0) for e in modimpl.evaluations]
|
||||
initvalues = {
|
||||
"jour": time.strftime("%d/%m/%Y", time.localtime()),
|
||||
"jour": time.strftime(scu.DATE_FMT, time.localtime()),
|
||||
"note_max": 20,
|
||||
"numero": (max(numeros) + 1) if numeros else 0,
|
||||
"publish_incomplete": is_malus,
|
||||
|
@ -144,7 +144,7 @@ def evaluation_create_form(
|
|||
if edit:
|
||||
initvalues["blocked"] = evaluation.is_blocked()
|
||||
initvalues["blocked_until"] = (
|
||||
evaluation.blocked_until.strftime("%d/%m/%Y")
|
||||
evaluation.blocked_until.strftime(scu.DATE_FMT)
|
||||
if evaluation.blocked_until
|
||||
and evaluation.blocked_until < Evaluation.BLOCKED_FOREVER
|
||||
else ""
|
||||
|
@ -231,7 +231,13 @@ def evaluation_create_form(
|
|||
{
|
||||
"input_type": "boolcheckbox",
|
||||
"title": "Prise en compte immédiate",
|
||||
"explanation": "notes utilisées même si incomplètes",
|
||||
"explanation": """notes utilisées même si incomplètes (dangereux,
|
||||
à n'utiliser que dans des cas particuliers
|
||||
<a target="_blank" rel="noopener noreferrer"
|
||||
href="https://scodoc.org/Evaluation/#pourquoi-eviter-dutiliser-prise-en-compte-immediate"
|
||||
>voir la documentation</a>
|
||||
)
|
||||
""",
|
||||
},
|
||||
),
|
||||
(
|
||||
|
@ -399,7 +405,7 @@ def evaluation_create_form(
|
|||
if args.get("blocked_until"):
|
||||
try:
|
||||
args["blocked_until"] = datetime.datetime.strptime(
|
||||
args["blocked_until"], "%d/%m/%Y"
|
||||
args["blocked_until"], scu.DATE_FMT
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise ScoValueError("Date déblocage (j/m/a) invalide") from exc
|
||||
|
|
|
@ -126,13 +126,15 @@ def evaluations_recap_table(formsemestre: FormSemestre) -> list[dict]:
|
|||
evaluation_id=evaluation_id,
|
||||
),
|
||||
"_titre_target_attrs": 'class="discretelink"',
|
||||
"date": e.date_debut.strftime("%d/%m/%Y") if e.date_debut else "",
|
||||
"date": e.date_debut.strftime(scu.DATE_FMT) if e.date_debut else "",
|
||||
"_date_order": e.date_debut.isoformat() if e.date_debut else "",
|
||||
"complete": "oui" if eval_etat.is_complete else "non",
|
||||
"_complete_target": "#",
|
||||
"_complete_target_attrs": 'class="bull_link" title="prise en compte dans les moyennes"'
|
||||
if eval_etat.is_complete
|
||||
else 'class="bull_link incomplete" title="il manque des notes"',
|
||||
"_complete_target_attrs": (
|
||||
'class="bull_link" title="prise en compte dans les moyennes"'
|
||||
if eval_etat.is_complete
|
||||
else 'class="bull_link incomplete" title="il manque des notes"'
|
||||
),
|
||||
"manquantes": len(modimpl_results.evals_etudids_sans_note[e.id]),
|
||||
"inscrits": modimpl_results.nb_inscrits_module,
|
||||
"nb_abs": sum(modimpl_results.evals_notes[e.id] == scu.NOTES_ABSENCE),
|
||||
|
|
|
@ -381,8 +381,10 @@ def formsemestre_evaluations_cal(formsemestre_id):
|
|||
if e.date_debut == e.date_fin:
|
||||
heure_debut_txt, heure_fin_txt = "?", "?"
|
||||
else:
|
||||
heure_debut_txt = e.date_debut.strftime("%Hh%M") if e.date_debut else "?"
|
||||
heure_fin_txt = e.date_fin.strftime("%Hh%M") if e.date_fin else "?"
|
||||
heure_debut_txt = (
|
||||
e.date_debut.strftime(scu.TIME_FMT) if e.date_debut else "?"
|
||||
)
|
||||
heure_fin_txt = e.date_fin.strftime(scu.TIME_FMT) if e.date_fin else "?"
|
||||
|
||||
description = f"""{
|
||||
e.moduleimpl.module.titre
|
||||
|
@ -526,7 +528,7 @@ def formsemestre_evaluations_delai_correction(formsemestre_id, fmt="html"):
|
|||
"date_first_complete": date_first_complete,
|
||||
"delai_correction": delai_correction,
|
||||
"jour": (
|
||||
e.date_debut.strftime("%d/%m/%Y") if e.date_debut else "sans date"
|
||||
e.date_debut.strftime(scu.DATE_FMT) if e.date_debut else "sans date"
|
||||
),
|
||||
"_jour_target": url_for(
|
||||
"notes.evaluation_listenotes",
|
||||
|
|
|
@ -72,7 +72,7 @@ def xldate_as_datetime(xldate, datemode=0):
|
|||
Peut lever une ValueError
|
||||
"""
|
||||
try:
|
||||
return datetime.datetime.strptime(xldate, "%d/%m/%Y")
|
||||
return datetime.datetime.strptime(xldate, scu.DATE_FMT)
|
||||
except:
|
||||
return openpyxl.utils.datetime.from_ISO8601(xldate)
|
||||
|
||||
|
|
|
@ -489,9 +489,10 @@ def formation_import_xml(doc: str, import_tags=True, use_local_refcomp=False):
|
|||
return formation.id, modules_old2new, ues_old2new
|
||||
|
||||
|
||||
def formation_list_table() -> GenTable:
|
||||
def formation_list_table(detail: bool) -> GenTable:
|
||||
"""List formation, grouped by titre and sorted by versions
|
||||
and listing associated semestres
|
||||
and listing associated semestres.
|
||||
If detail, add column with more details.
|
||||
returns a table
|
||||
"""
|
||||
formations: list[Formation] = Formation.query.filter_by(dept_id=g.scodoc_dept_id)
|
||||
|
@ -534,6 +535,15 @@ def formation_list_table() -> GenTable:
|
|||
if formation.referentiel_competence
|
||||
else ""
|
||||
),
|
||||
"_referentiel_target": (
|
||||
url_for(
|
||||
"notes.refcomp_show",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
refcomp_id=formation.referentiel_competence.id,
|
||||
)
|
||||
if formation.referentiel_competence
|
||||
else ""
|
||||
),
|
||||
}
|
||||
# Ajoute les semestres associés à chaque formation:
|
||||
row["formsemestres"] = formation.formsemestres.order_by(
|
||||
|
@ -561,10 +571,15 @@ def formation_list_table() -> GenTable:
|
|||
else []
|
||||
)
|
||||
)
|
||||
# Répartition des UEs dans les semestres
|
||||
# utilise pour voir si la formation couvre tous les semestres
|
||||
row["semestres_ues"] = ", ".join(
|
||||
"S" + str(x if (x is not None and x > 0) else "-")
|
||||
for x in sorted({(ue.semestre_idx or 0) for ue in formation.ues})
|
||||
)
|
||||
# Date surtout utilisées pour le tri:
|
||||
if row["formsemestres"]:
|
||||
row["date_fin_dernier_sem"] = (
|
||||
row["formsemestres"][-1].date_fin.isoformat(),
|
||||
)
|
||||
row["date_fin_dernier_sem"] = row["formsemestres"][-1].date_fin.isoformat()
|
||||
row["annee_dernier_sem"] = row["formsemestres"][-1].date_fin.year
|
||||
else:
|
||||
row["date_fin_dernier_sem"] = ""
|
||||
|
@ -617,6 +632,8 @@ def formation_list_table() -> GenTable:
|
|||
"commentaire",
|
||||
"sems_list_txt",
|
||||
)
|
||||
if detail:
|
||||
columns_ids += ("annee_dernier_sem", "semestres_ues")
|
||||
titles = {
|
||||
"buttons": "",
|
||||
"commentaire": "Commentaire",
|
||||
|
@ -627,6 +644,9 @@ def formation_list_table() -> GenTable:
|
|||
"formation_code": "Code",
|
||||
"sems_list_txt": "Semestres",
|
||||
"referentiel": "Réf.",
|
||||
"date_fin_dernier_sem": "Fin dernier sem.",
|
||||
"annee_dernier_sem": "Année dernier sem.",
|
||||
"semestres_ues": "Semestres avec UEs",
|
||||
}
|
||||
return GenTable(
|
||||
columns_ids=columns_ids,
|
||||
|
@ -639,7 +659,7 @@ def formation_list_table() -> GenTable:
|
|||
html_class="formation_list_table table_leftalign",
|
||||
html_with_td_classes=True,
|
||||
html_sortable=True,
|
||||
base_url=f"{request.base_url}",
|
||||
base_url=f"{request.base_url}" + ("?detail=on" if detail else ""),
|
||||
page_title=title,
|
||||
pdf_title=title,
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
|
|
|
@ -521,7 +521,7 @@ def _record_ue_validations_and_coefs(
|
|||
coef = _convert_field_to_float(coef)
|
||||
if coef == "" or coef is False:
|
||||
coef = None
|
||||
now_dmy = time.strftime("%d/%m/%Y")
|
||||
now_dmy = time.strftime(scu.DATE_FMT)
|
||||
log(
|
||||
f"_record_ue_validations_and_coefs: {formsemestre.id} etudid={etud.id} ue_id={ue.id} moy_ue={note} ue_coef={coef}"
|
||||
)
|
||||
|
|
|
@ -106,7 +106,7 @@ def do_formsemestre_inscription_create(args, method=None):
|
|||
cnx,
|
||||
args={
|
||||
"etudid": args["etudid"],
|
||||
"event_date": time.strftime("%d/%m/%Y"),
|
||||
"event_date": time.strftime(scu.DATE_FMT),
|
||||
"formsemestre_id": args["formsemestre_id"],
|
||||
"event_type": "INSCRIPTION",
|
||||
},
|
||||
|
|
|
@ -64,14 +64,11 @@ from app.scodoc import sco_archives_formsemestre
|
|||
from app.scodoc import sco_bulletins
|
||||
from app.scodoc import codes_cursus
|
||||
from app.scodoc import sco_compute_moy
|
||||
from app.scodoc import sco_edit_ue
|
||||
from app.scodoc import sco_evaluations
|
||||
from app.scodoc import sco_evaluation_db
|
||||
from app.scodoc import sco_formations
|
||||
from app.scodoc import sco_formsemestre
|
||||
from app.scodoc import sco_formsemestre_inscriptions
|
||||
from app.scodoc import sco_groups
|
||||
from app.scodoc import sco_moduleimpl
|
||||
from app.scodoc import sco_preferences
|
||||
from app.scodoc import sco_users
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
|
@ -146,8 +143,10 @@ def _build_menu_stats(formsemestre: FormSemestre):
|
|||
]
|
||||
|
||||
|
||||
def formsemestre_status_menubar(formsemestre: FormSemestre) -> str:
|
||||
def formsemestre_status_menubar(formsemestre: FormSemestre | None) -> str:
|
||||
"""HTML to render menubar"""
|
||||
if formsemestre is None:
|
||||
return ""
|
||||
formsemestre_id = formsemestre.id
|
||||
if formsemestre.etat:
|
||||
change_lock_msg = "Verrouiller"
|
||||
|
@ -692,7 +691,7 @@ def formsemestre_description_table(
|
|||
)
|
||||
e["_date_evaluation_order"] = e["jour"].isoformat()
|
||||
e["date_evaluation"] = (
|
||||
e["jour"].strftime("%d/%m/%Y") if e["jour"] else ""
|
||||
e["jour"].strftime(scu.DATE_FMT) if e["jour"] else ""
|
||||
)
|
||||
e["UE"] = row["UE"]
|
||||
e["_UE_td_attrs"] = row["_UE_td_attrs"]
|
||||
|
|
|
@ -1160,7 +1160,7 @@ def formsemestre_validate_previous_ue(formsemestre: FormSemestre, etud: Identite
|
|||
"input_type": "date",
|
||||
"size": 9,
|
||||
"explanation": "j/m/a",
|
||||
"default": time.strftime("%d/%m/%Y"),
|
||||
"default": time.strftime(scu.DATE_FMT),
|
||||
},
|
||||
),
|
||||
(
|
||||
|
|
|
@ -453,6 +453,10 @@ class DisplayedGroupsInfos:
|
|||
for i in to_remove:
|
||||
del T[i]
|
||||
|
||||
def get_etudids(self) -> set[int]:
|
||||
"Les etudids des groupes choisis"
|
||||
return {member["etudid"] for member in self.members}
|
||||
|
||||
def get_form_elem(self):
|
||||
"""html hidden input with groups"""
|
||||
H = []
|
||||
|
|
|
@ -513,7 +513,7 @@ def _build_page(
|
|||
|
||||
<div>{scu.EMO_WARNING}
|
||||
<em>Seuls les semestres dont la date de fin est proche de la date de début
|
||||
de ce semestre ({formsemestre.date_debut.strftime("%d/%m/%Y")}) sont pris en
|
||||
de ce semestre ({formsemestre.date_debut.strftime(scu.DATE_FMT)}) sont pris en
|
||||
compte.</em>
|
||||
</div>
|
||||
{etuds_select_boxes(auth_etuds_by_sem, inscrits_ailleurs)}
|
||||
|
@ -704,7 +704,7 @@ def etuds_select_boxes(
|
|||
elink += (
|
||||
'<span class="finalisationinscription">'
|
||||
+ " : inscription finalisée le "
|
||||
+ etud["datefinalisationinscription"].strftime("%d/%m/%Y")
|
||||
+ etud["datefinalisationinscription"].strftime(scu.DATE_FMT)
|
||||
+ "</span>"
|
||||
)
|
||||
|
||||
|
|
|
@ -827,7 +827,7 @@ def _add_eval_columns(
|
|||
nb_notes,
|
||||
evaluation.description,
|
||||
(
|
||||
evaluation.date_debut.strftime("%d/%m/%Y")
|
||||
evaluation.date_debut.strftime(scu.DATE_FMT)
|
||||
if evaluation.date_debut
|
||||
else ""
|
||||
),
|
||||
|
|
|
@ -34,22 +34,28 @@ from operator import itemgetter
|
|||
from flask import url_for, g, request
|
||||
|
||||
import app
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import sco_formsemestre
|
||||
from app.scodoc import sco_preferences
|
||||
from app.scodoc import sco_report
|
||||
from app.scodoc import sco_etud
|
||||
import sco_version
|
||||
from app.scodoc import (
|
||||
html_sco_header,
|
||||
sco_formsemestre,
|
||||
sco_groups_view,
|
||||
sco_preferences,
|
||||
sco_report,
|
||||
sco_etud,
|
||||
)
|
||||
from app.models import FormSemestre
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
import app.scodoc.sco_utils as scu
|
||||
import sco_version
|
||||
|
||||
|
||||
def formsemestre_table_etuds_lycees(
|
||||
formsemestre_id, group_lycees=True, only_primo=False
|
||||
formsemestre: FormSemestre, groups_infos, group_lycees=True, only_primo=False
|
||||
):
|
||||
"""Récupère liste d'etudiants avec etat et decision."""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
etuds = sco_report.tsp_etud_list(formsemestre_id, only_primo=only_primo)[0]
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre.id)
|
||||
etuds = sco_report.tsp_etud_list(
|
||||
formsemestre.id, groups_infos=groups_infos, only_primo=only_primo
|
||||
)[0]
|
||||
if only_primo:
|
||||
primostr = "primo-entrants du "
|
||||
else:
|
||||
|
@ -59,7 +65,7 @@ def formsemestre_table_etuds_lycees(
|
|||
etuds,
|
||||
group_lycees,
|
||||
title,
|
||||
sco_preferences.SemPreferences(formsemestre_id),
|
||||
sco_preferences.SemPreferences(formsemestre.id),
|
||||
)
|
||||
|
||||
|
||||
|
@ -180,13 +186,20 @@ def _table_etuds_lycees(etuds, group_lycees, title, preferences, no_links=False)
|
|||
|
||||
def formsemestre_etuds_lycees(
|
||||
formsemestre_id,
|
||||
group_ids: list[int] = None, # si indiqué, ne prend que ces groupes
|
||||
fmt="html",
|
||||
only_primo=False,
|
||||
no_grouping=False,
|
||||
):
|
||||
"""Table des lycées d'origine"""
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
||||
group_ids,
|
||||
formsemestre_id=formsemestre.id,
|
||||
select_all_when_unspecified=True,
|
||||
)
|
||||
tab, etuds_by_lycee = formsemestre_table_etuds_lycees(
|
||||
formsemestre_id, only_primo=only_primo, group_lycees=not no_grouping
|
||||
formsemestre, groups_infos, only_primo=only_primo, group_lycees=not no_grouping
|
||||
)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
|
||||
if only_primo:
|
||||
|
@ -196,13 +209,19 @@ def formsemestre_etuds_lycees(
|
|||
t = tab.make_page(fmt=fmt, with_html_headers=False)
|
||||
if fmt != "html":
|
||||
return t
|
||||
F = [sco_report.tsp_form_primo_group(only_primo, no_grouping, formsemestre_id, fmt)]
|
||||
F = [
|
||||
sco_report.tsp_form_primo_group(
|
||||
only_primo, no_grouping, formsemestre_id, fmt, groups_infos=groups_infos
|
||||
)
|
||||
]
|
||||
H = [
|
||||
html_sco_header.sco_header(
|
||||
page_title=tab.page_title,
|
||||
init_google_maps=True,
|
||||
init_qtip=True,
|
||||
javascripts=["js/etud_info.js", "js/map_lycees.js"],
|
||||
cssstyles=sco_groups_view.CSSSTYLES,
|
||||
javascripts=sco_groups_view.JAVASCRIPTS
|
||||
+ ["js/etud_info.js", "js/map_lycees.js"],
|
||||
),
|
||||
"""<h2 class="formsemestre">Lycées d'origine des étudiants</h2>""",
|
||||
"\n".join(F),
|
||||
|
|
|
@ -50,8 +50,6 @@ from app.scodoc import html_sco_header
|
|||
from app.scodoc import htmlutils
|
||||
from app.scodoc import sco_cache
|
||||
from app.scodoc import codes_cursus
|
||||
from app.scodoc import sco_edit_module
|
||||
from app.scodoc import sco_edit_ue
|
||||
from app.scodoc import sco_etud
|
||||
from app.scodoc import sco_formsemestre_inscriptions
|
||||
from app.scodoc import sco_groups
|
||||
|
@ -431,7 +429,7 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
|
|||
)
|
||||
if info["ue_status"]["event_date"]:
|
||||
H.append(
|
||||
f"""(cap. le {info["ue_status"]["event_date"].strftime("%d/%m/%Y")})"""
|
||||
f"""(cap. le {info["ue_status"]["event_date"].strftime(scu.DATE_FMT)})"""
|
||||
)
|
||||
if is_apc:
|
||||
is_inscrit_ue = (etud.id, ue.id) not in res.dispense_ues
|
||||
|
@ -584,7 +582,7 @@ def _list_but_ue_inscriptions(res: NotesTableCompat, read_only: bool = True) ->
|
|||
validation = validations_ue[-1] if validations_ue else None
|
||||
expl_validation = (
|
||||
f"""Validée ({validation.code}) le {
|
||||
validation.event_date.strftime("%d/%m/%Y")}"""
|
||||
validation.event_date.strftime(scu.DATE_FMT)}"""
|
||||
if validation
|
||||
else ""
|
||||
)
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
"""Tableau de bord module"""
|
||||
|
||||
import math
|
||||
import time
|
||||
import datetime
|
||||
|
||||
from flask import g, url_for
|
||||
|
@ -48,13 +47,10 @@ from app.scodoc.sco_permissions import Permission
|
|||
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import htmlutils
|
||||
from app.scodoc import sco_cal
|
||||
from app.scodoc import sco_compute_moy
|
||||
from app.scodoc import sco_evaluations
|
||||
from app.scodoc import sco_formsemestre_status
|
||||
from app.scodoc import sco_groups
|
||||
from app.scodoc import sco_moduleimpl
|
||||
from app.scodoc import sco_permissions_check
|
||||
from app.tables import list_etuds
|
||||
|
||||
|
||||
|
@ -262,7 +258,7 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
|||
# 2ieme ligne: Semestre, Coef
|
||||
H.append("""<tr><td class="fichetitre2">""")
|
||||
if formsemestre.semestre_id >= 0:
|
||||
H.append("""Semestre: </td><td>%s""" % formsemestre.semestre_id)
|
||||
H.append(f"""Semestre: </td><td>{formsemestre.semestre_id}""")
|
||||
else:
|
||||
H.append("""</td><td>""")
|
||||
if sem_locked:
|
||||
|
@ -290,7 +286,8 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
|||
)
|
||||
if current_user.has_permission(Permission.EtudInscrit):
|
||||
H.append(
|
||||
f"""<a class="stdlink" style="margin-left:2em;" href="moduleimpl_inscriptions_edit?moduleimpl_id={modimpl.id}">modifier</a>"""
|
||||
f"""<a class="stdlink" style="margin-left:2em;"
|
||||
href="moduleimpl_inscriptions_edit?moduleimpl_id={modimpl.id}">modifier</a>"""
|
||||
)
|
||||
H.append("</td></tr>")
|
||||
# Ligne: règle de calcul
|
||||
|
@ -614,7 +611,7 @@ def _ligne_evaluation(
|
|||
|
||||
if evaluation.is_blocked():
|
||||
etat_txt = f"""évaluation bloquée {
|
||||
"jusqu'au " + evaluation.blocked_until.strftime("%d/%m/%Y")
|
||||
"jusqu'au " + evaluation.blocked_until.strftime(scu.DATE_FMT)
|
||||
if evaluation.blocked_until < Evaluation.BLOCKED_FOREVER
|
||||
else "" }
|
||||
"""
|
||||
|
@ -657,7 +654,8 @@ def _ligne_evaluation(
|
|||
<th class="moduleimpl_evaluations">Notes</th>
|
||||
<th class="moduleimpl_evaluations">Abs</th>
|
||||
<th class="moduleimpl_evaluations">N</th>
|
||||
<th class="moduleimpl_evaluations moduleimpl_evaluation_moy" colspan="2"><span>{etat_txt}</span></th>
|
||||
<th class="moduleimpl_evaluations moduleimpl_evaluation_moy"
|
||||
colspan="2"><span>{etat_txt}</span></th>
|
||||
</tr>
|
||||
<tr class="{tr_class} mievr_in">
|
||||
<td class="mievr">"""
|
||||
|
|
|
@ -730,7 +730,7 @@ def get_html_annotations_list(etud: Identite) -> list[str]:
|
|||
author = User.query.filter_by(user_name=annot.author).first()
|
||||
html_annotations_list.append(
|
||||
f"""<tr><td><span class="annodate">Le {
|
||||
annot.date.strftime("%d/%m/%Y") if annot.date else "?"}
|
||||
annot.date.strftime(scu.DATE_FMT) if annot.date else "?"}
|
||||
par {author.get_prenomnom() if author else "?"} :
|
||||
</span><span class="annoc">{annot.comment or ""}</span></td>{del_link}</tr>
|
||||
"""
|
||||
|
|
|
@ -335,7 +335,7 @@ class PlacementRunner:
|
|||
|
||||
def _production_pdf(self):
|
||||
pdf_title = "<br>".join(self.desceval)
|
||||
pdf_title += f"""\nDate : {self.evaluation.date_debut.strftime("%d/%m/%Y")
|
||||
pdf_title += f"""\nDate : {self.evaluation.date_debut.strftime(scu.DATE_FMT)
|
||||
if self.evaluation.date_debut else '-'
|
||||
} - Horaire : {self.evaluation.heure_debut()} à {self.evaluation.heure_fin()
|
||||
}"""
|
||||
|
@ -485,7 +485,7 @@ class PlacementRunner:
|
|||
worksheet.append_blank_row()
|
||||
worksheet.append_single_cell_row(desceval, self.styles["titres"])
|
||||
worksheet.append_single_cell_row(
|
||||
f"""Date : {self.evaluation.date_debut.strftime("%d/%m/%Y")
|
||||
f"""Date : {self.evaluation.date_debut.strftime(scu.DATE_FMT)
|
||||
if self.evaluation.date_debut else '-'
|
||||
} - Horaire : {self.evaluation.heure_debut()} à {self.evaluation.heure_fin()
|
||||
}""",
|
||||
|
|
|
@ -517,7 +517,7 @@ def _normalize_apo_fields(infolist):
|
|||
)
|
||||
infos["datefinalisationinscription_str"] = infos[
|
||||
"datefinalisationinscription"
|
||||
].strftime("%d/%m/%Y")
|
||||
].strftime(scu.DATE_FMT)
|
||||
else:
|
||||
infos["datefinalisationinscription"] = None
|
||||
infos["datefinalisationinscription_str"] = ""
|
||||
|
|
|
@ -327,7 +327,7 @@ def feuille_preparation_jury(formsemestre_id):
|
|||
"Préparé par %s le %s sur %s pour %s"
|
||||
% (
|
||||
sco_version.SCONAME,
|
||||
time.strftime("%d/%m/%Y"),
|
||||
time.strftime(scu.DATE_FMT),
|
||||
request.url_root,
|
||||
current_user,
|
||||
)
|
||||
|
|
|
@ -40,6 +40,7 @@ from operator import itemgetter
|
|||
from flask import url_for, g, request
|
||||
import pydot
|
||||
|
||||
from app import log
|
||||
from app.but import jury_but
|
||||
from app.comp import res_sem
|
||||
from app.comp.res_compat import NotesTableCompat
|
||||
|
@ -47,18 +48,21 @@ from app.models import FormSemestre, ScolarAutorisationInscription
|
|||
from app.models import FormationModalite
|
||||
from app.models.etudiants import Identite
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import notesdb as ndb
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import codes_cursus
|
||||
from app.scodoc import sco_etud
|
||||
from app.scodoc import sco_formsemestre
|
||||
from app.scodoc import sco_formsemestre_inscriptions
|
||||
from app.scodoc import sco_preferences
|
||||
import sco_version
|
||||
from app.scodoc import (
|
||||
codes_cursus,
|
||||
html_sco_header,
|
||||
sco_etud,
|
||||
sco_formsemestre,
|
||||
sco_formsemestre_inscriptions,
|
||||
sco_groups_view,
|
||||
sco_preferences,
|
||||
)
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
from app import log
|
||||
from app.scodoc.codes_cursus import code_semestre_validant
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
from app.scodoc import notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
import sco_version
|
||||
|
||||
MAX_ETUD_IN_DESCR = 20
|
||||
|
||||
|
@ -68,21 +72,25 @@ LEGENDES_CODES_BUT = {
|
|||
}
|
||||
|
||||
|
||||
def formsemestre_etuds_stats(sem: dict, only_primo=False):
|
||||
def formsemestre_etuds_stats(
|
||||
formsemestre: FormSemestre,
|
||||
only_primo=False,
|
||||
groups_infos: sco_groups_view.DisplayedGroupsInfos | None = None,
|
||||
):
|
||||
"""Récupère liste d'etudiants avec etat et decision."""
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"])
|
||||
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||
|
||||
T = nt.get_table_moyennes_triees()
|
||||
|
||||
etudids = groups_infos.get_etudids() if groups_infos else set()
|
||||
rows = nt.get_table_moyennes_triees()
|
||||
# Décisions de jury BUT pour les semestres pairs seulement
|
||||
jury_but_mode = (
|
||||
formsemestre.formation.is_apc() and formsemestre.semestre_id % 2 == 0
|
||||
)
|
||||
# Construit liste d'étudiants du semestre avec leur decision
|
||||
etuds = []
|
||||
for t in T:
|
||||
for t in rows:
|
||||
etudid = t[-1]
|
||||
if etudids and etudid not in etudids:
|
||||
continue
|
||||
etudiant = Identite.get_etud(etudid)
|
||||
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
||||
etud["annee_admission"] = etud["annee"] # plus explicite
|
||||
|
@ -96,7 +104,7 @@ def formsemestre_etuds_stats(sem: dict, only_primo=False):
|
|||
etud["codedecision"] = "(nd)" # pas de decision jury
|
||||
# Ajout devenir (autorisations inscriptions), utile pour stats passage
|
||||
aut_list = ScolarAutorisationInscription.query.filter_by(
|
||||
etudid=etudid, origin_formsemestre_id=sem["formsemestre_id"]
|
||||
etudid=etudid, origin_formsemestre_id=formsemestre.id
|
||||
).all()
|
||||
autorisations = [f"S{a.semestre_id}" for a in aut_list]
|
||||
autorisations.sort()
|
||||
|
@ -115,27 +123,27 @@ def formsemestre_etuds_stats(sem: dict, only_primo=False):
|
|||
bs.append(etud["specialite"])
|
||||
etud["bac-specialite"] = " ".join(bs)
|
||||
#
|
||||
if (not only_primo) or is_primo_etud(etud, sem):
|
||||
if (not only_primo) or is_primo_etud(etud, formsemestre):
|
||||
etuds.append(etud)
|
||||
return etuds
|
||||
|
||||
|
||||
def is_primo_etud(etud: dict, sem: dict):
|
||||
def is_primo_etud(etud: dict, formsemestre: FormSemestre):
|
||||
"""Determine si un (filled) etud a été inscrit avant ce semestre.
|
||||
Regarde la liste des semestres dans lesquels l'étudiant est inscrit.
|
||||
Si semestre pair, considère comme primo-entrants ceux qui étaient
|
||||
primo dans le précédent (S_{2n-1}).
|
||||
"""
|
||||
debut_cur = sem["date_debut_iso"]
|
||||
debut_cur_iso = formsemestre.date_debut.isoformat()
|
||||
# si semestre impair et sem. précédent contigu, recule date debut
|
||||
if (
|
||||
(len(etud["sems"]) > 1)
|
||||
and (sem["semestre_id"] % 2 == 0)
|
||||
and (etud["sems"][1]["semestre_id"] == (sem["semestre_id"] - 1))
|
||||
and (formsemestre.semestre_id % 2 == 0)
|
||||
and (etud["sems"][1]["semestre_id"] == (formsemestre.semestre_id - 1))
|
||||
):
|
||||
debut_cur = etud["sems"][1]["date_debut_iso"]
|
||||
debut_cur_iso = etud["sems"][1]["date_debut_iso"]
|
||||
for s in etud["sems"]: # le + recent d'abord
|
||||
if s["date_debut_iso"] < debut_cur:
|
||||
if s["date_debut_iso"] < debut_cur_iso:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
@ -274,22 +282,6 @@ def formsemestre_report(
|
|||
return tab
|
||||
|
||||
|
||||
# def formsemestre_report_bacs(formsemestre_id, fmt='html'):
|
||||
# """
|
||||
# Tableau sur résultats par type de bac
|
||||
# """
|
||||
# sem = sco_formsemestre.get_formsemestre( formsemestre_id)
|
||||
# title = 'Statistiques bacs ' + sem['titreannee']
|
||||
# etuds = formsemestre_etuds_stats(sem)
|
||||
# tab = formsemestre_report(formsemestre_id, etuds,
|
||||
# category='bac', result='codedecision',
|
||||
# category_name='Bac',
|
||||
# title=title)
|
||||
# return tab.make_page(
|
||||
# title = """<h2>Résultats de <a href="formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titreannee)s</a></h2>""" % sem,
|
||||
# fmt=fmt, page_title = title)
|
||||
|
||||
|
||||
def formsemestre_report_counts(
|
||||
formsemestre_id: int,
|
||||
fmt="html",
|
||||
|
@ -297,6 +289,7 @@ def formsemestre_report_counts(
|
|||
result: str = None,
|
||||
allkeys: bool = False,
|
||||
only_primo: bool = False,
|
||||
group_ids: list[int] = None, # si indiqué, ne prend que ces groupes
|
||||
):
|
||||
"""
|
||||
Tableau comptage avec choix des categories
|
||||
|
@ -307,8 +300,12 @@ def formsemestre_report_counts(
|
|||
si vrai, toutes les valeurs présentes dans les données
|
||||
sinon liste prédéfinie (voir ci-dessous)
|
||||
"""
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
||||
group_ids,
|
||||
formsemestre_id=formsemestre.id,
|
||||
select_all_when_unspecified=True,
|
||||
)
|
||||
# Décisions de jury BUT pour les semestres pairs seulement
|
||||
jury_but_mode = (
|
||||
formsemestre.formation.is_apc() and formsemestre.semestre_id % 2 == 0
|
||||
|
@ -319,7 +316,9 @@ def formsemestre_report_counts(
|
|||
|
||||
category_name = category.capitalize()
|
||||
title = "Comptages " + category_name
|
||||
etuds = formsemestre_etuds_stats(sem, only_primo=only_primo)
|
||||
etuds = formsemestre_etuds_stats(
|
||||
formsemestre, groups_infos=groups_infos, only_primo=only_primo
|
||||
)
|
||||
tab = formsemestre_report(
|
||||
formsemestre_id,
|
||||
etuds,
|
||||
|
@ -329,7 +328,7 @@ def formsemestre_report_counts(
|
|||
title=title,
|
||||
only_primo=only_primo,
|
||||
)
|
||||
if not etuds:
|
||||
if len(formsemestre.inscriptions) == 0:
|
||||
F = ["""<p><em>Aucun étudiant</em></p>"""]
|
||||
else:
|
||||
if allkeys:
|
||||
|
@ -357,9 +356,10 @@ def formsemestre_report_counts(
|
|||
keys += ["nb_rcue_valides", "decision_annee"]
|
||||
keys.sort(key=scu.heterogeneous_sorting_key)
|
||||
F = [
|
||||
"""<form name="f" method="get" action="%s"><p>
|
||||
Colonnes: <select name="result" onchange="document.f.submit()">"""
|
||||
% request.base_url
|
||||
f"""<form id="group_selector" name="f" method="get" action="{request.base_url}">
|
||||
Colonnes:
|
||||
<select name="result" onchange="document.f.submit()">
|
||||
"""
|
||||
]
|
||||
for k in keys:
|
||||
if k == result:
|
||||
|
@ -381,30 +381,38 @@ def formsemestre_report_counts(
|
|||
'<option value="%s" %s>%s</option>'
|
||||
% (k, selected, LEGENDES_CODES_BUT.get(k, k))
|
||||
)
|
||||
F.append("</select>")
|
||||
if only_primo:
|
||||
checked = 'checked="1"'
|
||||
else:
|
||||
checked = ""
|
||||
F.append(
|
||||
'<br><input type="checkbox" name="only_primo" onchange="document.f.submit()" %s>Restreindre aux primo-entrants</input>'
|
||||
% checked
|
||||
f"""
|
||||
</select>
|
||||
<div style="margin-top:12px;">
|
||||
<input type="checkbox" name="only_primo" onchange="document.f.submit()"
|
||||
{'checked' if only_primo else ''}>
|
||||
Restreindre aux primo-entrants</input>
|
||||
<span style="margin: 12px;">
|
||||
Restreindre au(x) groupe(s) :
|
||||
{sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True)
|
||||
if groups_infos else ''}
|
||||
</span>
|
||||
<input type="hidden" name="formsemestre_id" value="{formsemestre_id}"/>
|
||||
</div>
|
||||
</form>
|
||||
"""
|
||||
)
|
||||
F.append(
|
||||
'<input type="hidden" name="formsemestre_id" value="%s"/>' % formsemestre_id
|
||||
)
|
||||
F.append("</p></form>")
|
||||
|
||||
t = tab.make_page(
|
||||
title="""<h2 class="formsemestre">Comptes croisés</h2>""",
|
||||
tableau = tab.make_page(
|
||||
fmt=fmt,
|
||||
title="""<h2 class="formsemestre">Comptes croisés</h2>""",
|
||||
with_html_headers=False,
|
||||
)
|
||||
if fmt != "html":
|
||||
return t
|
||||
return tableau
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title=title),
|
||||
t,
|
||||
html_sco_header.sco_header(
|
||||
cssstyles=sco_groups_view.CSSSTYLES,
|
||||
javascripts=sco_groups_view.JAVASCRIPTS,
|
||||
page_title=title,
|
||||
),
|
||||
tableau,
|
||||
"\n".join(F),
|
||||
"""<p class="help">Le tableau affiche le nombre d'étudiants de ce semestre dans chacun
|
||||
des cas choisis: à l'aide des deux menus, vous pouvez choisir les catégories utilisées
|
||||
|
@ -418,7 +426,8 @@ def formsemestre_report_counts(
|
|||
|
||||
# --------------------------------------------------------------------------
|
||||
def table_suivi_cohorte(
|
||||
formsemestre_id,
|
||||
formsemestre: FormSemestre,
|
||||
groups_infos,
|
||||
percent=False,
|
||||
bac="", # selection sur type de bac
|
||||
bacspecialite="",
|
||||
|
@ -441,9 +450,8 @@ def table_suivi_cohorte(
|
|||
Determination des dates: on regroupe les semestres commençant à des dates proches
|
||||
|
||||
"""
|
||||
sem = sco_formsemestre.get_formsemestre(
|
||||
formsemestre_id
|
||||
) # sem est le semestre origine
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre.id)
|
||||
# sem est le semestre origine
|
||||
t0 = time.time()
|
||||
|
||||
def logt(op):
|
||||
|
@ -452,12 +460,12 @@ def table_suivi_cohorte(
|
|||
|
||||
logt("table_suivi_cohorte: start")
|
||||
# 1-- Liste des semestres posterieurs dans lesquels ont été les etudiants de sem
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||
etudids = nt.get_etudids()
|
||||
|
||||
etudids_inscrits = {ins.etudid for ins in formsemestre.inscriptions}
|
||||
etudids_groups = groups_infos.get_etudids()
|
||||
etudids = etudids_inscrits.intersection(etudids_groups)
|
||||
logt("A: orig etuds set")
|
||||
S = {formsemestre_id: sem} # ensemble de formsemestre_id
|
||||
S = {formsemestre.id: sem} # ensemble de formsemestre_id
|
||||
orig_set = set() # ensemble d'etudid du semestre d'origine
|
||||
bacs = set()
|
||||
bacspecialites = set()
|
||||
|
@ -479,7 +487,7 @@ def table_suivi_cohorte(
|
|||
)
|
||||
and (not civilite or (civilite == etud["civilite"]))
|
||||
and (not statut or (statut == etud["statut"]))
|
||||
and (not only_primo or is_primo_etud(etud, sem))
|
||||
and (not only_primo or is_primo_etud(etud, formsemestre))
|
||||
):
|
||||
orig_set.add(etudid)
|
||||
# semestres suivants:
|
||||
|
@ -524,17 +532,15 @@ def table_suivi_cohorte(
|
|||
s["nb_dipl"] = nb_dipl
|
||||
|
||||
# 3-- Regroupe les semestres par date de debut
|
||||
P = [] # liste de periodsem
|
||||
|
||||
class PeriodSem:
|
||||
pass
|
||||
def __init__(self, datedebut: datetime.datetime, sems: list[dict]):
|
||||
self.datedebut = datedebut
|
||||
self.sems = sems
|
||||
|
||||
# semestre de depart:
|
||||
porigin = PeriodSem()
|
||||
d, m, y = [int(x) for x in sem["date_debut"].split("/")]
|
||||
porigin.datedebut = datetime.datetime(y, m, d)
|
||||
porigin.sems = [sem]
|
||||
|
||||
porigin = PeriodSem(datetime.datetime(y, m, d), [sem])
|
||||
P = [] # liste de periodsem
|
||||
#
|
||||
tolerance = datetime.timedelta(days=45)
|
||||
for s in sems:
|
||||
|
@ -545,9 +551,7 @@ def table_suivi_cohorte(
|
|||
merged = True
|
||||
break
|
||||
if not merged:
|
||||
p = PeriodSem()
|
||||
p.datedebut = s["date_debut_dt"]
|
||||
p.sems = [s]
|
||||
p = PeriodSem(s["date_debut_dt"], [s])
|
||||
P.append(p)
|
||||
|
||||
# 4-- regroupe par indice de semestre S_i
|
||||
|
@ -602,7 +606,7 @@ def table_suivi_cohorte(
|
|||
L.append(d)
|
||||
# Compte nb de démissions et de ré-orientation par période
|
||||
logt("D: cout dems reos")
|
||||
sem["dems"], sem["reos"] = _count_dem_reo(formsemestre_id, sem["members"])
|
||||
sem["dems"], sem["reos"] = _count_dem_reo(formsemestre.id, sem["members"])
|
||||
for p in P:
|
||||
p.dems = set()
|
||||
p.reos = set()
|
||||
|
@ -666,8 +670,8 @@ def table_suivi_cohorte(
|
|||
L.append(l)
|
||||
|
||||
columns_ids = [p.datedebut for p in P]
|
||||
titles = dict([(p.datedebut, p.datedebut.strftime("%d/%m/%y")) for p in P])
|
||||
titles[porigin.datedebut] = porigin.datedebut.strftime("%d/%m/%y")
|
||||
titles = dict([(p.datedebut, p.datedebut.strftime(scu.DATE_FMT)) for p in P])
|
||||
titles[porigin.datedebut] = porigin.datedebut.strftime(scu.DATE_FMT)
|
||||
if percent:
|
||||
pp = "(en % de la population initiale) "
|
||||
titles["row_title"] = "%"
|
||||
|
@ -703,7 +707,7 @@ def table_suivi_cohorte(
|
|||
caption="Suivi cohorte " + pp + sem["titreannee"] + dbac,
|
||||
page_title="Suivi cohorte " + sem["titreannee"],
|
||||
html_class="table_cohorte",
|
||||
preferences=sco_preferences.SemPreferences(formsemestre_id),
|
||||
preferences=sco_preferences.SemPreferences(formsemestre.id),
|
||||
)
|
||||
# Explication: liste des semestres associés à chaque date
|
||||
if not P:
|
||||
|
@ -713,13 +717,10 @@ def table_suivi_cohorte(
|
|||
else:
|
||||
expl = ["<h3>Semestres associés à chaque date:</h3><ul>"]
|
||||
for p in P:
|
||||
expl.append("<li><b>%s</b>:" % p.datedebut.strftime("%d/%m/%y"))
|
||||
expl.append(f"""<li><b>{p.datedebut.strftime(scu.DATE_FMT)}</b>:""")
|
||||
ls = []
|
||||
for s in p.sems:
|
||||
ls.append(
|
||||
'<a href="formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titreannee)s</a>'
|
||||
% s
|
||||
)
|
||||
ls.append(formsemestre.html_link_status())
|
||||
expl.append(", ".join(ls) + "</li>")
|
||||
expl.append("</ul>")
|
||||
return (
|
||||
|
@ -737,6 +738,7 @@ def table_suivi_cohorte(
|
|||
def formsemestre_suivi_cohorte(
|
||||
formsemestre_id,
|
||||
fmt="html",
|
||||
group_ids: list[int] = None, # si indiqué, ne prend que ces groupes
|
||||
percent=1,
|
||||
bac="",
|
||||
bacspecialite="",
|
||||
|
@ -747,9 +749,19 @@ def formsemestre_suivi_cohorte(
|
|||
only_primo=False,
|
||||
) -> str:
|
||||
"""Affiche suivi cohortes par numero de semestre"""
|
||||
annee_bac = str(annee_bac or "")
|
||||
annee_admission = str(annee_admission or "")
|
||||
percent = int(percent)
|
||||
try:
|
||||
annee_bac = str(annee_bac or "")
|
||||
annee_admission = str(annee_admission or "")
|
||||
percent = int(percent)
|
||||
except ValueError as exc:
|
||||
raise ScoValueError("formsemestre_suivi_cohorte: argument invalide") from exc
|
||||
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
||||
group_ids,
|
||||
formsemestre_id=formsemestre.id,
|
||||
select_all_when_unspecified=True,
|
||||
)
|
||||
(
|
||||
tab,
|
||||
expl,
|
||||
|
@ -760,7 +772,8 @@ def formsemestre_suivi_cohorte(
|
|||
civilites,
|
||||
statuts,
|
||||
) = table_suivi_cohorte(
|
||||
formsemestre_id,
|
||||
formsemestre,
|
||||
groups_infos=groups_infos,
|
||||
percent=percent,
|
||||
bac=bac,
|
||||
bacspecialite=bacspecialite,
|
||||
|
@ -772,7 +785,7 @@ def formsemestre_suivi_cohorte(
|
|||
)
|
||||
tab.base_url = (
|
||||
"%s?formsemestre_id=%s&percent=%s&bac=%s&bacspecialite=%s&civilite=%s"
|
||||
% (request.base_url, formsemestre_id, percent, bac, bacspecialite, civilite)
|
||||
% (request.base_url, formsemestre.id, percent, bac, bacspecialite, civilite)
|
||||
)
|
||||
if only_primo:
|
||||
tab.base_url += "&only_primo=on"
|
||||
|
@ -783,25 +796,29 @@ def formsemestre_suivi_cohorte(
|
|||
base_url = request.base_url
|
||||
burl = "%s?formsemestre_id=%s&bac=%s&bacspecialite=%s&civilite=%s&statut=%s" % (
|
||||
base_url,
|
||||
formsemestre_id,
|
||||
formsemestre.id,
|
||||
bac,
|
||||
bacspecialite,
|
||||
civilite,
|
||||
statut,
|
||||
)
|
||||
if percent:
|
||||
pplink = '<p><a href="%s&percent=0">Afficher les résultats bruts</a></p>' % burl
|
||||
pplink = f"""<p><a class="stdlink"
|
||||
href="{burl}&percent=0">Afficher les résultats bruts</a></p>"""
|
||||
else:
|
||||
pplink = (
|
||||
'<p><a href="%s&percent=1">Afficher les résultats en pourcentages</a></p>'
|
||||
% burl
|
||||
)
|
||||
pplink = f"""<p><a class="stdlink"
|
||||
href="{burl}&percent=1">Afficher les résultats en pourcentages</a></p>"""
|
||||
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title=tab.page_title),
|
||||
html_sco_header.sco_header(
|
||||
cssstyles=sco_groups_view.CSSSTYLES,
|
||||
javascripts=sco_groups_view.JAVASCRIPTS,
|
||||
page_title=tab.page_title,
|
||||
),
|
||||
"""<h2 class="formsemestre">Suivi cohorte: devenir des étudiants de ce semestre</h2>""",
|
||||
_gen_form_selectetuds(
|
||||
formsemestre_id,
|
||||
formsemestre.id,
|
||||
groups_infos=groups_infos,
|
||||
only_primo=only_primo,
|
||||
bac=bac,
|
||||
bacspecialite=bacspecialite,
|
||||
|
@ -854,6 +871,7 @@ def _gen_form_selectetuds(
|
|||
annee_admissions=None,
|
||||
civilites=None,
|
||||
statuts=None,
|
||||
groups_infos: sco_groups_view.DisplayedGroupsInfos = None,
|
||||
):
|
||||
"""HTML form pour choix criteres selection etudiants"""
|
||||
annee_bacs = annee_bacs or []
|
||||
|
@ -877,9 +895,10 @@ def _gen_form_selectetuds(
|
|||
else:
|
||||
selected = 'selected="selected"'
|
||||
F = [
|
||||
f"""<form id="f" method="get" action="{request.base_url}">
|
||||
<p>Bac: <select name="bac" onchange="javascript: submit(this);">
|
||||
<option value="" {selected}>tous</option>
|
||||
f"""<form id="group_selector" name="f" method="get" action="{request.base_url}">
|
||||
<div>Bac:
|
||||
<select name="bac" onchange="javascript: submit(this);">
|
||||
<option value="" {selected}>tous</option>
|
||||
"""
|
||||
]
|
||||
for b in bacs:
|
||||
|
@ -894,7 +913,8 @@ def _gen_form_selectetuds(
|
|||
else:
|
||||
selected = 'selected="selected"'
|
||||
F.append(
|
||||
f""" Bac/Specialité: <select name="bacspecialite" onchange="javascript: submit(this);">
|
||||
f""" Bac/Specialité:
|
||||
<select name="bacspecialite" onchange="javascript: submit(this);">
|
||||
<option value="" {selected}>tous</option>
|
||||
"""
|
||||
)
|
||||
|
@ -938,17 +958,24 @@ def _gen_form_selectetuds(
|
|||
else:
|
||||
selected = ""
|
||||
F.append(f'<option value="{b}" {selected}>{b}</option>')
|
||||
F.append("</select>")
|
||||
|
||||
F.append(
|
||||
f"""<br>
|
||||
<input type="checkbox" name="only_primo"
|
||||
onchange="javascript: submit(this);"
|
||||
{'checked="1"' if only_primo else ""}/>Restreindre aux primo-entrants
|
||||
<input type="hidden" name="formsemestre_id" value="{formsemestre_id}"/>
|
||||
<input type="hidden" name="percent" value="{percent}"/>
|
||||
f"""
|
||||
</select>
|
||||
<div style="margin-top:12px;">
|
||||
<input type="checkbox" name="only_primo"
|
||||
onchange="javascript: submit(this);"
|
||||
{'checked="1"' if only_primo else ""}/>Restreindre aux primo-entrants
|
||||
|
||||
</p>
|
||||
<span style="margin: 12px;">
|
||||
Restreindre au(x) groupe(s) :
|
||||
{sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True)
|
||||
if groups_infos else ''}
|
||||
</span>
|
||||
<input type="hidden" name="formsemestre_id" value="{formsemestre_id}"/>
|
||||
<input type="hidden" name="percent" value="{percent}"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
"""
|
||||
)
|
||||
|
@ -1002,17 +1029,6 @@ def _count_dem_reo(formsemestre_id, etudids):
|
|||
return dems, reos
|
||||
|
||||
|
||||
"""OLDGEA:
|
||||
27s pour S1 F.I. classique Semestre 1 2006-2007
|
||||
B 2.3s
|
||||
C 5.6s
|
||||
D 5.9s
|
||||
Z 27s => cache des semestres pour nt
|
||||
|
||||
à chaud: 3s
|
||||
B: etuds sets: 2.4s => lent: N x getEtudInfo (non caché)
|
||||
"""
|
||||
|
||||
EXP_LIC = re.compile(r"licence", re.I)
|
||||
EXP_LPRO = re.compile(r"professionnelle", re.I)
|
||||
|
||||
|
@ -1125,6 +1141,7 @@ def get_code_cursus_etud(
|
|||
|
||||
def tsp_etud_list(
|
||||
formsemestre_id,
|
||||
groups_infos: sco_groups_view.DisplayedGroupsInfos | None = None,
|
||||
only_primo=False,
|
||||
bac="", # selection sur type de bac
|
||||
bacspecialite="",
|
||||
|
@ -1136,11 +1153,13 @@ def tsp_etud_list(
|
|||
"""Liste des etuds a considerer dans table suivi cursus
|
||||
ramene aussi ensembles des bacs, genres, statuts de (tous) les etudiants
|
||||
"""
|
||||
# log('tsp_etud_list(%s, bac="%s")' % (formsemestre_id,bac))
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||
etudids = nt.get_etudids()
|
||||
etudids_inscrits = {ins.etudid for ins in formsemestre.inscriptions}
|
||||
if groups_infos:
|
||||
etudids_groups = groups_infos.get_etudids()
|
||||
etudids = etudids_inscrits.intersection(etudids_groups)
|
||||
else:
|
||||
etudids = etudids_inscrits
|
||||
etuds = []
|
||||
bacs = set()
|
||||
bacspecialites = set()
|
||||
|
@ -1162,7 +1181,7 @@ def tsp_etud_list(
|
|||
)
|
||||
and (not civilite or (civilite == etud["civilite"]))
|
||||
and (not statut or (statut == etud["statut"]))
|
||||
and (not only_primo or is_primo_etud(etud, sem))
|
||||
and (not only_primo or is_primo_etud(etud, formsemestre))
|
||||
):
|
||||
etuds.append(etud)
|
||||
|
||||
|
@ -1173,7 +1192,6 @@ def tsp_etud_list(
|
|||
civilites.add(etud["civilite"])
|
||||
if etud["statut"]: # ne montre pas les statuts non renseignés
|
||||
statuts.add(etud["statut"])
|
||||
# log('tsp_etud_list: %s etuds' % len(etuds))
|
||||
return etuds, bacs, bacspecialites, annee_bacs, annee_admissions, civilites, statuts
|
||||
|
||||
|
||||
|
@ -1290,31 +1308,30 @@ def table_suivi_cursus(formsemestre_id, only_primo=False, grouped_parcours=True)
|
|||
return tab
|
||||
|
||||
|
||||
def tsp_form_primo_group(only_primo, no_grouping, formsemestre_id, fmt):
|
||||
"""Element de formulaire pour choisir si restriction aux primos entrants et groupement par lycees"""
|
||||
F = ["""<form name="f" method="get" action="%s">""" % request.base_url]
|
||||
if only_primo:
|
||||
checked = 'checked="1"'
|
||||
else:
|
||||
checked = ""
|
||||
F.append(
|
||||
'<input type="checkbox" name="only_primo" onchange="document.f.submit()" %s>Restreindre aux primo-entrants</input>'
|
||||
% checked
|
||||
)
|
||||
if no_grouping:
|
||||
checked = 'checked="1"'
|
||||
else:
|
||||
checked = ""
|
||||
F.append(
|
||||
'<input type="checkbox" name="no_grouping" onchange="document.f.submit()" %s>Lister chaque étudiant</input>'
|
||||
% checked
|
||||
)
|
||||
F.append(
|
||||
'<input type="hidden" name="formsemestre_id" value="%s"/>' % formsemestre_id
|
||||
)
|
||||
F.append('<input type="hidden" name="fmt" value="%s"/>' % fmt)
|
||||
F.append("""</form>""")
|
||||
return "\n".join(F)
|
||||
def tsp_form_primo_group(
|
||||
only_primo, no_grouping, formsemestre_id, fmt, groups_infos=None
|
||||
) -> str:
|
||||
"""Element de formulaire pour choisir si restriction aux primos entrants,
|
||||
groupement par lycees et groupes
|
||||
"""
|
||||
primo_checked = 'checked="1"' if only_primo else ""
|
||||
no_grouping_checked = 'checked="1"' if no_grouping else ""
|
||||
return f"""
|
||||
<form id="group_selector" name="f" method="get" action="{request.base_url}">
|
||||
<input type="checkbox" name="only_primo"
|
||||
onchange="document.f.submit()" {primo_checked}>Restreindre aux primo-entrants</input>
|
||||
<input type="checkbox" name="no_grouping" onchange="document.f.submit()"
|
||||
{no_grouping_checked}>Lister chaque étudiant</input>
|
||||
|
||||
<span style="margin: 12px;">
|
||||
Restreindre au(x) groupe(s) :
|
||||
{sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True)
|
||||
if groups_infos else ''}
|
||||
</span>
|
||||
<input type="hidden" name="formsemestre_id" value="{formsemestre_id}"/>
|
||||
<input type="hidden" name="fmt" value="{fmt}"/>
|
||||
</form>
|
||||
"""
|
||||
|
||||
|
||||
def formsemestre_suivi_cursus(
|
||||
|
@ -1337,7 +1354,11 @@ def formsemestre_suivi_cursus(
|
|||
t = tab.make_page(fmt=fmt, with_html_headers=False)
|
||||
if fmt != "html":
|
||||
return t
|
||||
F = [tsp_form_primo_group(only_primo, no_grouping, formsemestre_id, fmt)]
|
||||
F = [
|
||||
tsp_form_primo_group(
|
||||
only_primo, no_grouping, formsemestre_id, fmt, groups_infos=None
|
||||
)
|
||||
]
|
||||
|
||||
H = [
|
||||
html_sco_header.sco_header(
|
||||
|
@ -1356,6 +1377,7 @@ def formsemestre_suivi_cursus(
|
|||
# -------------
|
||||
def graph_cursus(
|
||||
formsemestre_id,
|
||||
groups_infos: sco_groups_view.DisplayedGroupsInfos | None = None,
|
||||
fmt="svg",
|
||||
only_primo=False,
|
||||
bac="", # selection sur type de bac
|
||||
|
@ -1376,6 +1398,7 @@ def graph_cursus(
|
|||
statuts,
|
||||
) = tsp_etud_list(
|
||||
formsemestre_id,
|
||||
groups_infos=groups_infos,
|
||||
only_primo=only_primo,
|
||||
bac=bac,
|
||||
bacspecialite=bacspecialite,
|
||||
|
@ -1512,6 +1535,9 @@ def graph_cursus(
|
|||
# semestre de depart en vert
|
||||
n = g.get_node("SEM" + str(formsemestre_id))[0]
|
||||
n.set_color("green")
|
||||
n.set_style("filled")
|
||||
n.set_fillcolor("lightgreen")
|
||||
n.set_penwidth(2.0)
|
||||
# demissions en rouge, octagonal
|
||||
for nid in dem_nodes.values():
|
||||
n = g.get_node(nid)[0]
|
||||
|
@ -1606,6 +1632,7 @@ def graph_cursus(
|
|||
|
||||
def formsemestre_graph_cursus(
|
||||
formsemestre_id,
|
||||
group_ids: list[int] = None, # si indiqué, ne prend que ces groupes
|
||||
fmt="html",
|
||||
only_primo=False,
|
||||
bac="", # selection sur type de bac
|
||||
|
@ -1619,7 +1646,12 @@ def formsemestre_graph_cursus(
|
|||
"""Graphe suivi cohortes"""
|
||||
annee_bac = str(annee_bac or "")
|
||||
annee_admission = str(annee_admission or "")
|
||||
# log("formsemestre_graph_cursus")
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
||||
group_ids,
|
||||
formsemestre_id=formsemestre.id,
|
||||
select_all_when_unspecified=True,
|
||||
)
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
if fmt == "pdf":
|
||||
(
|
||||
|
@ -1633,6 +1665,7 @@ def formsemestre_graph_cursus(
|
|||
statuts,
|
||||
) = graph_cursus(
|
||||
formsemestre_id,
|
||||
groups_infos=groups_infos,
|
||||
fmt="pdf",
|
||||
only_primo=only_primo,
|
||||
bac=bac,
|
||||
|
@ -1657,6 +1690,7 @@ def formsemestre_graph_cursus(
|
|||
statuts,
|
||||
) = graph_cursus(
|
||||
formsemestre_id,
|
||||
groups_infos=groups_infos,
|
||||
fmt="png",
|
||||
only_primo=only_primo,
|
||||
bac=bac,
|
||||
|
@ -1695,6 +1729,7 @@ def formsemestre_graph_cursus(
|
|||
statuts,
|
||||
) = graph_cursus(
|
||||
formsemestre_id,
|
||||
groups_infos=groups_infos,
|
||||
only_primo=only_primo,
|
||||
bac=bac,
|
||||
bacspecialite=bacspecialite,
|
||||
|
@ -1706,14 +1741,17 @@ def formsemestre_graph_cursus(
|
|||
|
||||
H = [
|
||||
html_sco_header.sco_header(
|
||||
cssstyles=sco_groups_view.CSSSTYLES,
|
||||
javascripts=sco_groups_view.JAVASCRIPTS,
|
||||
page_title="Graphe cursus de %(titreannee)s" % sem,
|
||||
no_side_bar=True,
|
||||
),
|
||||
"""<h2 class="formsemestre">Cursus des étudiants de ce semestre</h2>""",
|
||||
doc,
|
||||
"<p>%d étudiants sélectionnés</p>" % len(etuds),
|
||||
f"<p>{len(etuds)} étudiants sélectionnés</p>",
|
||||
_gen_form_selectetuds(
|
||||
formsemestre_id,
|
||||
groups_infos=groups_infos,
|
||||
only_primo=only_primo,
|
||||
bac=bac,
|
||||
bacspecialite=bacspecialite,
|
||||
|
@ -1743,6 +1781,10 @@ def formsemestre_graph_cursus(
|
|||
passant d'un semestre à l'autre (s'il y en a moins de {MAX_ETUD_IN_DESCR}, vous
|
||||
pouvez visualiser leurs noms en passant le curseur sur le chiffre).
|
||||
</p>
|
||||
<p class="help">
|
||||
Le menu <em>Restreindre au(x) groupe(s)</em> permet de restreindre l'étude aux
|
||||
étudiants appartenant aux groupes indiqués <em>dans le semestre d'origine</em>.
|
||||
</p>
|
||||
""",
|
||||
html_sco_header.sco_footer(),
|
||||
]
|
||||
|
|
|
@ -892,7 +892,7 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]):
|
|||
eval_name = f"{evaluation.moduleimpl.module.code}-{indication_date}"
|
||||
|
||||
date_str = (
|
||||
f"""du {evaluation.date_debut.strftime("%d/%m/%Y")}"""
|
||||
f"""du {evaluation.date_debut.strftime(scu.DATE_FMT)}"""
|
||||
if evaluation.date_debut
|
||||
else "(sans date)"
|
||||
)
|
||||
|
|
|
@ -250,7 +250,6 @@ def trombino_copy_photos(group_ids=None, dialog_confirmed=False):
|
|||
"Copy photos from portal to ScoDoc (overwriting local copy)"
|
||||
group_ids = [] if group_ids is None else group_ids
|
||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
||||
breakpoint()
|
||||
back_url = "groups_view?%s&curtab=tab-photos" % groups_infos.groups_query_args
|
||||
|
||||
portal_url = sco_portal_apogee.get_portal_url()
|
||||
|
|
|
@ -117,7 +117,7 @@ def convert_fr_date(date_str: str, allow_iso=True) -> datetime.datetime:
|
|||
ScoValueError si date invalide.
|
||||
"""
|
||||
try:
|
||||
return datetime.datetime.strptime(date_str, "%d/%m/%Y")
|
||||
return datetime.datetime.strptime(date_str, DATE_FMT)
|
||||
except ValueError:
|
||||
# Try to add century ?
|
||||
m = re.match(r"^(\d{1,2})/(\d{1,2})/(\d\d)$", date_str)
|
||||
|
@ -129,7 +129,7 @@ def convert_fr_date(date_str: str, allow_iso=True) -> datetime.datetime:
|
|||
year += 1900
|
||||
try:
|
||||
return datetime.datetime.strptime(
|
||||
f"{m.group(1)}/{m.group(2)}/{year}", "%d/%m/%Y"
|
||||
f"{m.group(1)}/{m.group(2)}/{year}", DATE_FMT
|
||||
)
|
||||
except ValueError:
|
||||
pass
|
||||
|
@ -550,6 +550,11 @@ MONTH_NAMES = (
|
|||
)
|
||||
DAY_NAMES = ("lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche")
|
||||
|
||||
TIME_FMT = "%H:%M" # affichage des heures
|
||||
DATE_FMT = "%d/%m/%Y" # affichage des dates
|
||||
DATEATIME_FMT = DATE_FMT + " à " + TIME_FMT
|
||||
DATETIME_FMT = DATE_FMT + " " + TIME_FMT
|
||||
|
||||
|
||||
def fmt_note(val, note_max=None, keep_numeric=False):
|
||||
"""conversion note en str pour affichage dans tables HTML ou PDF.
|
||||
|
@ -1067,9 +1072,9 @@ def bul_filename(formsemestre, etud, prefix="bul"):
|
|||
|
||||
def flash_errors(form):
|
||||
"""Flashes form errors (version sommaire)"""
|
||||
for field, errors in form.errors.items():
|
||||
for field, _ in form.errors.items():
|
||||
flash(
|
||||
"Erreur: voir le champ %s" % (getattr(form, field).label.text,),
|
||||
f"Erreur: voir le champ {getattr(form, field).label.text}",
|
||||
"warning",
|
||||
)
|
||||
# see https://getbootstrap.com/docs/4.0/components/alerts/
|
||||
|
|
|
@ -141,111 +141,111 @@ table.dataTable.with-highlight tr:hover td {
|
|||
background-color: rgba(255, 255, 0, 0.415);
|
||||
}
|
||||
|
||||
table.dataTable.order-column tbody tr > .sorting_1,
|
||||
table.dataTable.order-column tbody tr > .sorting_2,
|
||||
table.dataTable.order-column tbody tr > .sorting_3,
|
||||
table.dataTable.display tbody tr > .sorting_1,
|
||||
table.dataTable.display tbody tr > .sorting_2,
|
||||
table.dataTable.display tbody tr > .sorting_3 {
|
||||
table.dataTable.order-column tbody tr>.sorting_1,
|
||||
table.dataTable.order-column tbody tr>.sorting_2,
|
||||
table.dataTable.order-column tbody tr>.sorting_3,
|
||||
table.dataTable.display tbody tr>.sorting_1,
|
||||
table.dataTable.display tbody tr>.sorting_2,
|
||||
table.dataTable.display tbody tr>.sorting_3 {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
table.dataTable.order-column tbody tr.selected > .sorting_1,
|
||||
table.dataTable.order-column tbody tr.selected > .sorting_2,
|
||||
table.dataTable.order-column tbody tr.selected > .sorting_3,
|
||||
table.dataTable.display tbody tr.selected > .sorting_1,
|
||||
table.dataTable.display tbody tr.selected > .sorting_2,
|
||||
table.dataTable.display tbody tr.selected > .sorting_3 {
|
||||
table.dataTable.order-column tbody tr.selected>.sorting_1,
|
||||
table.dataTable.order-column tbody tr.selected>.sorting_2,
|
||||
table.dataTable.order-column tbody tr.selected>.sorting_3,
|
||||
table.dataTable.display tbody tr.selected>.sorting_1,
|
||||
table.dataTable.display tbody tr.selected>.sorting_2,
|
||||
table.dataTable.display tbody tr.selected>.sorting_3 {
|
||||
background-color: #acbad4;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr.odd > .sorting_1,
|
||||
table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 {
|
||||
table.dataTable.display tbody tr.odd>.sorting_1,
|
||||
table.dataTable.order-column.stripe tbody tr.odd>.sorting_1 {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr.odd > .sorting_2,
|
||||
table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 {
|
||||
table.dataTable.display tbody tr.odd>.sorting_2,
|
||||
table.dataTable.order-column.stripe tbody tr.odd>.sorting_2 {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr.odd > .sorting_3,
|
||||
table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 {
|
||||
table.dataTable.display tbody tr.odd>.sorting_3,
|
||||
table.dataTable.order-column.stripe tbody tr.odd>.sorting_3 {
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr.odd.selected > .sorting_1,
|
||||
table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 {
|
||||
table.dataTable.display tbody tr.odd.selected>.sorting_1,
|
||||
table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_1 {
|
||||
background-color: #a6b3cd;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr.odd.selected > .sorting_2,
|
||||
table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 {
|
||||
table.dataTable.display tbody tr.odd.selected>.sorting_2,
|
||||
table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_2 {
|
||||
background-color: #a7b5ce;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr.odd.selected > .sorting_3,
|
||||
table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 {
|
||||
table.dataTable.display tbody tr.odd.selected>.sorting_3,
|
||||
table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_3 {
|
||||
background-color: #a9b6d0;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr.even > .sorting_1,
|
||||
table.dataTable.order-column.stripe tbody tr.even > .sorting_1 {
|
||||
table.dataTable.display tbody tr.even>.sorting_1,
|
||||
table.dataTable.order-column.stripe tbody tr.even>.sorting_1 {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr.even > .sorting_2,
|
||||
table.dataTable.order-column.stripe tbody tr.even > .sorting_2 {
|
||||
table.dataTable.display tbody tr.even>.sorting_2,
|
||||
table.dataTable.order-column.stripe tbody tr.even>.sorting_2 {
|
||||
background-color: #fbfbfb;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr.even > .sorting_3,
|
||||
table.dataTable.order-column.stripe tbody tr.even > .sorting_3 {
|
||||
table.dataTable.display tbody tr.even>.sorting_3,
|
||||
table.dataTable.order-column.stripe tbody tr.even>.sorting_3 {
|
||||
background-color: #fdfdfd;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr.even.selected > .sorting_1,
|
||||
table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 {
|
||||
table.dataTable.display tbody tr.even.selected>.sorting_1,
|
||||
table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_1 {
|
||||
background-color: #acbad4;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr.even.selected > .sorting_2,
|
||||
table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 {
|
||||
table.dataTable.display tbody tr.even.selected>.sorting_2,
|
||||
table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_2 {
|
||||
background-color: #adbbd6;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr.even.selected > .sorting_3,
|
||||
table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 {
|
||||
table.dataTable.display tbody tr.even.selected>.sorting_3,
|
||||
table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_3 {
|
||||
background-color: #afbdd8;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr:hover > .sorting_1,
|
||||
table.dataTable.order-column.hover tbody tr:hover > .sorting_1 {
|
||||
table.dataTable.display tbody tr:hover>.sorting_1,
|
||||
table.dataTable.order-column.hover tbody tr:hover>.sorting_1 {
|
||||
background-color: #eaeaea;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr:hover > .sorting_2,
|
||||
table.dataTable.order-column.hover tbody tr:hover > .sorting_2 {
|
||||
table.dataTable.display tbody tr:hover>.sorting_2,
|
||||
table.dataTable.order-column.hover tbody tr:hover>.sorting_2 {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr:hover > .sorting_3,
|
||||
table.dataTable.order-column.hover tbody tr:hover > .sorting_3 {
|
||||
table.dataTable.display tbody tr:hover>.sorting_3,
|
||||
table.dataTable.order-column.hover tbody tr:hover>.sorting_3 {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr:hover.selected > .sorting_1,
|
||||
table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 {
|
||||
table.dataTable.display tbody tr:hover.selected>.sorting_1,
|
||||
table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1 {
|
||||
background-color: #a1aec7;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr:hover.selected > .sorting_2,
|
||||
table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 {
|
||||
table.dataTable.display tbody tr:hover.selected>.sorting_2,
|
||||
table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2 {
|
||||
background-color: #a2afc8;
|
||||
}
|
||||
|
||||
table.dataTable.display tbody tr:hover.selected > .sorting_3,
|
||||
table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 {
|
||||
table.dataTable.display tbody tr:hover.selected>.sorting_3,
|
||||
table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3 {
|
||||
background-color: #a4b2cb;
|
||||
}
|
||||
|
||||
|
@ -420,13 +420,11 @@ table.dataTable td {
|
|||
color: #333333 !important;
|
||||
border: 1px solid #979797;
|
||||
background-color: white;
|
||||
background: -webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
left bottom,
|
||||
color-stop(0%, white),
|
||||
color-stop(100%, gainsboro)
|
||||
);
|
||||
background: -webkit-gradient(linear,
|
||||
left top,
|
||||
left bottom,
|
||||
color-stop(0%, white),
|
||||
color-stop(100%, gainsboro));
|
||||
/* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, white 0%, gainsboro 100%);
|
||||
/* Chrome10+,Safari5.1+ */
|
||||
|
@ -454,13 +452,11 @@ table.dataTable td {
|
|||
color: white !important;
|
||||
border: 1px solid #111111;
|
||||
background-color: #585858;
|
||||
background: -webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
left bottom,
|
||||
color-stop(0%, #585858),
|
||||
color-stop(100%, #111111)
|
||||
);
|
||||
background: -webkit-gradient(linear,
|
||||
left top,
|
||||
left bottom,
|
||||
color-stop(0%, #585858),
|
||||
color-stop(100%, #111111));
|
||||
/* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #585858 0%, #111111 100%);
|
||||
/* Chrome10+,Safari5.1+ */
|
||||
|
@ -477,13 +473,11 @@ table.dataTable td {
|
|||
.dataTables_wrapper .dataTables_paginate .paginate_button:active {
|
||||
outline: none;
|
||||
background-color: #2b2b2b;
|
||||
background: -webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
left bottom,
|
||||
color-stop(0%, #2b2b2b),
|
||||
color-stop(100%, #0c0c0c)
|
||||
);
|
||||
background: -webkit-gradient(linear,
|
||||
left top,
|
||||
left bottom,
|
||||
color-stop(0%, #2b2b2b),
|
||||
color-stop(100%, #0c0c0c));
|
||||
/* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
|
||||
/* Chrome10+,Safari5.1+ */
|
||||
|
@ -514,50 +508,38 @@ table.dataTable td {
|
|||
text-align: center;
|
||||
font-size: 1.2em;
|
||||
background-color: white;
|
||||
background: -webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
right top,
|
||||
color-stop(0%, rgba(255, 255, 255, 0)),
|
||||
color-stop(25%, rgba(255, 255, 255, 0.9)),
|
||||
color-stop(75%, rgba(255, 255, 255, 0.9)),
|
||||
color-stop(100%, rgba(255, 255, 255, 0))
|
||||
);
|
||||
background: -webkit-linear-gradient(
|
||||
left,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.9) 25%,
|
||||
rgba(255, 255, 255, 0.9) 75%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
background: -moz-linear-gradient(
|
||||
left,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.9) 25%,
|
||||
rgba(255, 255, 255, 0.9) 75%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
background: -ms-linear-gradient(
|
||||
left,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.9) 25%,
|
||||
rgba(255, 255, 255, 0.9) 75%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
background: -o-linear-gradient(
|
||||
left,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.9) 25%,
|
||||
rgba(255, 255, 255, 0.9) 75%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.9) 25%,
|
||||
rgba(255, 255, 255, 0.9) 75%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
background: -webkit-gradient(linear,
|
||||
left top,
|
||||
right top,
|
||||
color-stop(0%, rgba(255, 255, 255, 0)),
|
||||
color-stop(25%, rgba(255, 255, 255, 0.9)),
|
||||
color-stop(75%, rgba(255, 255, 255, 0.9)),
|
||||
color-stop(100%, rgba(255, 255, 255, 0)));
|
||||
background: -webkit-linear-gradient(left,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.9) 25%,
|
||||
rgba(255, 255, 255, 0.9) 75%,
|
||||
rgba(255, 255, 255, 0) 100%);
|
||||
background: -moz-linear-gradient(left,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.9) 25%,
|
||||
rgba(255, 255, 255, 0.9) 75%,
|
||||
rgba(255, 255, 255, 0) 100%);
|
||||
background: -ms-linear-gradient(left,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.9) 25%,
|
||||
rgba(255, 255, 255, 0.9) 75%,
|
||||
rgba(255, 255, 255, 0) 100%);
|
||||
background: -o-linear-gradient(left,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.9) 25%,
|
||||
rgba(255, 255, 255, 0.9) 75%,
|
||||
rgba(255, 255, 255, 0) 100%);
|
||||
background: linear-gradient(to right,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.9) 25%,
|
||||
rgba(255, 255, 255, 0.9) 75%,
|
||||
rgba(255, 255, 255, 0) 100%);
|
||||
}
|
||||
|
||||
.dataTables_wrapper .dataTables_length,
|
||||
|
@ -577,69 +559,17 @@ table.dataTable td {
|
|||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.dataTables_wrapper
|
||||
.dataTables_scroll
|
||||
div.dataTables_scrollBody
|
||||
> table
|
||||
> thead
|
||||
> tr
|
||||
> th,
|
||||
.dataTables_wrapper
|
||||
.dataTables_scroll
|
||||
div.dataTables_scrollBody
|
||||
> table
|
||||
> thead
|
||||
> tr
|
||||
> td,
|
||||
.dataTables_wrapper
|
||||
.dataTables_scroll
|
||||
div.dataTables_scrollBody
|
||||
> table
|
||||
> tbody
|
||||
> tr
|
||||
> th,
|
||||
.dataTables_wrapper
|
||||
.dataTables_scroll
|
||||
div.dataTables_scrollBody
|
||||
> table
|
||||
> tbody
|
||||
> tr
|
||||
> td {
|
||||
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>th,
|
||||
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>td,
|
||||
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>th,
|
||||
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.dataTables_wrapper
|
||||
.dataTables_scroll
|
||||
div.dataTables_scrollBody
|
||||
> table
|
||||
> thead
|
||||
> tr
|
||||
> th
|
||||
> div.dataTables_sizing,
|
||||
.dataTables_wrapper
|
||||
.dataTables_scroll
|
||||
div.dataTables_scrollBody
|
||||
> table
|
||||
> thead
|
||||
> tr
|
||||
> td
|
||||
> div.dataTables_sizing,
|
||||
.dataTables_wrapper
|
||||
.dataTables_scroll
|
||||
div.dataTables_scrollBody
|
||||
> table
|
||||
> tbody
|
||||
> tr
|
||||
> th
|
||||
> div.dataTables_sizing,
|
||||
.dataTables_wrapper
|
||||
.dataTables_scroll
|
||||
div.dataTables_scrollBody
|
||||
> table
|
||||
> tbody
|
||||
> tr
|
||||
> td
|
||||
> div.dataTables_sizing {
|
||||
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>th>div.dataTables_sizing,
|
||||
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>td>div.dataTables_sizing,
|
||||
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>th>div.dataTables_sizing,
|
||||
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>td>div.dataTables_sizing {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
margin: 0 !important;
|
||||
|
@ -650,8 +580,8 @@ table.dataTable td {
|
|||
border-bottom: 1px solid #111111;
|
||||
}
|
||||
|
||||
.dataTables_wrapper.no-footer div.dataTables_scrollHead > table,
|
||||
.dataTables_wrapper.no-footer div.dataTables_scrollBody > table {
|
||||
.dataTables_wrapper.no-footer div.dataTables_scrollHead>table,
|
||||
.dataTables_wrapper.no-footer div.dataTables_scrollBody>table {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
|
@ -664,6 +594,7 @@ table.dataTable td {
|
|||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
|
||||
.dataTables_wrapper .dataTables_info,
|
||||
.dataTables_wrapper .dataTables_paginate {
|
||||
float: none;
|
||||
|
@ -676,6 +607,7 @@ table.dataTable td {
|
|||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
|
||||
.dataTables_wrapper .dataTables_length,
|
||||
.dataTables_wrapper .dataTables_filter {
|
||||
float: none;
|
||||
|
@ -703,6 +635,10 @@ table.table_leftalign tr td {
|
|||
text-align: left;
|
||||
}
|
||||
|
||||
p.gt_caption {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* Ligne(s) de titre */
|
||||
table.dataTable thead tr th {
|
||||
background-color: rgb(90%, 90%, 90%);
|
||||
|
@ -757,7 +693,8 @@ table.dataTable.gt_table {
|
|||
table.dataTable.gt_table.gt_left {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
table.dataTable.gt_table.gt_left td,
|
||||
table.dataTable.gt_table.gt_left th {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
|
@ -1188,10 +1188,11 @@ a.discretelink:hover {
|
|||
|
||||
.help {
|
||||
max-width: var(--sco-content-max-width);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.help {
|
||||
font-style: italic;
|
||||
.help em {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.help_important {
|
||||
|
@ -2410,10 +2411,10 @@ li.notes_formation_list {
|
|||
padding-top: 10px;
|
||||
}
|
||||
|
||||
table.formation_list_table {
|
||||
width: 100%;
|
||||
table.dataTable.formation_list_table.gt_table {
|
||||
border-collapse: collapse;
|
||||
background-color: rgb(0%, 90%, 90%);
|
||||
margin-right: 12px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
table#formation_list_table tr.gt_hl {
|
||||
|
@ -2454,8 +2455,8 @@ table.formation_list_table td.buttons span.but_placeholder {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.formation_list_table td.titre {
|
||||
width: 45%;
|
||||
.formation_list_table td.sems_list_txt {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
.formation_list_table td.commentaire {
|
||||
|
|
|
@ -23,7 +23,7 @@ function groups_view_url() {
|
|||
url.param()["formsemestre_id"] =
|
||||
$("#group_selector")[0].formsemestre_id.value;
|
||||
|
||||
var selected_groups = $("#group_selector select").val();
|
||||
var selected_groups = $("#group_selector select#group_ids_sel").val();
|
||||
url.param()["group_ids"] = selected_groups; // remplace par groupes selectionnes
|
||||
|
||||
return url;
|
||||
|
|
|
@ -13,6 +13,8 @@ from sqlalchemy import desc, literal, union, asc
|
|||
from app import db, g
|
||||
from app.auth.models import User
|
||||
from app.models import Assiduite, Identite, Justificatif, Module
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
||||
from app.scodoc.sco_utils import (
|
||||
EtatAssiduite,
|
||||
EtatJustificatif,
|
||||
|
@ -401,8 +403,8 @@ class RowAssiJusti(tb.Row):
|
|||
]
|
||||
|
||||
if multi_days and self.ligne["type"] != "justificatif":
|
||||
date_affichees[0] = self.ligne["date_debut"].strftime("%d/%m/%y")
|
||||
date_affichees[1] = self.ligne["date_fin"].strftime("%d/%m/%y")
|
||||
date_affichees[0] = self.ligne["date_debut"].strftime(scu.DATE_FMT)
|
||||
date_affichees[1] = self.ligne["date_fin"].strftime(scu.DATE_FMT)
|
||||
|
||||
self.add_cell(
|
||||
"date_debut",
|
||||
|
@ -443,7 +445,7 @@ class RowAssiJusti(tb.Row):
|
|||
"entry_date",
|
||||
"Saisie le",
|
||||
(
|
||||
self.ligne["entry_date"].strftime("%d/%m/%y à %H:%M")
|
||||
self.ligne["entry_date"].strftime(scu.DATEATIME_FMT)
|
||||
if self.ligne["entry_date"]
|
||||
else "?"
|
||||
),
|
||||
|
|
|
@ -386,7 +386,7 @@ class TableRecap(tb.Table):
|
|||
for e in evals:
|
||||
col_id = f"eval_{e.id}"
|
||||
title = f"""{modimpl.module.code} {eval_index} {
|
||||
e.date_debut.strftime("%d/%m/%Y") if e.date_debut else ""
|
||||
e.date_debut.strftime(scu.DATE_FMT) if e.date_debut else ""
|
||||
}"""
|
||||
col_classes = []
|
||||
if first_eval:
|
||||
|
|
|
@ -48,7 +48,7 @@ div.submit > input {
|
|||
{% if justif %}
|
||||
<div class="info-saisie">
|
||||
Saisie par {{justif.user.get_prenomnom() if justif.user else "inconnu"}}
|
||||
le {{justif.entry_date.strftime("%d/%m/%Y à %H:%M") if justif.entry_date else "?"}}
|
||||
le {{justif.entry_date.strftime(scu.DATEATIME_FMT) if justif.entry_date else "?"}}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
{% block app_content %}
|
||||
<div class="tab-content">
|
||||
|
||||
<h2>Présence du groupe {{group_title}} le {{date_debut.strftime("%d/%m/%Y")}}
|
||||
de {{date_debut.strftime("%H:%M")}} à {{date_fin.strftime("%H:%M")}}
|
||||
<h2>Présence du groupe {{group_title}} le {{date_debut.strftime(scu.DATE_FMT)}}
|
||||
de {{date_debut.strftime(scu.TIME_FMT)}} à {{date_fin.strftime(scu.TIME_FMT)}}
|
||||
</h2>
|
||||
|
||||
<div>
|
||||
|
|
|
@ -326,15 +326,15 @@ main();
|
|||
<div class="plage">
|
||||
{% if ligne.justif.date_debut.date() == ligne.justif.date_fin.date() %}
|
||||
<span class="date">
|
||||
{{ligne.justif.date_debut.strftime("%d/%m/%y")}} de {{ligne.justif.date_debut.strftime("%Hh%M")}} à
|
||||
{{ligne.justif.date_fin.strftime("%Hh%M")}}
|
||||
{{ligne.justif.date_debut.strftime(scu.DATE_FMT)}} de {{ligne.justif.date_debut.strftime(scu.TIME_FMT)}} à
|
||||
{{ligne.justif.date_fin.strftime(scu.TIME_FMT)}}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="date_debut">
|
||||
du {{ligne.justif.date_debut.strftime("%d/%m/%y")}}
|
||||
du {{ligne.justif.date_debut.strftime(scu.DATE_FMT)}}
|
||||
</span>
|
||||
<span class="date_fin">
|
||||
au {{ligne.justif.date_fin.strftime("%d/%m/%y")}}
|
||||
au {{ligne.justif.date_fin.strftime(scu.DATE_FMT)}}
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="entry_date hint">
|
||||
|
@ -352,8 +352,8 @@ main();
|
|||
<li>
|
||||
{{scu.EtatAssiduite(assi.etat).version_lisible()}}
|
||||
{% if assi.date_debut.date() == assi.date_fin.date() %}
|
||||
du {{assi.date_debut.strftime("%d/%m/%y")}} de {{assi.date_debut.strftime("%Hh%M")}} à
|
||||
{{assi.date_fin.strftime("%Hh%M")}}
|
||||
du {{assi.date_debut.strftime(scu.DATE_FMT)}} de {{assi.date_debut.strftime(scu.TIME_FMT)}} à
|
||||
{{assi.date_fin.strftime(scu.TIME_FMT)}}
|
||||
{% else %}
|
||||
du {{assi.date_debut.strftime("%d/%m/%y %Hh%M")}} au {{assi.date_fin.strftime("%d/%m/%y
|
||||
%Hh%M")}}
|
||||
|
|
|
@ -30,15 +30,15 @@
|
|||
</div>
|
||||
{% if current_user.is_administrator() %}
|
||||
<div class="user_info_admin">
|
||||
<b>Dernière vue :</b> {{user.last_seen.strftime("%d/%m/%Y à %H:%M") if user.last_seen else "-"}}<br>
|
||||
<b>Dernière connexion CAS :</b> {{user.cas_last_login.strftime("%d/%m/%Y à %H:%M") if user.cas_last_login else "-"}}<br>
|
||||
<b>Dernière vue :</b> {{user.last_seen.strftime(scu.DATEATIME_FMT) if user.last_seen else "-"}}<br>
|
||||
<b>Dernière connexion CAS :</b> {{user.cas_last_login.strftime(scu.DATEATIME_FMT) if user.cas_last_login else "-"}}<br>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="user_basics">
|
||||
<b>Dernière modif mot de passe:</b>
|
||||
{{user.date_modif_passwd.strftime("%d/%m/%Y") if user.date_modif_passwd else ""}}<br>
|
||||
{{user.date_modif_passwd.strftime(scu.DATE_FMT) if user.date_modif_passwd else ""}}<br>
|
||||
<b>Date d'expiration:</b>
|
||||
{{user.date_expiration.strftime("%d/%m/%Y") if user.date_expiration else "(sans limite)"}}
|
||||
{{user.date_expiration.strftime(scu.DATE_FMT) if user.date_expiration else "(sans limite)"}}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
{# Base de toutes les pages ScoDoc #}
|
||||
{% block doc -%}
|
||||
<!DOCTYPE html>
|
||||
{%- block doc -%}<!DOCTYPE html>{# Base de toutes les pages ScoDoc #}
|
||||
<html{% block html_attribs %}{% endblock html_attribs %}>
|
||||
{%- block html %}
|
||||
<head>
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
)}}">{{etud.nomprenom}}</a></h2>
|
||||
{% endif %}
|
||||
<form name="f" method="GET" action="{{request.base_url}}">
|
||||
<input type="hidden" name="formsemestre_id" value="{{formsemestre.id}}"></input>
|
||||
<input type="hidden" name="etudid" value="{{etud.id}}"></input>
|
||||
<input type="hidden" name="fmt" value="{{fmt}}"></input>
|
||||
<input type="hidden" name="formsemestre_id" value="{{formsemestre.id}}">
|
||||
<input type="hidden" name="etudid" value="{{etud.id}}">
|
||||
<input type="hidden" name="fmt" value="{{fmt}}">
|
||||
<div class="bull_titre_semestre">
|
||||
Bulletin
|
||||
<span class="bull_liensemestre">
|
||||
|
@ -36,7 +36,7 @@
|
|||
formsemestre_id=formsemestre.id,
|
||||
etudid=etud.id,
|
||||
)
|
||||
}}&version='+this.value;"" class="noprint">
|
||||
}}&version='+this.value;" class="noprint">
|
||||
{% if formsemestre.formation.is_apc() %}
|
||||
{% set menu_items = scu.BULLETINS_VERSIONS_BUT.items() %}
|
||||
{% else %}
|
||||
|
|
|
@ -134,7 +134,7 @@
|
|||
<div class="descr_jury">
|
||||
{% if bul.semestre.decision_annee %}
|
||||
Décision saisie le {{
|
||||
datetime.datetime.fromisoformat(bul.semestre.decision_annee.date).strftime("%d/%m/%Y")
|
||||
datetime.datetime.fromisoformat(bul.semestre.decision_annee.date).strftime(scu.DATE_FMT)
|
||||
}},
|
||||
année <b>BUT{{bul.semestre.decision_annee.ordre}}</b>
|
||||
<b>{{bul.semestre.decision_annee.code}}</b>.
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
{# -*- mode: jinja-html -*- #}
|
||||
{% extends "sco_page.j2" %}
|
||||
{% import 'wtf.j2' as wtf %}
|
||||
|
||||
{% block app_content %}
|
||||
<h1>Changer le référentiel de compétences de la formation
|
||||
{{formation.get_titre_version()}}
|
||||
</h1>
|
||||
|
||||
<div class="help">
|
||||
|
||||
<p> Normalement, il n'est pas possible de changer une
|
||||
formation de référentiel de compétences. En effet, de nombreux éléments sont
|
||||
déduis du référentiel: les parcours, les compétences, les apprentissages
|
||||
critiques, et donc les validations de jury des étudiants qui ont suivi la
|
||||
formation.
|
||||
</p>
|
||||
|
||||
<p> Cependant, dans certains cas, le ministère a publié une nouvelle version
|
||||
d'un référentiel de spécialité, mais les changements ne sont que très légers:
|
||||
détails des noms de parcours ou des compétences. Dans ces cas là, la structure
|
||||
étant la même, il est autorisé de changer le référentiel.
|
||||
</p>
|
||||
|
||||
<p>Seuls les référentiels déjà chargés et compatibles (ayant la même structure)
|
||||
sont proposés dans le menu ci-dessous.
|
||||
</p>
|
||||
|
||||
<p class="fontred">
|
||||
Attention: tout changement de référentiel entraine la perte des associations
|
||||
entre les modules et les apprentissages critiques.
|
||||
</p>
|
||||
|
||||
<p class="fontred">
|
||||
Attention: <b>fonction expérimentale</b>. Vérifiez vos sauvegardes.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div>La formation est actuellement associée au référentiel
|
||||
<a class="stdlink" href="{{
|
||||
url_for('notes.refcomp_show',
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
refcomp_id=formation.referentiel_competence.id)
|
||||
}}">{{formation.referentiel_competence.get_title()}}</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" style="margin-top: 24px;">
|
||||
{{ form.hidden_tag() }}
|
||||
<div>
|
||||
{{ form.object_select.label }}<br>
|
||||
{{ form.object_select }}
|
||||
</div>
|
||||
<div style="margin-top: 24px;">{{ form.submit() }} {{ form.cancel() }}</div>
|
||||
</form>
|
||||
|
||||
|
||||
{% endblock %}
|
|
@ -18,7 +18,7 @@
|
|||
<div>{{validation.ue1.acronyme}} - {{validation.ue2.acronyme}}</div>
|
||||
<div>Jury de {{validation.formsemestre.titre_annee() if validation.formsemestre else "-"}}</div>
|
||||
<div>enregistré le {{
|
||||
validation.date.strftime("%d/%m/%Y à %H:%M")
|
||||
validation.date.strftime(scu.DATEATIME_FMT)
|
||||
}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<h2>Référentiel de compétences {{ref.type_titre}} {{ref.specialite_long}}</h2>
|
||||
|
||||
<div class="help">
|
||||
Référentiel chargé le {{ref.scodoc_date_loaded.strftime("%d/%m/%Y à %H:%M") if ref.scodoc_date_loaded else ""}} à
|
||||
Référentiel chargé le {{ref.scodoc_date_loaded.strftime(scu.DATEATIME_FMT) if ref.scodoc_date_loaded else ""}} à
|
||||
partir du fichier <tt>{{ref.scodoc_orig_filename or "(inconnu)"}}</tt>.
|
||||
</div>
|
||||
|
||||
|
@ -27,9 +27,18 @@
|
|||
<li>Formations se référant à ce référentiel:
|
||||
<ul>
|
||||
{% for formation in ref.formations %}
|
||||
<li><a class="stdlink" href="{{
|
||||
url_for('notes.ue_table', scodoc_dept=g.scodoc_dept, formation_id=formation.id )
|
||||
}}">{{ formation.get_titre_version() }}</a></li>
|
||||
<li>
|
||||
<a class="stdlink" href="{{
|
||||
url_for('notes.ue_table', scodoc_dept=g.scodoc_dept, formation_id=formation.id )
|
||||
}}">{{ formation.get_titre_version() }}</a>
|
||||
{% if referentiels_equivalents %}
|
||||
<a style="margin-left: 8px;" class="stdlink" href="
|
||||
{{ url_for('notes.formation_change_refcomp',
|
||||
scodoc_dept=g.scodoc_dept, formation_id=formation.id )
|
||||
}}
|
||||
">(changer)</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% else %}
|
||||
<li><em>aucune</em></li>
|
||||
{% endfor %}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<div>Jury de {{validation.formsemestre.titre_annee()
|
||||
if validation.formsemestre else "-"}}</div>
|
||||
<div>enregistrée le {{
|
||||
validation.event_date.strftime("%d/%m/%Y à %H:%M")
|
||||
validation.event_date.strftime(scu.DATEATIME_FMT)
|
||||
if validation.event_date else "-"
|
||||
}}</div>
|
||||
<div>{{"%g"|format(validation.ects())}} ECTS</div>
|
||||
|
@ -116,7 +116,7 @@
|
|||
<div class="scoplement">
|
||||
<div>Validation du RCUE</div>
|
||||
<div>enregistrée le {{
|
||||
validation.date.strftime("%d/%m/%Y à %H:%M")
|
||||
validation.date.strftime(scu.DATEATIME_FMT)
|
||||
if validation.date else "-"
|
||||
}}
|
||||
</div>
|
||||
|
|
|
@ -3,39 +3,46 @@
|
|||
<!-- formsemestre_header -->
|
||||
<div class="formsemestre_page_title noprint">
|
||||
<div class="infos">
|
||||
<span class="semtitle"><a class="stdlink" title="{{sco.sem.session_id()}}" href="{{
|
||||
<span class="semtitle"><a class="stdlink" title="{{sco.formsemestre.session_id()}}" href="{{
|
||||
url_for('notes.formsemestre_status',
|
||||
scodoc_dept=g.scodoc_dept, formsemestre_id=sco.sem.id)
|
||||
}}">{{sco.sem.titre}}</a>
|
||||
<a title="{{sco.sem.etapes_apo_str()}}">
|
||||
{% if sco.sem.semestre_id != -1 %}, {{sco.sem.formation.get_cursus().SESSION_NAME}}
|
||||
{{sco.sem.semestre_id}}
|
||||
scodoc_dept=g.scodoc_dept, formsemestre_id=sco.formsemestre.id)
|
||||
}}">{{sco.formsemestre.titre}}</a>
|
||||
<a title="{{sco.formsemestre.etapes_apo_str()}}">
|
||||
{% if sco.formsemestre.semestre_id != -1 %}, {{sco.formsemestre.formation.get_cursus().SESSION_NAME}}
|
||||
{{sco.formsemestre.semestre_id}}
|
||||
{% endif %}</a>
|
||||
{% if sco.sem.modalite %} en {{sco.sem.modalite}}{% endif %}</span>
|
||||
{% if sco.formsemestre.modalite %} en {{sco.formsemestre.modalite}}{% endif %}
|
||||
</span>
|
||||
<span class="dates">
|
||||
<a title="du {{sco.sem.date_debut.strftime('%d/%m/%Y')}}
|
||||
au {{sco.sem.date_fin.strftime('%d/%m/%Y')}} ">{{scu.MONTH_NAMES_ABBREV[ sco.sem.date_debut.month - 1]}}
|
||||
{{sco.sem.date_debut.year}} - {{scu.MONTH_NAMES_ABBREV[sco.sem.date_fin.month - 1]}}
|
||||
{{sco.sem.date_fin.year}}</a></span>
|
||||
<a title="du {{sco.formsemestre.date_debut.strftime('%d/%m/%Y')}}
|
||||
au {{sco.formsemestre.date_fin.strftime('%d/%m/%Y')}} ">{{scu.MONTH_NAMES_ABBREV[ sco.formsemestre.date_debut.month - 1]}}
|
||||
{{sco.formsemestre.date_debut.year}} - {{scu.MONTH_NAMES_ABBREV[sco.formsemestre.date_fin.month - 1]}}
|
||||
{{sco.formsemestre.date_fin.year}}</a>
|
||||
</span>
|
||||
<span class="resp"><a
|
||||
title="{{sco.sem.responsables_str(abbrev_prenom=False)}}">{{sco.sem.responsables_str()}}</a></span>
|
||||
title="{{sco.formsemestre.responsables_str(abbrev_prenom=False)}}">{{sco.formsemestre.responsables_str()}}</a></span>
|
||||
<span class="nbinscrits"><a class="discretelink" href="{{url_for('scolar.groups_view', scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=sco.sem.id)}}">{{sco.sem.inscriptions|length}} inscrits</a></span><span class="lock">{% if
|
||||
not sco.sem.etat %}<a href="{{url_for('notes.formsemestre_flip_lock', scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=sco.sem.id)}}">{{scu.icontag("lock_img", border="0", title="Semestre
|
||||
verrouillé")|safe}}</a>{% endif %}</span><span class="eye">
|
||||
formsemestre_id=sco.formsemestre.id)}}">{{sco.formsemestre.inscriptions|length}} inscrits</a></span>
|
||||
<span class="lock">
|
||||
{% if not sco.formsemestre.etat %}<a href="{{url_for('notes.formsemestre_flip_lock', scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=sco.formsemestre.id)}}">{{scu.icontag("lock_img", border="0", title="Semestre
|
||||
verrouillé")|safe}}</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="eye">
|
||||
{% if not scu.is_passerelle_disabled() %}
|
||||
<a href="{{url_for('notes.formsemestre_change_publication_bul', scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=sco.sem.id)}}">
|
||||
{% if sco.sem.bul_hide_xml %}
|
||||
formsemestre_id=sco.formsemestre.id)}}">
|
||||
{% if sco.formsemestre.bul_hide_xml %}
|
||||
{{ scu.ICON_HIDDEN|safe}}
|
||||
{% else %}
|
||||
{{ scu.ICON_PUBLISHED|safe }}
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{{ sco.sem_menu_bar|safe }}
|
||||
{{ sco.formsemestre_status_menu_bar()|safe }}
|
||||
</div>
|
||||
<!-- end of formsemestre_header -->
|
|
@ -1,5 +1,5 @@
|
|||
{%- extends 'babase.j2' -%}
|
||||
{# -*- Base des pages ordinaires, dans départements -*- #}
|
||||
{% extends 'babase.j2' %}
|
||||
|
||||
{% block styles %}
|
||||
{{super()}}
|
||||
|
@ -21,24 +21,25 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- sco_page -->
|
||||
{% block scodoc_sidebar %}
|
||||
{% include "sidebar.j2" %}
|
||||
{% include "sidebar.j2" %}
|
||||
{% endblock %}
|
||||
|
||||
<div id="gtrcontent" class="gtrcontent">
|
||||
{% include "flashed_messages.j2" %}
|
||||
{% if sco.sem %}
|
||||
{% block formsemestre_header %}
|
||||
{% include "formsemestre_header.j2" %}
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
<div id="gtrcontent" class="gtrcontent">
|
||||
{% include "flashed_messages.j2" %}
|
||||
{% if sco.formsemestre %}
|
||||
{% block formsemestre_header %}
|
||||
{% include "formsemestre_header.j2" %}
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
|
||||
<div class="sco-app-content">
|
||||
{% block app_content %}
|
||||
page vide
|
||||
{% endblock %}
|
||||
<div class="sco-app-content">
|
||||
{% block app_content %}
|
||||
page vide
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
{% if sco.etud_cur_sem %}
|
||||
<span title="absences du {{ sco.etud_cur_sem['date_debut'].strftime('%d/%m/%Y') }}
|
||||
au {{ sco.etud_cur_sem['date_fin'].strftime('%d/%m/%Y') }}">({{sco.prefs["assi_metrique"]}})
|
||||
<br />{{'%1g'|format(sco.nbabsjust)}} J., {{'%1g'|format(sco.nbabsnj)}} N.J.</span>
|
||||
<br />{{'%1g'|format(sco.nb_abs_just)}} J., {{'%1g'|format(sco.nb_abs_nj)}} N.J.</span>
|
||||
{% endif %}
|
||||
<ul>
|
||||
{% if current_user.has_permission(sco.Permission.AbsChange) %}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"""ScoDoc Flask views
|
||||
"""
|
||||
import datetime
|
||||
from functools import cached_property
|
||||
|
||||
from flask import Blueprint
|
||||
from flask import g, current_app, request
|
||||
|
@ -57,8 +58,20 @@ class ScoData:
|
|||
self.Permission = Permission
|
||||
self.scu = scu
|
||||
self.SCOVERSION = sco_version.SCOVERSION
|
||||
# -- Informations étudiant courant, si sélectionné:
|
||||
if etud is None:
|
||||
self._init_etud = etud
|
||||
self._init_formsemestre = formsemestre
|
||||
# les comptes d'absences sont initialisés lors de l'accès à etud_cur_sem
|
||||
self.nb_abs_nj = 0
|
||||
self.nb_abs_just = 0
|
||||
self.nb_abs = 0
|
||||
# .etud, .formsemestre, etc. sont des cached_property
|
||||
# car ScoData() est créé par @context_processor
|
||||
# AVANT le décorateur scodoc qui initialise g.scodoc_xxx
|
||||
|
||||
@cached_property
|
||||
def etud(self) -> Identite | None:
|
||||
"Informations étudiant courant, si sélectionné"
|
||||
if self._init_etud is None:
|
||||
etudid = g.get("etudid", None)
|
||||
if etudid is None:
|
||||
if request.method == "GET":
|
||||
|
@ -66,50 +79,65 @@ class ScoData:
|
|||
elif request.method == "POST":
|
||||
etudid = request.form.get("etudid", None)
|
||||
if etudid is not None:
|
||||
etud = Identite.get_etud(etudid)
|
||||
self.etud = etud
|
||||
if etud is not None:
|
||||
# Infos sur l'étudiant courant
|
||||
ins = self.etud.inscription_courante()
|
||||
if ins:
|
||||
self.etud_cur_sem = ins.formsemestre
|
||||
(
|
||||
self.nbabsnj,
|
||||
self.nbabsjust,
|
||||
self.nbabs,
|
||||
) = sco_assiduites.get_assiduites_count_in_interval(
|
||||
etud.id,
|
||||
self.etud_cur_sem.date_debut.isoformat(),
|
||||
self.etud_cur_sem.date_fin.isoformat(),
|
||||
scu.translate_assiduites_metric(
|
||||
sco_preferences.get_preference("assi_metrique")
|
||||
),
|
||||
)
|
||||
else:
|
||||
self.etud_cur_sem = None
|
||||
else:
|
||||
self.etud = None
|
||||
# --- Informations sur semestre courant, si sélectionné
|
||||
if formsemestre is None:
|
||||
formsemestre_id = retreive_formsemestre_from_request()
|
||||
if formsemestre_id is not None:
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
if formsemestre is None:
|
||||
self.sem = None
|
||||
self.sem_menu_bar = None
|
||||
else:
|
||||
self.sem = formsemestre
|
||||
self.sem_menu_bar = sco_formsemestre_status.formsemestre_status_menubar(
|
||||
self.sem
|
||||
return Identite.get_etud(etudid)
|
||||
return self._init_etud
|
||||
|
||||
@cached_property
|
||||
def etud_cur_sem(self) -> FormSemestre | None:
|
||||
"le semestre courant de l'étudiant courant"
|
||||
etud = self.etud
|
||||
if etud is None:
|
||||
return None
|
||||
ins = self.etud.inscription_courante()
|
||||
cur_sem = ins.formsemestre
|
||||
if ins:
|
||||
(
|
||||
self.nb_abs_nj,
|
||||
self.nb_abs_just,
|
||||
self.nb_abs,
|
||||
) = sco_assiduites.get_assiduites_count_in_interval(
|
||||
etud.id,
|
||||
cur_sem.date_debut.isoformat(),
|
||||
cur_sem.date_fin.isoformat(),
|
||||
scu.translate_assiduites_metric(
|
||||
sco_preferences.get_preference("assi_metrique")
|
||||
),
|
||||
)
|
||||
self.formsemestre = formsemestre
|
||||
# --- Préférences
|
||||
# prefs fallback to global pref if sem is None:
|
||||
if formsemestre:
|
||||
formsemestre_id = formsemestre.id
|
||||
else:
|
||||
formsemestre_id = None
|
||||
self.prefs = sco_preferences.SemPreferences(formsemestre_id)
|
||||
return cur_sem
|
||||
return None
|
||||
|
||||
@cached_property
|
||||
def formsemestre(self) -> FormSemestre | None:
|
||||
"Le formsemestre courant, si sélectionné"
|
||||
if self._init_formsemestre is None:
|
||||
formsemestre_id = retreive_formsemestre_from_request()
|
||||
return (
|
||||
FormSemestre.get_formsemestre(formsemestre_id)
|
||||
if formsemestre_id is not None
|
||||
else None
|
||||
)
|
||||
return self._init_formsemestre
|
||||
|
||||
@cached_property
|
||||
def sem_menu_bar(self) -> str | None:
|
||||
"Le html de la bare de menu formsemestre s'il y en a un."
|
||||
return (
|
||||
sco_formsemestre_status.formsemestre_status_menubar(self.formsemestre)
|
||||
if self.formsemestre
|
||||
else None
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def prefs(self):
|
||||
"Préférences"
|
||||
# prefs fallback to global pref if no current formsemestre:
|
||||
return sco_preferences.SemPreferences(
|
||||
self.formsemestre.id if self.formsemestre else None
|
||||
)
|
||||
|
||||
def formsemestre_status_menu_bar(self) -> str:
|
||||
"Le HTML de la barre de menu semestre"
|
||||
return sco_formsemestre_status.formsemestre_status_menubar(self.formsemestre)
|
||||
|
||||
|
||||
from app.views import (
|
||||
|
|
|
@ -313,13 +313,13 @@ def _get_dates_from_assi_form(
|
|||
date_fin = None
|
||||
# On commence par convertir individuellement tous les champs
|
||||
try:
|
||||
date_debut = datetime.datetime.strptime(form.date_debut.data, "%d/%m/%Y")
|
||||
date_debut = datetime.datetime.strptime(form.date_debut.data, scu.DATE_FMT)
|
||||
except ValueError:
|
||||
date_debut = None
|
||||
form.set_error("date début invalide", form.date_debut)
|
||||
try:
|
||||
date_fin = (
|
||||
datetime.datetime.strptime(form.date_fin.data, "%d/%m/%Y")
|
||||
datetime.datetime.strptime(form.date_fin.data, scu.DATE_FMT)
|
||||
if form.date_fin.data
|
||||
else None
|
||||
)
|
||||
|
@ -358,7 +358,7 @@ def _get_dates_from_assi_form(
|
|||
# La date de dépôt (si vide, la date actuelle)
|
||||
try:
|
||||
dt_entry_date = (
|
||||
datetime.datetime.strptime(form.entry_date.data, "%d/%m/%Y")
|
||||
datetime.datetime.strptime(form.entry_date.data, scu.DATE_FMT)
|
||||
if form.entry_date.data
|
||||
else datetime.datetime.now() # local tz
|
||||
)
|
||||
|
@ -573,8 +573,8 @@ def bilan_etud():
|
|||
abort(404, "étudiant inexistant dans ce département")
|
||||
|
||||
# Gestion des dates du bilan (par défaut l'année scolaire)
|
||||
date_debut = scu.date_debut_annee_scolaire().strftime("%d/%m/%Y")
|
||||
date_fin: str = scu.date_fin_annee_scolaire().strftime("%d/%m/%Y")
|
||||
date_debut = scu.date_debut_annee_scolaire().strftime(scu.DATE_FMT)
|
||||
date_fin: str = scu.date_fin_annee_scolaire().strftime(scu.DATE_FMT)
|
||||
|
||||
# Récupération de la métrique d'assiduité
|
||||
assi_metric = scu.translate_assiduites_metric(
|
||||
|
@ -634,18 +634,18 @@ def edit_justificatif_etud(justif_id: int):
|
|||
form = AjoutJustificatifEtudForm(obj=justif)
|
||||
# Set the default value for the etat field
|
||||
if request.method == "GET":
|
||||
form.date_debut.data = justif.date_debut.strftime("%d/%m/%Y")
|
||||
form.date_fin.data = justif.date_fin.strftime("%d/%m/%Y")
|
||||
form.date_debut.data = justif.date_debut.strftime(scu.DATE_FMT)
|
||||
form.date_fin.data = justif.date_fin.strftime(scu.DATE_FMT)
|
||||
if form.date_fin.data == form.date_debut.data:
|
||||
# un seul jour: pas de date de fin, indique les heures
|
||||
form.date_fin.data = ""
|
||||
form.heure_debut.data = justif.date_debut.strftime("%H:%M")
|
||||
form.heure_fin.data = justif.date_fin.strftime("%H:%M")
|
||||
form.heure_debut.data = justif.date_debut.strftime(scu.TIME_FMT)
|
||||
form.heure_fin.data = justif.date_fin.strftime(scu.TIME_FMT)
|
||||
form.entry_date.data = (
|
||||
justif.entry_date.strftime("%d/%m/%Y") if justif.entry_date else ""
|
||||
justif.entry_date.strftime(scu.DATE_FMT) if justif.entry_date else ""
|
||||
)
|
||||
form.entry_time.data = (
|
||||
justif.entry_date.strftime("%H:%M") if justif.entry_date else ""
|
||||
justif.entry_date.strftime(scu.TIME_FMT) if justif.entry_date else ""
|
||||
)
|
||||
form.etat.data = str(justif.etat)
|
||||
|
||||
|
@ -968,7 +968,7 @@ def choix_date() -> str:
|
|||
ok: bool = False
|
||||
try:
|
||||
date: datetime.date = datetime.datetime.strptime(
|
||||
form.date.data, "%d/%m/%Y"
|
||||
form.date.data, scu.DATE_FMT
|
||||
).date()
|
||||
if date < formsemestre.date_debut or date > formsemestre.date_fin:
|
||||
form.set_error(
|
||||
|
@ -999,8 +999,8 @@ def choix_date() -> str:
|
|||
"assiduites/pages/choix_date.j2",
|
||||
form=form,
|
||||
sco=ScoData(formsemestre=formsemestre),
|
||||
deb=formsemestre.date_debut.strftime("%d/%m/%Y"),
|
||||
fin=formsemestre.date_fin.strftime("%d/%m/%Y"),
|
||||
deb=formsemestre.date_debut.strftime(scu.DATE_FMT),
|
||||
fin=formsemestre.date_fin.strftime(scu.DATE_FMT),
|
||||
)
|
||||
|
||||
|
||||
|
@ -1607,11 +1607,11 @@ def recup_assiduites_plage():
|
|||
|
||||
# Vérification des dates
|
||||
try:
|
||||
date_deb = datetime.datetime.strptime(date_deb, "%d/%m/%Y")
|
||||
date_deb = datetime.datetime.strptime(date_deb, scu.DATE_FMT)
|
||||
except ValueError as exc:
|
||||
raise ScoValueError("date_debut invalide", dest_url=request.referrer) from exc
|
||||
try:
|
||||
date_fin = datetime.datetime.strptime(date_fin, "%d/%m/%Y")
|
||||
date_fin = datetime.datetime.strptime(date_fin, scu.DATE_FMT)
|
||||
except ValueError as exc:
|
||||
raise ScoValueError("date_fin invalide", dest_url=request.referrer) from exc
|
||||
|
||||
|
@ -1893,12 +1893,12 @@ def _preparer_objet(
|
|||
"filenames": filenames,
|
||||
}
|
||||
|
||||
objet_prepare["date_fin"] = objet.date_fin.strftime("%d/%m/%y à %H:%M")
|
||||
objet_prepare["date_fin"] = objet.date_fin.strftime(scu.DATEATIME_FMT)
|
||||
objet_prepare["real_date_fin"] = objet.date_fin.isoformat()
|
||||
objet_prepare["date_debut"] = objet.date_debut.strftime("%d/%m/%y à %H:%M")
|
||||
objet_prepare["date_debut"] = objet.date_debut.strftime(scu.DATEATIME_FMT)
|
||||
objet_prepare["real_date_debut"] = objet.date_debut.isoformat()
|
||||
|
||||
objet_prepare["entry_date"] = objet.entry_date.strftime("%d/%m/%y à %H:%M")
|
||||
objet_prepare["entry_date"] = objet.entry_date.strftime(scu.DATEATIME_FMT)
|
||||
|
||||
objet_prepare["etud_nom"] = objet.etudiant.nomprenom
|
||||
|
||||
|
@ -2243,7 +2243,7 @@ def _get_date_str(deb: datetime.datetime, fin: datetime.datetime) -> str:
|
|||
"du dd/mm/yyyy hh:MM audd/mm/yyyy hh:MM" sinon
|
||||
"""
|
||||
if deb.date() == fin.date():
|
||||
temps = deb.strftime("%d/%m/%Y %H:%M").split(" ") + [fin.strftime("%H:%M")]
|
||||
temps = deb.strftime("%d/%m/%Y %H:%M").split(" ") + [fin.strftime(scu.TIME_FMT)]
|
||||
return f"le {temps[0]} de {temps[1]} à {temps[2]}"
|
||||
return f'du {deb.strftime("%d/%m/%Y %H:%M")} au {fin.strftime("%d/%m/%Y %H:%M")}'
|
||||
|
||||
|
@ -2542,7 +2542,7 @@ class Jour:
|
|||
"""
|
||||
Renvoie la date du jour au format "dd/mm/yyyy"
|
||||
"""
|
||||
return self.date.strftime("%d/%m/%Y")
|
||||
return self.date.strftime(scu.DATE_FMT)
|
||||
|
||||
def get_class(self, show_pres: bool = False, show_reta: bool = False) -> str:
|
||||
"""
|
||||
|
|
|
@ -45,7 +45,6 @@ from app import models
|
|||
from app.auth.models import User
|
||||
from app.but import (
|
||||
apc_edit_ue,
|
||||
bulletin_but_court, # ne pas enlever: ajoute des routes !
|
||||
cursus_but,
|
||||
jury_edit_manual,
|
||||
jury_but,
|
||||
|
@ -53,6 +52,8 @@ from app.but import (
|
|||
jury_but_validation_auto,
|
||||
jury_but_view,
|
||||
)
|
||||
from app.but import bulletin_but_court # ne pas enlever: ajoute des routes !
|
||||
|
||||
from app.but.forms import jury_but_forms
|
||||
|
||||
|
||||
|
@ -449,12 +450,14 @@ sco_publish(
|
|||
Permission.EditFormation,
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
sco_publish(
|
||||
"/ue_edit",
|
||||
sco_edit_ue.ue_edit,
|
||||
Permission.EditFormation,
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/ue_edit/<int:ue_id>", methods=["GET", "POST"])
|
||||
@scodoc
|
||||
@permission_required(Permission.EditFormation)
|
||||
def ue_edit(ue_id: int):
|
||||
"Edition de l'UE"
|
||||
return sco_edit_ue.ue_edit(ue_id)
|
||||
|
||||
|
||||
@bp.route("/set_ue_niveau_competence", methods=["POST"])
|
||||
|
@ -504,7 +507,7 @@ def ue_table(formation_id=None, semestre_idx=1, msg=""):
|
|||
@bp.route("/ue_infos/<int:ue_id>")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def ue_infos(ue_id):
|
||||
def ue_infos(ue_id: int):
|
||||
ue = UniteEns.query.get_or_404(ue_id)
|
||||
return sco_edit_apc.html_ue_infos(ue)
|
||||
|
||||
|
@ -688,15 +691,23 @@ def module_clone():
|
|||
def index_html():
|
||||
"Page accueil formations"
|
||||
fmt = request.args.get("fmt", "html")
|
||||
detail = scu.to_bool(request.args.get("detail", False))
|
||||
|
||||
editable = current_user.has_permission(Permission.EditFormation)
|
||||
table = sco_formations.formation_list_table()
|
||||
table = sco_formations.formation_list_table(detail=detail)
|
||||
|
||||
if fmt != "html":
|
||||
return table.make_page(fmt=fmt, filename=f"Formations-{g.scodoc_dept}")
|
||||
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title="Formations (programmes)"),
|
||||
"""<h2>Formations (programmes pédagogiques)</h2>
|
||||
f"""<h2>Formations (programmes pédagogiques)</h2>
|
||||
<form>
|
||||
<input type="checkbox" id="detailCheckbox" name="detail"
|
||||
onchange="this.form.submit();"
|
||||
{'checked' if detail else ''}>
|
||||
<label for="detailCheckbox">Informations détaillées</label>
|
||||
</form>
|
||||
""",
|
||||
table.html(),
|
||||
]
|
||||
|
@ -2452,7 +2463,7 @@ def formsemestre_validation_but(
|
|||
if formsemestre.date_fin - datetime.date.today() > datetime.timedelta(days=12):
|
||||
# encore loin de la fin du semestre de départ de ce jury ?
|
||||
warning += f"""<div class="warning">Le semestre S{formsemestre.semestre_id}
|
||||
terminera le {formsemestre.date_fin.strftime("%d/%m/%Y")} :
|
||||
terminera le {formsemestre.date_fin.strftime(scu.DATE_FMT)} :
|
||||
êtes-vous certain de vouloir enregistrer une décision de jury ?
|
||||
</div>"""
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ PN / Référentiel de compétences
|
|||
|
||||
Emmanuel Viennet, 2021
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
|
@ -19,11 +20,20 @@ from app import db, log
|
|||
from app.decorators import scodoc, permission_required
|
||||
from app.models import Formation
|
||||
from app.models.but_refcomp import ApcReferentielCompetences
|
||||
from app.but import change_refcomp
|
||||
from app.but.import_refcomp import orebut_import_refcomp
|
||||
from app.but.forms.refcomp_forms import FormationRefCompForm, RefCompLoadForm
|
||||
from app.but.forms.refcomp_forms import (
|
||||
FormationChangeRefCompForm,
|
||||
FormationRefCompForm,
|
||||
RefCompLoadForm,
|
||||
)
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc.sco_exceptions import ScoFormatError, ScoValueError
|
||||
from app.scodoc.sco_exceptions import (
|
||||
ScoFormatError,
|
||||
ScoNoReferentielCompetences,
|
||||
ScoValueError,
|
||||
)
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.views import notes_bp as bp
|
||||
from app.views import ScoData
|
||||
|
@ -47,9 +57,12 @@ def refcomp(refcomp_id):
|
|||
def refcomp_show(refcomp_id):
|
||||
"""Affichage du référentiel de compétences."""
|
||||
referentiel_competence = ApcReferentielCompetences.query.get_or_404(refcomp_id)
|
||||
# Autres référentiels "équivalents" pour proposer de changer les formations:
|
||||
referentiels_equivalents = referentiel_competence.equivalents()
|
||||
return render_template(
|
||||
"but/refcomp_show.j2",
|
||||
ref=referentiel_competence,
|
||||
referentiels_equivalents=referentiels_equivalents,
|
||||
title="Référentiel de compétences",
|
||||
data_source=url_for(
|
||||
"notes.refcomp",
|
||||
|
@ -279,3 +292,55 @@ def refcomp_load(formation_id=None):
|
|||
formation=formation,
|
||||
title="Chargement réf. compétences",
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/formation/<int:formation_id>/change_refcomp", methods=["GET", "POST"])
|
||||
@scodoc
|
||||
@permission_required(Permission.EditFormation)
|
||||
def formation_change_refcomp(formation_id: int):
|
||||
"""Tente de changer le ref. de comp. de la formation"""
|
||||
formation = Formation.get_formation(formation_id)
|
||||
ref_comp: ApcReferentielCompetences = formation.referentiel_competence
|
||||
if ref_comp is None:
|
||||
raise ScoNoReferentielCompetences(formation=formation)
|
||||
# Autres référentiels "équivalents" pour proposer de changer les formations:
|
||||
referentiels_equivalents = ref_comp.equivalents()
|
||||
form = FormationChangeRefCompForm()
|
||||
form.object_select.choices = [
|
||||
(ref.id, ref.get_title()) for ref in referentiels_equivalents
|
||||
]
|
||||
if request.method == "POST" and form.cancel.data: # cancel button
|
||||
return redirect(
|
||||
url_for(
|
||||
"notes.refcomp_show",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
refcomp_id=formation.referentiel_competence.id,
|
||||
)
|
||||
)
|
||||
if form.validate_on_submit():
|
||||
try:
|
||||
new_ref_id = int(form.object_select.data)
|
||||
except TypeError as exc:
|
||||
raise ScoValueError("nouveau refcomp id invalide") from exc
|
||||
new_ref = None
|
||||
for ref in referentiels_equivalents:
|
||||
if ref.id == new_ref_id:
|
||||
new_ref = ref
|
||||
break
|
||||
if new_ref is None:
|
||||
raise ScoValueError("nouveau refcomp invalide")
|
||||
change_refcomp.formation_change_referentiel(formation, new_ref)
|
||||
flash("Formation changée de référentiel")
|
||||
return redirect(
|
||||
url_for(
|
||||
"notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation.id
|
||||
)
|
||||
)
|
||||
|
||||
return render_template(
|
||||
"but/change_refcomp.j2",
|
||||
form=form,
|
||||
formation=formation,
|
||||
referentiels_equivalents=referentiels_equivalents,
|
||||
title="Changer de référentiel de compétences",
|
||||
)
|
||||
|
|
|
@ -823,7 +823,8 @@ def form_change_coordonnees(etudid):
|
|||
("telephonemobile", {"size": 13, "title": "Mobile"}),
|
||||
),
|
||||
initvalues=adr,
|
||||
submitlabel="Valider le formulaire",
|
||||
submitlabel="Enregistrer",
|
||||
cancelbutton="Annuler",
|
||||
)
|
||||
dest_url = url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||
if tf[0] == 0:
|
||||
|
@ -1162,7 +1163,7 @@ def _form_dem_of_def(
|
|||
).first_or_404()
|
||||
if not formsemestre.etat:
|
||||
raise ScoValueError("Modification impossible: semestre verrouille")
|
||||
nowdmy = time.strftime("%d/%m/%Y")
|
||||
nowdmy = time.strftime(scu.DATE_FMT)
|
||||
#
|
||||
header = html_sco_header.sco_header(
|
||||
page_title=f"""{operation_name} de {etud.nomprenom} (du semestre {formsemestre.titre_mois()})"""
|
||||
|
|
|
@ -245,7 +245,7 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
|
|||
initvalues["roles"] = []
|
||||
if "date_expiration" in initvalues:
|
||||
initvalues["date_expiration"] = (
|
||||
the_user.date_expiration.strftime("%d/%m/%Y")
|
||||
the_user.date_expiration.strftime(scu.DATE_FMT)
|
||||
if the_user.date_expiration
|
||||
else ""
|
||||
)
|
||||
|
@ -391,9 +391,9 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
|
|||
{
|
||||
"title": "e-mail",
|
||||
"input_type": "text",
|
||||
"explanation": "requis, doit fonctionner"
|
||||
if not edit_only_roles
|
||||
else "",
|
||||
"explanation": (
|
||||
"requis, doit fonctionner" if not edit_only_roles else ""
|
||||
),
|
||||
"size": 36,
|
||||
"allow_null": False,
|
||||
"readonly": edit_only_roles,
|
||||
|
@ -448,10 +448,10 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
|
|||
"title": "e-mail institutionnel",
|
||||
"input_type": "text",
|
||||
"explanation": (
|
||||
"requis" if require_email_institutionnel else "facultatif"
|
||||
)
|
||||
if not edit_only_roles
|
||||
else "",
|
||||
("requis" if require_email_institutionnel else "facultatif")
|
||||
if not edit_only_roles
|
||||
else ""
|
||||
),
|
||||
"size": 36,
|
||||
"allow_null": not require_email_institutionnel,
|
||||
"readonly": edit_only_roles,
|
||||
|
@ -550,9 +550,11 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
|
|||
{
|
||||
"title": "Date d'expiration", # j/m/a
|
||||
"input_type": "datedmy",
|
||||
"explanation": "j/m/a, laisser vide si pas de limite"
|
||||
if not edit_only_roles
|
||||
else "",
|
||||
"explanation": (
|
||||
"j/m/a, laisser vide si pas de limite"
|
||||
if not edit_only_roles
|
||||
else ""
|
||||
),
|
||||
"size": 9,
|
||||
"allow_null": True,
|
||||
"readonly": edit_only_roles,
|
||||
|
@ -664,7 +666,7 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
|
|||
try:
|
||||
if vals["date_expiration"]:
|
||||
vals["date_expiration"] = datetime.datetime.strptime(
|
||||
vals["date_expiration"], "%d/%m/%Y"
|
||||
vals["date_expiration"], scu.DATE_FMT
|
||||
)
|
||||
if vals["date_expiration"] < datetime.datetime.now():
|
||||
H.append(tf_error_message("date expiration passée"))
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Certains référentiels de compétences peuvent être considérés
|
||||
# comme équivalents
|
||||
# Si ils ont la même structure
|
||||
# Voir ApcReferentielCompetences.map_to_other_referentiel
|
||||
|
||||
# Mappings: nouveau : ancien
|
||||
QLIO: # la clé est 'specialite'
|
||||
parcours: # codes de parcours
|
||||
OSC: MSC
|
||||
QMI: MQSE
|
||||
# et un transitoire UPHF:
|
||||
MPBS: MP
|
||||
PCLG: MSC
|
||||
QPSMI: MQSE
|
||||
ATN: MTD
|
||||
# competences: # titres de compétences ('nom_court' dans le XML)
|
||||
|
||||
SD: STID
|
|
@ -1,7 +1,7 @@
|
|||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.6.956"
|
||||
SCOVERSION = "9.6.960"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ from app.models.evaluations import Evaluation
|
|||
from app.scodoc import sco_dump_db
|
||||
from app.scodoc.sco_logos import make_logo_local
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.views import notes, scolar
|
||||
from app.views import notes, scolar, ScoData
|
||||
import app.scodoc.sco_utils as scu
|
||||
import tools
|
||||
from tools.fakedatabase import create_test_api_database
|
||||
|
@ -58,9 +58,9 @@ cli.register(app)
|
|||
|
||||
@app.context_processor
|
||||
def inject_sco_utils():
|
||||
"Make scu available in all Jinja templates"
|
||||
"Make scu and sco available in all Jinja templates"
|
||||
# if modified, put the same in conftest.py#27
|
||||
return dict(scu=scu)
|
||||
return {"scu": scu, "sco": ScoData()}
|
||||
|
||||
|
||||
@app.shell_context_processor
|
||||
|
|
|
@ -27,7 +27,7 @@ def test_client():
|
|||
@apptest.context_processor
|
||||
def inject_sco_utils():
|
||||
"Make scu available in all Jinja templates"
|
||||
return dict(scu=scu)
|
||||
return {"scu": scu, "sco": ScoData()}
|
||||
|
||||
with apptest.test_request_context():
|
||||
# initialize scodoc "g":
|
||||
|
|
|
@ -414,7 +414,7 @@ class ScoFake(object):
|
|||
for e_idx in range(1, nb_evaluations_per_module + 1):
|
||||
e = self.create_evaluation(
|
||||
moduleimpl_id=moduleimpl_id,
|
||||
date_debut=datetime.datetime.strptime(date_debut, "%d/%m/%Y"),
|
||||
date_debut=datetime.datetime.strptime(date_debut, scu.DATE_FMT),
|
||||
description="evaluation test %s" % e_idx,
|
||||
coefficient=1.0,
|
||||
)
|
||||
|
|
|
@ -962,8 +962,8 @@ def _create_abs(
|
|||
date_debut, date_fin, demijournee, estjust=False, etudid=False, estabs=True
|
||||
):
|
||||
etud = Identite.from_request(etudid)
|
||||
deb: dt.date = dt.datetime.strptime(date_debut, "%d/%m/%Y").date()
|
||||
fin: dt.date = dt.datetime.strptime(date_fin, "%d/%m/%Y").date()
|
||||
deb: dt.date = dt.datetime.strptime(date_debut, scu.DATE_FMT).date()
|
||||
fin: dt.date = dt.datetime.strptime(date_fin, scu.DATE_FMT).date()
|
||||
abs_list: list[Absence] = []
|
||||
while deb < fin:
|
||||
if deb.weekday() in [5, 6]:
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
"""Outils pour environnements de démo.
|
||||
|
||||
Change aléatoirement les identites (nip, civilite, nom, prenom) des étudiants d'un semestre.
|
||||
Change aléatoirement les identites (nip, civilite, nom, prenom) des étudiants.
|
||||
Un semestre ou tous si non spécifié.
|
||||
|
||||
Le NIP est choisi aléatoirement (nombre entier à 8 chiffres).
|
||||
Les noms et prénoms sont issus des fichiers noms.txt, prenoms-h.txt, prenoms-f.txt
|
||||
|
@ -19,37 +20,39 @@ from gen_nomprenoms import nomprenom
|
|||
|
||||
|
||||
def usage():
|
||||
print(f"Usage: {sys.argv[0]} dbname formsemestre_id")
|
||||
print(f"Usage: {sys.argv[0]} dbname [formsemestre_id]")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
if len(sys.argv) not in (2, 3):
|
||||
usage()
|
||||
|
||||
dbname = sys.argv[1]
|
||||
formsemestre_id = sys.argv[2]
|
||||
formsemestre_id = sys.argv[2] if len(sys.argv) > 2 else None
|
||||
DBCNXSTRING = f"dbname={dbname}"
|
||||
|
||||
# Liste des etudiants inscrits à ce semestre
|
||||
cnx = psycopg2.connect(DBCNXSTRING)
|
||||
cursor = cnx.cursor()
|
||||
|
||||
cursor.execute(
|
||||
"select count(*) from notes_formsemestre where id=%(formsemestre_id)s",
|
||||
{"formsemestre_id": formsemestre_id},
|
||||
)
|
||||
nsem = cursor.fetchone()[0]
|
||||
if nsem != 1:
|
||||
print(f"{nsem} formsemestre matching {formsemestre_id} in {dbname}")
|
||||
sys.exit(2)
|
||||
|
||||
cursor.execute(
|
||||
"""select i.id
|
||||
from identite i, notes_formsemestre_inscription ins
|
||||
where i.id=ins.etudid and ins.formsemestre_id=%(formsemestre_id)s
|
||||
""",
|
||||
{"formsemestre_id": formsemestre_id},
|
||||
)
|
||||
if formsemestre_id is None:
|
||||
cursor.execute("SELECT i.id from identite i")
|
||||
else:
|
||||
cursor.execute(
|
||||
"select count(*) from notes_formsemestre where id=%(formsemestre_id)s",
|
||||
{"formsemestre_id": formsemestre_id},
|
||||
)
|
||||
nsem = cursor.fetchone()[0]
|
||||
if nsem != 1:
|
||||
print(f"{nsem} formsemestre matching {formsemestre_id} in {dbname}")
|
||||
sys.exit(2)
|
||||
cursor.execute(
|
||||
"""select i.id
|
||||
from identite i, notes_formsemestre_inscription ins
|
||||
where i.id=ins.etudid and ins.formsemestre_id=%(formsemestre_id)s
|
||||
""",
|
||||
{"formsemestre_id": formsemestre_id},
|
||||
)
|
||||
|
||||
wcursor = cnx.cursor()
|
||||
n = 0
|
||||
|
|
Loading…
Reference in New Issue