This commit is contained in:
Emmanuel Viennet 2022-04-21 16:40:04 +02:00
commit eb5b8d69da
12 changed files with 173 additions and 55 deletions

View File

@ -302,22 +302,46 @@ class Identite(db.Model):
else: else:
date_ins = events[0].event_date date_ins = events[0].event_date
situation += date_ins.strftime(" le %d/%m/%Y") situation += date_ins.strftime(" le %d/%m/%Y")
elif inscr.etat == scu.DEF:
situation = f"défaillant en {inscr.formsemestre.titre_mois()}"
event = (
models.ScolarEvent.query.filter_by(
etudid=self.id,
formsemestre_id=inscr.formsemestre.id,
event_type="DEFAILLANCE",
)
.order_by(models.ScolarEvent.event_date)
.first()
)
if not event:
log(
f"*** situation inconsistante pour {self} (def mais pas d'event)"
)
situation += "???" # ???
else:
date_def = event.event_date
situation += date_def.strftime(" le %d/%m/%Y")
else: else:
situation = f"démission de {inscr.formsemestre.titre_mois()}" situation = f"démission de {inscr.formsemestre.titre_mois()}"
# Cherche la date de demission dans scolar_events: # Cherche la date de demission dans scolar_events:
events = models.ScolarEvent.query.filter_by( event = (
etudid=self.id, models.ScolarEvent.query.filter_by(
formsemestre_id=inscr.formsemestre.id, etudid=self.id,
event_type="DEMISSION", formsemestre_id=inscr.formsemestre.id,
).all() event_type="DEMISSION",
if not events: )
.order_by(models.ScolarEvent.event_date)
.first()
)
if not event:
log( log(
f"*** situation inconsistante pour {self} (demission mais pas d'event)" f"*** situation inconsistante pour {self} (demission mais pas d'event)"
) )
date_dem = "???" # ??? situation += "???" # ???
else: else:
date_dem = events[0].event_date date_dem = event.event_date
situation += date_dem.strftime(" le %d/%m/%Y") situation += date_dem.strftime(" le %d/%m/%Y")
else: else:
situation = "non inscrit" + self.e situation = "non inscrit" + self.e

View File

@ -237,6 +237,8 @@ def _sem_table_gt(sems, showcodes=False):
"titre_resp", "titre_resp",
"nb_inscrits", "nb_inscrits",
"etapes_apo_str", "etapes_apo_str",
"elt_annee_apo",
"elt_sem_apo",
) )
if showcodes: if showcodes:
columns_ids = ("formsemestre_id",) + columns_ids columns_ids = ("formsemestre_id",) + columns_ids
@ -253,6 +255,9 @@ def _sem_table_gt(sems, showcodes=False):
"dash_mois_fin": "Année", "dash_mois_fin": "Année",
"titre_resp": "Semestre", "titre_resp": "Semestre",
"nb_inscrits": "N", "nb_inscrits": "N",
"etapes_apo_str": "Étape Apo.",
"elt_annee_apo": "Elt. année Apo.",
"elt_sem_apo": "Elt. sem. Apo.",
}, },
columns_ids=columns_ids, columns_ids=columns_ids,
rows=sems, rows=sems,
@ -260,7 +265,11 @@ def _sem_table_gt(sems, showcodes=False):
html_class_ignore_default=True, html_class_ignore_default=True,
html_class=html_class, html_class=html_class,
html_sortable=True, html_sortable=True,
html_table_attrs=f"""data-apo_save_url="{url_for('notes.formsemestre_set_apo_etapes', scodoc_dept=g.scodoc_dept)}" """, html_table_attrs=f"""
data-apo_save_url="{url_for('notes.formsemestre_set_apo_etapes', scodoc_dept=g.scodoc_dept)}"
data-elt_annee_apo_save_url="{url_for('notes.formsemestre_set_elt_annee_apo', scodoc_dept=g.scodoc_dept)}"
data-elt_sem_apo_save_url="{url_for('notes.formsemestre_set_elt_sem_apo', scodoc_dept=g.scodoc_dept)}"
""",
html_with_td_classes=True, html_with_td_classes=True,
preferences=sco_preferences.SemPreferences(), preferences=sco_preferences.SemPreferences(),
) )
@ -298,6 +307,12 @@ def _style_sems(sems):
sem[ sem[
"_etapes_apo_str_td_attrs" "_etapes_apo_str_td_attrs"
] = f""" data-oid="{sem['formsemestre_id']}" data-value="{sem['etapes_apo_str']}" """ ] = f""" data-oid="{sem['formsemestre_id']}" data-value="{sem['etapes_apo_str']}" """
sem[
"_elt_annee_apo_td_attrs"
] = f""" data-oid="{sem['formsemestre_id']}" data-value="{sem['elt_annee_apo']}" """
sem[
"_elt_sem_apo_td_attrs"
] = f""" data-oid="{sem['formsemestre_id']}" data-value="{sem['elt_sem_apo']}" """
def delete_dept(dept_id: int): def delete_dept(dept_id: int):

View File

@ -30,6 +30,7 @@
import io import io
from zipfile import ZipFile, BadZipfile from zipfile import ZipFile, BadZipfile
from flask import Response
from flask import send_file, url_for from flask import send_file, url_for
from flask import g, request from flask import g, request
from flask_login import current_user from flask_login import current_user
@ -44,7 +45,7 @@ import app.scodoc.sco_utils as scu
# ---- Table recap formation # ---- Table recap formation
def formation_table_recap(formation_id, format="html"): def formation_table_recap(formation_id, format="html") -> Response:
"""Table recapitulant formation.""" """Table recapitulant formation."""
T = [] T = []
formation = Formation.query.get_or_404(formation_id) formation = Formation.query.get_or_404(formation_id)
@ -70,7 +71,7 @@ def formation_table_recap(formation_id, format="html"):
"_apo_td_attrs": f""" data-oid="{ue.id}" data-value="{ue.code_apogee or ''}" """, "_apo_td_attrs": f""" data-oid="{ue.id}" data-value="{ue.code_apogee or ''}" """,
"coef": ue.coefficient or "", "coef": ue.coefficient or "",
"ects": ue.ects, "ects": ue.ects,
"_css_row_class": f"ue ue_", "_css_row_class": "ue",
} }
) )
li += 1 li += 1

View File

@ -95,9 +95,12 @@ _formsemestreEditor = ndb.EditableTable(
def get_formsemestre(formsemestre_id, raise_soft_exc=False): def get_formsemestre(formsemestre_id, raise_soft_exc=False):
"list ONE formsemestre" "list ONE formsemestre"
if formsemestre_id is None:
raise ValueError(f"get_formsemestre: id manquant")
if formsemestre_id in g.stored_get_formsemestre: if formsemestre_id in g.stored_get_formsemestre:
return g.stored_get_formsemestre[formsemestre_id] return g.stored_get_formsemestre[formsemestre_id]
if not isinstance(formsemestre_id, int): if not isinstance(formsemestre_id, int):
log(f"get_formsemestre: invalid id '{formsemestre_id}'")
raise ScoInvalidIdType("formsemestre_id must be an integer !") raise ScoInvalidIdType("formsemestre_id must be an integer !")
sems = do_formsemestre_list(args={"formsemestre_id": formsemestre_id}) sems = do_formsemestre_list(args={"formsemestre_id": formsemestre_id})
if not sems: if not sems:

View File

@ -966,6 +966,7 @@ Il y a des notes en attente ! Le classement des étudiants n'a qu'une valeur ind
def formsemestre_status(formsemestre_id=None): def formsemestre_status(formsemestre_id=None):
"""Tableau de bord semestre HTML""" """Tableau de bord semestre HTML"""
# porté du DTML # porté du DTML
sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True) sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True)
modimpls = sco_moduleimpl.moduleimpl_withmodule_list( modimpls = sco_moduleimpl.moduleimpl_withmodule_list(
formsemestre_id=formsemestre_id formsemestre_id=formsemestre_id
@ -987,7 +988,9 @@ def formsemestre_status(formsemestre_id=None):
use_ue_coefs = sco_preferences.get_preference("use_ue_coefs", formsemestre_id) use_ue_coefs = sco_preferences.get_preference("use_ue_coefs", formsemestre_id)
H = [ H = [
html_sco_header.sco_header(page_title="Semestre %s" % sem["titreannee"]), html_sco_header.sco_header(
page_title=f"{formsemestre.sem_modalite()} {formsemestre.titre_annee()}"
),
'<div class="formsemestre_status">', '<div class="formsemestre_status">',
formsemestre_status_head( formsemestre_status_head(
formsemestre_id=formsemestre_id, page_title="Tableau de bord" formsemestre_id=formsemestre_id, page_title="Tableau de bord"

View File

@ -103,7 +103,7 @@ def formsemestre_recapcomplet(
return data return data
H = [ H = [
html_sco_header.sco_header( html_sco_header.sco_header(
page_title="Récapitulatif", page_title=f"{formsemestre.sem_modalite()}: moyennes",
no_side_bar=True, no_side_bar=True,
init_qtip=True, init_qtip=True,
javascripts=["js/etud_info.js", "js/table_recap.js"], javascripts=["js/etud_info.js", "js/table_recap.js"],

View File

@ -704,6 +704,7 @@ def do_import_etuds_from_portal(sem, a_importer, etudsapo_ident):
typ=ScolarNews.NEWS_INSCR, typ=ScolarNews.NEWS_INSCR,
text="Import Apogée de %d étudiants en " % len(created_etudids), text="Import Apogée de %d étudiants en " % len(created_etudids),
obj=sem["formsemestre_id"], obj=sem["formsemestre_id"],
max_frequency=10 * 60, # 10'
) )

View File

@ -1,5 +1,7 @@
/* Page accueil département */ /* Page accueil département */
var apo_editor = null; var apo_editor = null;
var elt_annee_apo_editor = null;
var elt_sem_apo_editor = null;
$(document).ready(function () { $(document).ready(function () {
var table_options = { var table_options = {
@ -17,8 +19,14 @@ $(document).ready(function () {
$('table.semlist').DataTable(table_options); $('table.semlist').DataTable(table_options);
let table_editable = document.querySelector("table#semlist.apo_editable"); let table_editable = document.querySelector("table#semlist.apo_editable");
if (table_editable) { if (table_editable) {
let apo_save_url = document.querySelector("table#semlist.apo_editable").dataset.apo_save_url; let save_url = document.querySelector("table#semlist.apo_editable").dataset.apo_save_url;
apo_editor = new ScoFieldEditor(".etapes_apo_str", apo_save_url, false); apo_editor = new ScoFieldEditor(".etapes_apo_str", save_url, false);
save_url = document.querySelector("table#semlist.apo_editable").dataset.elt_annee_apo_save_url;
elt_annee_apo_editor = new ScoFieldEditor(".elt_annee_apo", save_url, false);
save_url = document.querySelector("table#semlist.apo_editable").dataset.elt_sem_apo_save_url;
elt_sem_apo_editor = new ScoFieldEditor(".elt_sem_apo", save_url, false);
} }
}); });

View File

@ -1,6 +1,5 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends 'base.html' %} {% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}

View File

@ -290,10 +290,9 @@ def formsemestre_bulletinetud(
code_ine=None, code_ine=None,
): ):
format = format or "html" format = format or "html"
if not formsemestre_id:
flask.abort(404, "argument manquant: formsemestre_id")
if not isinstance(formsemestre_id, int): if not isinstance(formsemestre_id, int):
raise ScoInvalidIdType("formsemestre_id must be an integer !") raise ValueError("formsemestre_id must be an integer !")
formsemestre = FormSemestre.query.get_or_404(formsemestre_id) formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
if etudid: if etudid:
etud = models.Identite.query.get_or_404(etudid) etud = models.Identite.query.get_or_404(etudid)
@ -481,11 +480,16 @@ sco_publish(
Permission.ScoView, Permission.ScoView,
methods=["GET", "POST"], methods=["GET", "POST"],
) )
sco_publish(
"/formation_table_recap",
sco_formation_recap.formation_table_recap, @bp.route("/formation_table_recap")
Permission.ScoView, @scodoc
) @permission_required(Permission.ScoView)
@scodoc7func
def formation_table_recap(formation_id, format="html"):
return sco_formation_recap.formation_table_recap(formation_id, format="html")
sco_publish( sco_publish(
"/export_recap_formations_annee_scolaire", "/export_recap_formations_annee_scolaire",
sco_formation_recap.export_recap_formations_annee_scolaire, sco_formation_recap.export_recap_formations_annee_scolaire,
@ -2459,6 +2463,51 @@ def formsemestre_set_apo_etapes():
ScolarNews.add( ScolarNews.add(
typ=ScolarNews.NEWS_APO, typ=ScolarNews.NEWS_APO,
text=f"Modification code Apogée du semestre {formsemestre.titre_annee()})", text=f"Modification code Apogée du semestre {formsemestre.titre_annee()})",
max_frequency=10 * 60,
)
return ("", 204)
@bp.route("/formsemestre_set_elt_annee_apo", methods=["POST"])
@scodoc
@permission_required(Permission.ScoEditApo)
def formsemestre_set_elt_annee_apo():
"""Change les codes étapes du semestre indiqué.
Args: oid=formsemestre_id, value=chaine "V3ONM, V3ONM1, V3ONM2", codes séparés par des virgules
"""
oid = int(request.form.get("oid"))
value = (request.form.get("value") or "").strip()
formsemestre: FormSemestre = FormSemestre.query.get_or_404(oid)
if value != formsemestre.elt_annee_apo:
formsemestre.elt_annee_apo = value
db.session.add(formsemestre)
db.session.commit()
ScolarNews.add(
typ=ScolarNews.NEWS_APO,
text=f"Modification code Apogée du semestre {formsemestre.titre_annee()})",
max_frequency=10 * 60,
)
return ("", 204)
@bp.route("/formsemestre_set_elt_sem_apo", methods=["POST"])
@scodoc
@permission_required(Permission.ScoEditApo)
def formsemestre_set_elt_sem_apo():
"""Change les codes étapes du semestre indiqué.
Args: oid=formsemestre_id, value=chaine "V3ONM, V3ONM1, V3ONM2", codes séparés par des virgules
"""
oid = int(request.form.get("oid"))
value = (request.form.get("value") or "").strip()
formsemestre: FormSemestre = FormSemestre.query.get_or_404(oid)
if value != formsemestre.elt_sem_apo:
formsemestre.elt_sem_apo = value
db.session.add(formsemestre)
db.session.commit()
ScolarNews.add(
typ=ScolarNews.NEWS_APO,
text=f"Modification code Apogée du semestre {formsemestre.titre_annee()})",
max_frequency=10 * 60,
) )
return ("", 204) return ("", 204)
@ -2480,6 +2529,7 @@ def ue_set_apo():
ScolarNews.add( ScolarNews.add(
typ=ScolarNews.NEWS_FORM, typ=ScolarNews.NEWS_FORM,
text=f"Modification code Apogée d'UE dans la formation {ue.formation.titre} ({ue.formation.acronyme})", text=f"Modification code Apogée d'UE dans la formation {ue.formation.titre} ({ue.formation.acronyme})",
max_frequency=10 * 60,
) )
return ("", 204) return ("", 204)
@ -2501,6 +2551,7 @@ def module_set_apo():
ScolarNews.add( ScolarNews.add(
typ=ScolarNews.NEWS_FORM, typ=ScolarNews.NEWS_FORM,
text=f"Modification code Apogée d'UE dans la formation {mod.formation.titre} ({mod.formation.acronyme})", text=f"Modification code Apogée d'UE dans la formation {mod.formation.titre} ({mod.formation.acronyme})",
max_frequency=10 * 60,
) )
return ("", 204) return ("", 204)

View File

@ -1,74 +1,87 @@
alembic==1.7.5 alembic==1.7.7
astroid==2.11.2 astroid==2.11.2
async-timeout==4.0.2
attrs==21.4.0 attrs==21.4.0
Babel==2.9.1 Babel==2.9.1
black==22.3.0
blinker==1.4 blinker==1.4
certifi==2021.10.8 certifi==2021.10.8
cffi==1.15.0 cffi==1.15.0
chardet==4.0.0 chardet==4.0.0
charset-normalizer==2.0.9 charset-normalizer==2.0.12
click==8.0.3 click==8.1.2
cracklib==2.9.3 cracklib==2.9.3
cryptography==36.0.1 cryptography==36.0.2
Deprecated==1.2.13 Deprecated==1.2.13
dnspython==2.1.0 dill==0.3.4
dominate==2.6.0 dominate==2.6.0
email-validator==1.1.3 email-validator==1.1.3
et-xmlfile==1.1.0 et-xmlfile==1.1.0
Flask==2.0.2 Flask==2.1.1
Flask-Babel==2.0.0 Flask-Babel==2.0.0
Flask-Bootstrap==3.3.7.1 Flask-Bootstrap==3.3.7.1
Flask-Caching==1.10.1 Flask-Caching==1.10.1
Flask-HTTPAuth==4.5.0 Flask-HTTPAuth==4.5.0
Flask-Login==0.5.0 Flask-Login==0.6.0
Flask-Mail==0.9.1 Flask-Mail==0.9.1
Flask-Migrate==3.1.0 Flask-Migrate==3.1.0
Flask-Moment==1.0.2 Flask-Moment==1.0.2
Flask-SQLAlchemy==2.5.1 Flask-SQLAlchemy==2.5.1
Flask-WTF==1.0.0 Flask-WTF==1.0.1
greenlet==1.1.2 greenlet==1.1.2
gunicorn==20.1.0 gunicorn==20.1.0
icalendar==4.0.9 icalendar==4.0.9
idna==3.3 idna==3.3
importlib-metadata==4.11.3
iniconfig==1.1.1 iniconfig==1.1.1
isort==5.10.1 isort==5.10.1
itsdangerous==2.0.1 itsdangerous==2.1.2
Jinja2==3.0.3 Jinja2==3.1.1
lazy-object-proxy==1.7.1 lazy-object-proxy==1.7.1
lxml==4.8.0 lxml==4.8.0
Mako==1.1.6 Mako==1.2.0
MarkupSafe==2.0.1 MarkupSafe==2.1.1
mccabe==0.6.1 mccabe==0.7.0
numpy==1.22.0 mypy==0.942
mypy-extensions==0.4.3
numpy==1.22.3
openpyxl==3.0.9 openpyxl==3.0.9
packaging==21.3 packaging==21.3
pandas==1.3.5 pandas==1.4.2
Pillow==8.4.0 pathspec==0.9.0
Pillow==9.1.0
pkg_resources==0.0.0
platformdirs==2.5.1
pluggy==1.0.0 pluggy==1.0.0
psycopg2==2.9.3 psycopg2==2.9.3
py==1.11.0 py==1.11.0
pycparser==2.21 pycparser==2.21
pydot==1.4.2 pydot==1.4.2
PyJWT==2.3.0 PyJWT==2.3.0
pyOpenSSL==21.0.0 pylint==2.13.5
pyparsing==3.0.6 pylint-flask==0.6
pytest==6.2.5 pylint-flask-sqlalchemy==0.2.0
pylint-plugin-utils==0.7
pyOpenSSL==22.0.0
pyparsing==3.0.8
pytest==7.1.1
python-dateutil==2.8.2 python-dateutil==2.8.2
python-docx==0.8.11 python-docx==0.8.11
python-dotenv==0.19.2 python-dotenv==0.20.0
python-editor==1.0.4 python-editor==1.0.4
pytz==2021.3 pytz==2022.1
redis==4.1.0 redis==4.2.2
reportlab==3.6.5 reportlab==3.6.9
requests==2.26.0 requests==2.27.1
rq==1.10.1 rq==1.10.1
six==1.16.0 six==1.16.0
SQLAlchemy==1.4.29 SQLAlchemy==1.4.35
toml==0.10.2 toml==0.10.2
tornado==6.1 tornado==6.1
typing-extensions==4.0.1 typing_extensions==4.1.1
urllib3==1.26.7 urllib3==1.26.9
visitor==0.1.3 visitor==0.1.3
Werkzeug==2.0.2 Werkzeug==2.1.1
wrapt==1.13.3 wrapt==1.14.0
WTForms==3.0.1 WTForms==3.0.1
zipp==3.8.0

View File

@ -1,7 +1,7 @@
# -*- mode: python -*- # -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
SCOVERSION = "9.2.6" SCOVERSION = "9.2.7"
SCONAME = "ScoDoc" SCONAME = "ScoDoc"