diff --git a/app/models/events.py b/app/models/events.py
index 9725f3c8a7..b94549e764 100644
--- a/app/models/events.py
+++ b/app/models/events.py
@@ -36,18 +36,21 @@ class Scolog(db.Model):
class ScolarNews(db.Model):
"""Nouvelles pour page d'accueil"""
- NEWS_INSCR = "INSCR" # inscription d'étudiants (object=None ou formsemestre_id)
- NEWS_NOTE = "NOTES" # saisie note (object=moduleimpl_id)
- NEWS_FORM = "FORM" # modification formation (object=formation_id)
- NEWS_SEM = "SEM" # creation semestre (object=None)
NEWS_ABS = "ABS" # saisie absence
+ NEWS_APO = "APO" # changements de codes APO
+ NEWS_FORM = "FORM" # modification formation (object=formation_id)
+ NEWS_INSCR = "INSCR" # inscription d'étudiants (object=None ou formsemestre_id)
NEWS_MISC = "MISC" # unused
+ NEWS_NOTE = "NOTES" # saisie note (object=moduleimpl_id)
+ NEWS_SEM = "SEM" # creation semestre (object=None)
NEWS_MAP = {
- NEWS_INSCR: "inscription d'étudiants",
- NEWS_NOTE: "saisie note",
+ NEWS_ABS: "saisie absence",
+ NEWS_APO: "modif. code Apogée",
NEWS_FORM: "modification formation",
- NEWS_SEM: "création semestre",
+ NEWS_INSCR: "inscription d'étudiants",
NEWS_MISC: "opération", # unused
+ NEWS_NOTE: "saisie note",
+ NEWS_SEM: "création semestre",
}
NEWS_TYPES = list(NEWS_MAP.keys())
diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py
index 0d126fc7fb..fda7238394 100644
--- a/app/models/formsemestre.py
+++ b/app/models/formsemestre.py
@@ -375,7 +375,7 @@ class FormSemestre(db.Model):
return f"{self.titre} {self.formation.get_parcours().SESSION_NAME} {self.semestre_id}"
def sem_modalite(self) -> str:
- """Le semestre et la modialité, ex "S2 FI" ou "S3 APP" """
+ """Le semestre et la modalité, ex "S2 FI" ou "S3 APP" """
if self.semestre_id > 0:
descr_sem = f"S{self.semestre_id}"
else:
diff --git a/app/scodoc/gen_tables.py b/app/scodoc/gen_tables.py
index 01b94c064c..1c708ca5c6 100644
--- a/app/scodoc/gen_tables.py
+++ b/app/scodoc/gen_tables.py
@@ -385,12 +385,16 @@ class GenTable(object):
colspan_count = colspan
else:
colspan_txt = ""
+ attrs = row.get("_%s_td_attrs" % cid, "")
+ order = row.get(f"_{cid}_order")
+ if order:
+ attrs += f' data-order="{order}"'
r.append(
"<%s%s %s%s%s>%s%s>"
% (
elem,
std,
- row.get("_%s_td_attrs" % cid, ""),
+ attrs,
klass,
colspan_txt,
content,
diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py
index c6811d4547..fd25e5da3f 100644
--- a/app/scodoc/sco_edit_ue.py
+++ b/app/scodoc/sco_edit_ue.py
@@ -43,10 +43,8 @@ import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import ModuleType
from app.scodoc.TrivialFormulator import TrivialFormulator
-from app.scodoc.gen_tables import GenTable
from app.scodoc.sco_permissions import Permission
from app.scodoc.sco_exceptions import (
- ScoGenError,
ScoValueError,
ScoLockedFormError,
ScoNonEmptyFormationObject,
@@ -61,7 +59,6 @@ from app.scodoc import sco_edit_module
from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups
from app.scodoc import sco_moduleimpl
-from app.scodoc import sco_preferences
from app.scodoc import sco_tag_module
_ueEditor = ndb.EditableTable(
@@ -1355,93 +1352,6 @@ def ue_is_locked(ue_id):
return len(r) > 0
-# ---- Table recap formation
-def formation_table_recap(formation_id, format="html"):
- """Table recapitulant formation."""
- from app.scodoc import sco_formations
-
- F = sco_formations.formation_list(args={"formation_id": formation_id})
- if not F:
- raise ScoValueError("invalid formation_id")
- F = F[0]
- T = []
- ues = ue_list(args={"formation_id": formation_id})
- for ue in ues:
- Matlist = sco_edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]})
- for Mat in Matlist:
- Modlist = sco_edit_module.module_list(
- args={"matiere_id": Mat["matiere_id"]}
- )
- for Mod in Modlist:
- Mod["nb_moduleimpls"] = sco_edit_module.module_count_moduleimpls(
- Mod["module_id"]
- )
- #
- T.append(
- {
- "UE_acro": ue["acronyme"],
- "Mat_tit": Mat["titre"],
- "Mod_tit": Mod["abbrev"] or Mod["titre"],
- "Mod_code": Mod["code"],
- "Mod_coef": Mod["coefficient"],
- "Mod_sem": Mod["semestre_id"],
- "nb_moduleimpls": Mod["nb_moduleimpls"],
- "heures_cours": Mod["heures_cours"],
- "heures_td": Mod["heures_td"],
- "heures_tp": Mod["heures_tp"],
- "ects": Mod["ects"],
- }
- )
- columns_ids = [
- "UE_acro",
- "Mat_tit",
- "Mod_tit",
- "Mod_code",
- "Mod_coef",
- "Mod_sem",
- "nb_moduleimpls",
- "heures_cours",
- "heures_td",
- "heures_tp",
- "ects",
- ]
- titles = {
- "UE_acro": "UE",
- "Mat_tit": "Matière",
- "Mod_tit": "Module",
- "Mod_code": "Code",
- "Mod_coef": "Coef.",
- "Mod_sem": "Sem.",
- "nb_moduleimpls": "Nb utilisé",
- "heures_cours": "Cours (h)",
- "heures_td": "TD (h)",
- "heures_tp": "TP (h)",
- "ects": "ECTS",
- }
-
- title = (
- """Formation %(titre)s (%(acronyme)s) [version %(version)s] code %(formation_code)s"""
- % F
- )
- tab = GenTable(
- columns_ids=columns_ids,
- rows=T,
- titles=titles,
- origin="Généré par %s le " % scu.sco_version.SCONAME
- + scu.timedate_human_repr()
- + "",
- caption=title,
- html_caption=title,
- html_class="table_leftalign",
- base_url="%s?formation_id=%s" % (request.base_url, formation_id),
- page_title=title,
- html_title="
" + title + "
",
- pdf_title=title,
- preferences=sco_preferences.SemPreferences(),
- )
- return tab.make_page(format=format)
-
-
def ue_list_semestre_ids(ue: dict):
"""Liste triée des numeros de semestres des modules dans cette UE
Il est recommandable que tous les modules d'une UE aient le même indice de semestre.
diff --git a/app/scodoc/sco_formation_recap.py b/app/scodoc/sco_formation_recap.py
new file mode 100644
index 0000000000..e23b043edf
--- /dev/null
+++ b/app/scodoc/sco_formation_recap.py
@@ -0,0 +1,153 @@
+# -*- mode: python -*-
+# -*- coding: utf-8 -*-
+
+##############################################################################
+#
+# Gestion scolarite IUT
+#
+# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# Emmanuel Viennet emmanuel.viennet@viennet.net
+#
+##############################################################################
+
+"""Table recap formation (avec champs éditables)
+"""
+from flask import url_for
+from flask import g, request
+from flask_login import current_user
+
+from app.models import Formation, UniteEns, Module
+from app.models.formations import Matiere
+
+from app.scodoc.gen_tables import GenTable
+from app.scodoc.sco_permissions import Permission
+from app.scodoc import sco_preferences
+import app.scodoc.sco_utils as scu
+
+
+# ---- Table recap formation
+def formation_table_recap(formation_id, format="html"):
+ """Table recapitulant formation."""
+ T = []
+ formation = Formation.query.get_or_404(formation_id)
+ ues = formation.ues.order_by(UniteEns.semestre_idx, UniteEns.numero)
+ can_edit = current_user.has_permission(Permission.ScoChangeFormation)
+ li = 0
+ for ue in ues:
+ # L'UE
+ T.append(
+ {
+ "sem": f"S{ue.semestre_idx}" if ue.semestre_idx is not None else "-",
+ "_sem_order": f"{li:04d}",
+ "code": ue.acronyme,
+ "titre": ue.titre or "",
+ "apo": ue.code_apogee or "",
+ "_apo_td_attrs": f""" data-oid="{ue.id}" data-value="{ue.code_apogee or ''}" """,
+ "coef": ue.coefficient or "",
+ "ects": ue.ects,
+ "_css_row_class": f"ue ue_",
+ }
+ )
+ li += 1
+ matieres = ue.matieres.order_by(Matiere.numero)
+ for mat in matieres:
+ modules = mat.modules.order_by(Module.numero)
+ for mod in modules:
+ nb_moduleimpls = mod.modimpls.count()
+ # le module (ou ressource ou sae)
+ T.append(
+ {
+ "sem": f"S{mod.semestre_id}"
+ if mod.semestre_id is not None
+ else "-",
+ "_sem_order": f"{li:04d}",
+ "code": mod.code,
+ "titre": mod.abbrev or mod.titre,
+ "_titre_target": url_for(
+ "notes.module_edit",
+ scodoc_dept=g.scodoc_dept,
+ module_id=mod.id,
+ )
+ if can_edit
+ else None,
+ "apo": mod.code_apogee,
+ "_apo_td_attrs": f""" data-oid="{mod.id}" data-value="{mod.code_apogee or ''}" """,
+ "coef": mod.coefficient,
+ "nb_moduleimpls": nb_moduleimpls,
+ "heures_cours": mod.heures_cours,
+ "heures_td": mod.heures_td,
+ "heures_tp": mod.heures_tp,
+ "_css_row_class": f"mod {mod.type_abbrv()}",
+ }
+ )
+ columns_ids = [
+ "sem",
+ "code",
+ "apo",
+ # "mat", inutile d'afficher la matière
+ "titre",
+ "coef",
+ "ects",
+ "nb_moduleimpls",
+ "heures_cours",
+ "heures_td",
+ "heures_tp",
+ ]
+ titles = {
+ "ue": "UE",
+ "mat": "Matière",
+ "titre": "Titre",
+ "code": "Code",
+ "apo": "Apo",
+ "coef": "Coef.",
+ "sem": "Sem.",
+ "nb_moduleimpls": "Nb utilisé",
+ "heures_cours": "Cours (h)",
+ "heures_td": "TD (h)",
+ "heures_tp": "TP (h)",
+ "ects": "ECTS",
+ }
+
+ title = f"""Formation {formation.titre} ({formation.acronyme})
+ [version {formation.version}] code {formation.formation_code}"""
+ html_class = "stripe cell-border compact hover order-column formation_table_recap"
+ if current_user.has_permission(Permission.ScoEditApo):
+ html_class += " apo_editable"
+
+ tab = GenTable(
+ columns_ids=columns_ids,
+ rows=T,
+ titles=titles,
+ origin=f"Généré par {scu.sco_version.SCONAME} le {scu.timedate_human_repr()}",
+ caption=title,
+ html_caption=title,
+ html_class=html_class,
+ html_class_ignore_default=True,
+ html_table_attrs=f"""
+ data-apo_ue_save_url="{url_for('notes.ue_set_apo', scodoc_dept=g.scodoc_dept)}"
+ data-apo_mod_save_url="{url_for('notes.module_set_apo', scodoc_dept=g.scodoc_dept)}"
+ """,
+ html_with_td_classes=True,
+ base_url=f"{request.base_url}?formation_id={formation_id}",
+ page_title=title,
+ html_title=f"{title}
",
+ pdf_title=title,
+ preferences=sco_preferences.SemPreferences(),
+ table_id="formation_table_recap",
+ )
+ return tab.make_page(format=format, javascripts=["js/formation_recap.js"])
diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css
index 24548b28c9..6a0d9e73d6 100644
--- a/app/static/css/scodoc.css
+++ b/app/static/css/scodoc.css
@@ -1056,7 +1056,7 @@ span.wtf-field ul.errors li {
}
.configuration_logo entete_dept {
- display: inline-block;
+ display: inline-block;
}
.configuration_logo .effectifs {
@@ -3971,4 +3971,18 @@ table.evaluations_recap td.nb_abs,
table.evaluations_recap td.nb_att,
table.evaluations_recap td.nb_exc {
text-align: center;
+}
+
+/* ------------- Tableau récap formation ------------ */
+table.formation_table_recap tr.ue td {
+ font-weight: bold;
+}
+
+table.formation_table_recap td.coef,
+table.formation_table_recap td.ects,
+table.formation_table_recap td.nb_moduleimpls,
+table.formation_table_recap td.heures_cours,
+table.formation_table_recap td.heures_td,
+table.formation_table_recap td.heures_tp {
+ text-align: right;
}
\ No newline at end of file
diff --git a/app/static/js/formation_recap.js b/app/static/js/formation_recap.js
new file mode 100644
index 0000000000..0bcb009f47
--- /dev/null
+++ b/app/static/js/formation_recap.js
@@ -0,0 +1,28 @@
+/* Page accueil département */
+var apo_ue_editor = null;
+var apo_mod_editor = null;
+
+$(document).ready(function () {
+ var table_options = {
+ "paging": false,
+ "searching": false,
+ "info": false,
+ /* "autoWidth" : false, */
+ "fixedHeader": {
+ "header": true,
+ "footer": true
+ },
+ "orderCellsTop": true, // cellules ligne 1 pour tri
+ "aaSorting": [], // Prevent initial sorting
+ };
+ $('table#formation_table_recap').DataTable(table_options);
+ let table_editable = document.querySelector("table#formation_table_recap.apo_editable");
+ if (table_editable) {
+ let apo_ue_save_url = document.querySelector("table#formation_table_recap.apo_editable").dataset.apo_ue_save_url;
+ apo_ue_editor = new ScoFieldEditor("table#formation_table_recap tr.ue td.apo", apo_ue_save_url, false);
+ let apo_mod_save_url = document.querySelector("table#formation_table_recap.apo_editable").dataset.apo_mod_save_url;
+ apo_mod_editor = new ScoFieldEditor("table#formation_table_recap tr.mod td.apo", apo_mod_save_url, false);
+ }
+});
+
+
diff --git a/app/static/js/scolar_index.js b/app/static/js/scolar_index.js
index 4718e467e7..600a7cf5a4 100644
--- a/app/static/js/scolar_index.js
+++ b/app/static/js/scolar_index.js
@@ -15,8 +15,11 @@ $(document).ready(function () {
"aaSorting": [], // Prevent initial sorting
};
$('table.semlist').DataTable(table_options);
- let apo_save_url = document.querySelector("table#semlist.apo_editable").dataset.apo_save_url;
- apo_editor = new ScoFieldEditor(".etapes_apo_str", apo_save_url, false);
+ let table_editable = document.querySelector("table#semlist.apo_editable");
+ if (table_editable) {
+ let apo_save_url = document.querySelector("table#semlist.apo_editable").dataset.apo_save_url;
+ apo_editor = new ScoFieldEditor(".etapes_apo_str", apo_save_url, false);
+ }
});
diff --git a/app/views/notes.py b/app/views/notes.py
index ba0ca78e55..a4d8774192 100644
--- a/app/views/notes.py
+++ b/app/views/notes.py
@@ -45,11 +45,13 @@ from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models.formsemestre import FormSemestre
from app.models.formsemestre import FormSemestreUEComputationExpr
+from app.models.modules import Module
from app.models.ues import UniteEns
from app import api
from app import db
from app import models
+from app.models import ScolarNews
from app.auth.models import User
from app.but import bulletin_but
from app.decorators import (
@@ -86,7 +88,6 @@ from app.scodoc import sco_archives
from app.scodoc import sco_bulletins
from app.scodoc import sco_bulletins_pdf
from app.scodoc import sco_cache
-from app.scodoc import sco_compute_moy
from app.scodoc import sco_cost_formation
from app.scodoc import sco_debouche
from app.scodoc import sco_edit_apc
@@ -103,6 +104,7 @@ from app.scodoc import sco_evaluation_edit
from app.scodoc import sco_evaluation_recap
from app.scodoc import sco_export_results
from app.scodoc import sco_formations
+from app.scodoc import sco_formation_recap
from app.scodoc import sco_formsemestre
from app.scodoc import sco_formsemestre_custommenu
from app.scodoc import sco_formsemestre_edit
@@ -480,7 +482,9 @@ sco_publish(
methods=["GET", "POST"],
)
sco_publish(
- "/formation_table_recap", sco_edit_ue.formation_table_recap, Permission.ScoView
+ "/formation_table_recap",
+ sco_formation_recap.formation_table_recap,
+ Permission.ScoView,
)
sco_publish(
"/formation_add_malus_modules",
@@ -2433,7 +2437,52 @@ def formsemestre_set_apo_etapes():
formsemestre.etapes.append(etape)
db.session.add(formsemestre)
db.session.commit()
+ ScolarNews.add(
+ typ=ScolarNews.NEWS_APO,
+ text=f"Modification code Apogée du semestre {formsemestre.titre_annee()})",
+ )
+ return ("", 204)
+
+@bp.route("/ue_set_apo", methods=["POST"])
+@scodoc
+@permission_required(Permission.ScoEditApo)
+def ue_set_apo():
+ """Change le code APO de l'UE
+ Args: oid=ue_id, value=chaine "VRTU12" (1 seul code / UE)
+ """
+ ue_id = int(request.form.get("oid"))
+ code_apo = (request.form.get("value") or "").strip()
+ ue = UniteEns.query.get_or_404(ue_id)
+ if code_apo != ue.code_apogee:
+ ue.code_apogee = code_apo
+ db.session.add(ue)
+ db.session.commit()
+ ScolarNews.add(
+ typ=ScolarNews.NEWS_FORM,
+ text=f"Modification code Apogée d'UE dans la formation {ue.formation.titre} ({ue.formation.acronyme})",
+ )
+ return ("", 204)
+
+
+@bp.route("/module_set_apo", methods=["POST"])
+@scodoc
+@permission_required(Permission.ScoEditApo)
+def module_set_apo():
+ """Change le code APO du module
+ Args: oid=ue_id, value=chaine "VRTU12" (1 seul code / UE)
+ """
+ oid = int(request.form.get("oid"))
+ code_apo = (request.form.get("value") or "").strip()
+ mod = Module.query.get_or_404(oid)
+ if code_apo != mod.code_apogee:
+ mod.code_apogee = code_apo
+ db.session.add(mod)
+ db.session.commit()
+ ScolarNews.add(
+ typ=ScolarNews.NEWS_FORM,
+ text=f"Modification code Apogée d'UE dans la formation {mod.formation.titre} ({mod.formation.acronyme})",
+ )
return ("", 204)
diff --git a/sco_version.py b/sco_version.py
index 9136c351bf..a8d3aae110 100644
--- a/sco_version.py
+++ b/sco_version.py
@@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
-SCOVERSION = "9.2.5"
+SCOVERSION = "9.2.6"
SCONAME = "ScoDoc"