forked from ScoDoc/ScoDoc
435 lines
17 KiB
Python
435 lines
17 KiB
Python
# -*- mode: python -*-
|
|
# -*- coding: utf-8 -*-
|
|
|
|
##############################################################################
|
|
#
|
|
# ScoDoc
|
|
#
|
|
# Copyright (c) 1999 - 2024 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
|
|
#
|
|
##############################################################################
|
|
|
|
"""Page accueil département (liste des semestres, etc)
|
|
"""
|
|
|
|
from sqlalchemy import desc
|
|
from flask import g, url_for, render_template
|
|
from flask_login import current_user
|
|
from flask_sqlalchemy.query import Query
|
|
|
|
import app
|
|
from app import log
|
|
from app.models import FormSemestre, ScolarNews
|
|
import app.scodoc.sco_utils as scu
|
|
from app.scodoc.gen_tables import GenTable
|
|
from app.scodoc.sco_permissions import Permission
|
|
import app.scodoc.notesdb as ndb
|
|
from app.scodoc import sco_modalites
|
|
from app.scodoc import sco_preferences
|
|
from app.scodoc import sco_users
|
|
from app.views import ScoData
|
|
|
|
|
|
def index_html(showcodes=0, showsemtable=0, export_table_formsemestres=False):
|
|
"Page accueil département (liste des semestres)"
|
|
showcodes = int(showcodes)
|
|
showsemtable = int(showsemtable) or export_table_formsemestres
|
|
|
|
# Liste tous les formsemestres du dept, le plus récent d'abord
|
|
current_formsemestres = (
|
|
FormSemestre.query.filter_by(dept_id=g.scodoc_dept_id, etat=True)
|
|
.filter(FormSemestre.modalite != "EXT")
|
|
.order_by(desc(FormSemestre.date_debut))
|
|
)
|
|
locked_formsemestres = (
|
|
FormSemestre.query.filter_by(dept_id=g.scodoc_dept_id, etat=False)
|
|
.filter(FormSemestre.modalite != "EXT")
|
|
.order_by(desc(FormSemestre.date_debut))
|
|
)
|
|
formsemestres = (
|
|
FormSemestre.query.filter_by(dept_id=g.scodoc_dept_id)
|
|
.filter(FormSemestre.modalite != "EXT")
|
|
.order_by(desc(FormSemestre.date_debut))
|
|
)
|
|
if showsemtable: # table de tous les formsemestres
|
|
table = _sem_table_gt(
|
|
formsemestres,
|
|
showcodes=showcodes,
|
|
fmt="xlsx" if export_table_formsemestres else "html",
|
|
)
|
|
if export_table_formsemestres:
|
|
return table # cas spécial: on renvoie juste cette table
|
|
html_table_formsemestres = table.html()
|
|
else:
|
|
html_table_formsemestres = None
|
|
|
|
return render_template(
|
|
"scolar/index.j2",
|
|
current_user=current_user,
|
|
dept_name=sco_preferences.get_preference("DeptName"),
|
|
formsemestres=formsemestres,
|
|
html_current_formsemestres=_show_current_formsemestres(
|
|
current_formsemestres, showcodes
|
|
),
|
|
html_table_formsemestres=html_table_formsemestres,
|
|
locked_formsemestres=locked_formsemestres,
|
|
nb_locked=locked_formsemestres.count(),
|
|
nb_user_accounts=sco_users.get_users_count(dept=g.scodoc_dept),
|
|
page_title=f"ScoDoc {g.scodoc_dept}",
|
|
Permission=Permission,
|
|
scolar_news_summary=ScolarNews.scolar_news_summary_html(),
|
|
showsemtable=showsemtable,
|
|
sco=ScoData(),
|
|
)
|
|
|
|
|
|
def _convert_formsemestres_to_dicts(
|
|
formsemestres: Query, showcodes: bool, fmt: str = "html"
|
|
) -> list[dict]:
|
|
""" """
|
|
if fmt == "html":
|
|
# icon images:
|
|
groupicon = scu.icontag("groupicon_img", title="Inscrits", border="0")
|
|
emptygroupicon = scu.icontag(
|
|
"emptygroupicon_img", title="Pas d'inscrits", border="0"
|
|
)
|
|
lockicon = scu.icontag("lock32_img", title="verrouillé", border="0")
|
|
else:
|
|
groupicon = "X"
|
|
emptygroupicon = ""
|
|
lockicon = "X"
|
|
# génère liste de dict
|
|
sems = []
|
|
for formsemestre in formsemestres:
|
|
nb_inscrits = len(formsemestre.inscriptions)
|
|
formation = formsemestre.formation
|
|
sem = {
|
|
"anneescolaire": formsemestre.annee_scolaire(),
|
|
"anneescolaire_str": formsemestre.annee_scolaire_str(),
|
|
"bul_hide_xml": formsemestre.bul_hide_xml,
|
|
"dateord": formsemestre.date_debut,
|
|
"elt_annee_apo": formsemestre.elt_annee_apo,
|
|
"elt_sem_apo": formsemestre.elt_sem_apo,
|
|
"etapes_apo_str": formsemestre.etapes_apo_str(),
|
|
"formation": f"{formation.acronyme} v{formation.version}",
|
|
"_formation_target": url_for(
|
|
"notes.ue_table",
|
|
scodoc_dept=g.scodoc_dept,
|
|
formation_id=formation.id,
|
|
semestre_idx=formsemestre.semestre_id,
|
|
),
|
|
"formsemestre_id": formsemestre.id,
|
|
"groupicon": groupicon if nb_inscrits > 0 else emptygroupicon,
|
|
"lockimg": lockicon,
|
|
"modalite": formsemestre.modalite,
|
|
"mois_debut": formsemestre.mois_debut(),
|
|
"mois_fin": formsemestre.mois_fin(),
|
|
"nb_inscrits": nb_inscrits,
|
|
"responsable_name": formsemestre.responsables_str(),
|
|
"semestre_id": formsemestre.semestre_id,
|
|
"session_id": formsemestre.session_id(),
|
|
"titre_num": formsemestre.titre_num(),
|
|
"tmpcode": (f"<td><tt>{formsemestre.id}</tt></td>" if showcodes else ""),
|
|
}
|
|
sems.append(sem)
|
|
return sems
|
|
|
|
|
|
def _show_current_formsemestres(formsemestres: Query, showcodes: bool) -> str:
|
|
"""html div avec les formsemestres courants de la page d'accueil"""
|
|
|
|
H = []
|
|
if formsemestres.count():
|
|
H.append("""<div class="scobox-title">Sessions en cours</div>""")
|
|
H.append(_sem_table(_convert_formsemestres_to_dicts(formsemestres, showcodes)))
|
|
else:
|
|
# aucun semestre courant: affiche aide
|
|
H.append(
|
|
"""
|
|
<div class="scobox-title">Aucune session en cours !</div>
|
|
<p>Pour ajouter une session, aller dans <a href="Notes" id="link-programmes">Formations</a>,
|
|
choisissez une formation, puis suivez le lien "<em>UE, modules, semestres</em>".
|
|
</p>
|
|
<p>Là, en bas de page, suivez le lien
|
|
"<em>Mettre en place un nouveau semestre de formation...</em>"
|
|
</p>"""
|
|
)
|
|
return "\n".join(H)
|
|
|
|
|
|
def _sem_table(sems: list[dict]) -> str:
|
|
"""Affiche liste des semestres, utilisée pour semestres en cours"""
|
|
tmpl = f"""<tr class="%(trclass)s">%(tmpcode)s
|
|
<td class="semicon">%(lockimg)s <a href="{
|
|
url_for("notes.formsemestre_status", scodoc_dept=g.scodoc_dept)}?formsemestre_id=%(formsemestre_id)s#groupes">%(groupicon)s</a></td>
|
|
<td class="datesem">%(mois_debut)s <a title="%(session_id)s">-</a> %(mois_fin)s</td>
|
|
<td class="titresem"><a class="stdlink" href="{url_for("notes.formsemestre_status", scodoc_dept=g.scodoc_dept)}?formsemestre_id=%(formsemestre_id)s">%(titre_num)s</a>
|
|
<span class="respsem">(%(responsable_name)s)</span>
|
|
</td>
|
|
</tr>
|
|
"""
|
|
|
|
# Liste des semestres, groupés par modalités
|
|
sems_by_mod, modalites = sco_modalites.group_sems_by_modalite(sems)
|
|
|
|
H = ['<table class="listesems">']
|
|
for modalite in modalites:
|
|
if len(modalites) > 1:
|
|
H.append('<tr><th colspan="3">%s</th></tr>' % modalite["titre"])
|
|
|
|
if sems_by_mod[modalite["modalite"]]:
|
|
cur_idx = sems_by_mod[modalite["modalite"]][0]["semestre_id"]
|
|
for sem in sems_by_mod[modalite["modalite"]]:
|
|
if cur_idx != sem["semestre_id"]:
|
|
sem["trclass"] = "firstsem" # separe les groupes de semestres
|
|
cur_idx = sem["semestre_id"]
|
|
else:
|
|
sem["trclass"] = ""
|
|
H.append(tmpl % sem)
|
|
H.append("</table>")
|
|
return "\n".join(H)
|
|
|
|
|
|
def _sem_table_gt(formsemestres: Query, showcodes=False, fmt="html") -> GenTable:
|
|
"""Table des semestres
|
|
Utilise une datatables.
|
|
"""
|
|
sems = _style_sems(
|
|
_convert_formsemestres_to_dicts(formsemestres, showcodes, fmt=fmt), fmt=fmt
|
|
)
|
|
sems.sort(
|
|
key=lambda s: (
|
|
-s["anneescolaire"],
|
|
s["semestre_id"] if s["semestre_id"] > 0 else -s["semestre_id"] * 1000,
|
|
s["modalite"],
|
|
)
|
|
)
|
|
columns_ids = (
|
|
"lockimg",
|
|
"published",
|
|
"dash_mois_fin",
|
|
"semestre_id_n",
|
|
"modalite",
|
|
"titre_resp",
|
|
"nb_inscrits",
|
|
"formation",
|
|
"etapes_apo_str",
|
|
"elt_annee_apo",
|
|
"elt_sem_apo",
|
|
)
|
|
if showcodes:
|
|
columns_ids = ("formsemestre_id",) + columns_ids
|
|
|
|
html_class = "stripe cell-border compact hover order-column table_leftalign semlist"
|
|
if current_user.has_permission(Permission.EditApogee):
|
|
html_class += " apo_editable"
|
|
tab = GenTable(
|
|
titles={
|
|
"formsemestre_id": "id",
|
|
"semestre_id_n": "S#",
|
|
"modalite": "" if fmt == "html" else "Modalité",
|
|
"mois_debut": "Début",
|
|
"dash_mois_fin": "Année",
|
|
"titre_resp": "Semestre",
|
|
"nb_inscrits": "N",
|
|
"etapes_apo_str": "Étape Apo.",
|
|
"elt_annee_apo": "Elt. année Apo.",
|
|
"elt_sem_apo": "Elt. sem. Apo.",
|
|
"formation": "Formation",
|
|
},
|
|
columns_ids=columns_ids,
|
|
rows=sems,
|
|
table_id="semlist",
|
|
html_class_ignore_default=True,
|
|
html_class=html_class,
|
|
html_sortable=True,
|
|
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,
|
|
preferences=sco_preferences.SemPreferences(),
|
|
)
|
|
|
|
return tab
|
|
|
|
|
|
def _style_sems(sems: list[dict], fmt="html") -> list[dict]:
|
|
"""ajoute quelques attributs de présentation pour la table"""
|
|
is_h = fmt == "html"
|
|
if is_h:
|
|
icon_published = scu.ICON_PUBLISHED
|
|
icon_hidden = scu.ICON_HIDDEN
|
|
else:
|
|
icon_published = "publié"
|
|
icon_hidden = "non publié"
|
|
for sem in sems:
|
|
status_url = url_for(
|
|
"notes.formsemestre_status",
|
|
scodoc_dept=g.scodoc_dept,
|
|
formsemestre_id=sem["formsemestre_id"],
|
|
)
|
|
sem["_groupicon_target"] = status_url
|
|
sem["_formsemestre_id_class"] = "blacktt"
|
|
sem["dash_mois_fin"] = (
|
|
(f"""<a title="{sem['session_id']}">{sem['anneescolaire_str']}</a>""")
|
|
if is_h
|
|
else sem["anneescolaire_str"]
|
|
)
|
|
sem["_dash_mois_fin_class"] = "datesem"
|
|
sem["titre_resp"] = (
|
|
(
|
|
f"""<a class="stdlink" href="{status_url}">{sem['titre_num']}</a>
|
|
<span class="respsem">({sem['responsable_name']})</span>"""
|
|
)
|
|
if is_h
|
|
else f"""{sem['titre_num']} ({sem["responsable_name"]})"""
|
|
)
|
|
sem["published"] = icon_hidden if sem["bul_hide_xml"] else icon_published
|
|
|
|
sem["_css_row_class"] = "css_S%d css_M%s" % (
|
|
sem["semestre_id"],
|
|
sem["modalite"],
|
|
)
|
|
sem["_semestre_id_class"] = "semestre_id"
|
|
sem["_modalite_class"] = "modalite"
|
|
if sem["semestre_id"] == -1:
|
|
sem["semestre_id_n"] = ""
|
|
else:
|
|
sem["semestre_id_n"] = sem["semestre_id"]
|
|
# pour édition codes Apogée:
|
|
sem["_etapes_apo_str_td_attrs"] = (
|
|
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']}" """
|
|
)
|
|
return sems
|
|
|
|
|
|
def delete_dept(dept_id: int) -> str:
|
|
"""Suppression irréversible d'un département et de tous les objets rattachés"""
|
|
assert isinstance(dept_id, int)
|
|
|
|
# Un peu complexe, merci JMP :)
|
|
cnx = ndb.GetDBConnexion()
|
|
cursor = cnx.cursor()
|
|
try:
|
|
# 1- Create temp tables to store ids
|
|
reqs = [
|
|
"create temp table etudids_temp as select id from identite where dept_id = %(dept_id)s",
|
|
"""create temp table formsemestres_temp as select id
|
|
from notes_formsemestre where dept_id = %(dept_id)s""",
|
|
"""create temp table moduleimpls_temp as select id from notes_moduleimpl
|
|
where formsemestre_id in (select id from formsemestres_temp)""",
|
|
"""create temp table formations_temp as
|
|
select id from notes_formations where dept_id = %(dept_id)s""",
|
|
"create temp table tags_temp as select id from notes_tags where dept_id = %(dept_id)s",
|
|
]
|
|
for r in reqs:
|
|
log(f"delete_dept: {r}")
|
|
cursor.execute(r, {"dept_id": dept_id})
|
|
|
|
# 2- Delete student-related informations
|
|
# ordered list of tables
|
|
etud_tables = [
|
|
"notes_notes",
|
|
"group_membership",
|
|
"billet_absence",
|
|
"adresse",
|
|
"absences",
|
|
"notes_notes_log",
|
|
"notes_moduleimpl_inscription",
|
|
"itemsuivi",
|
|
"notes_appreciations",
|
|
"scolar_autorisation_inscription",
|
|
"absences_notifications",
|
|
"notes_formsemestre_inscription",
|
|
"scolar_formsemestre_validation",
|
|
"scolar_events",
|
|
]
|
|
for table in etud_tables:
|
|
log(f"delete from {table}")
|
|
cursor.execute(
|
|
f"delete from {table} where etudid in (select id from etudids_temp)"
|
|
)
|
|
|
|
reqs = [
|
|
"""delete from apc_validation_annee where referentiel_competence_id
|
|
in (select id from apc_referentiel_competences where dept_id = %(dept_id)s)""",
|
|
"delete from apc_referentiel_competences where dept_id = %(dept_id)s",
|
|
"delete from sco_prefs where dept_id = %(dept_id)s",
|
|
"""delete from notes_semset_formsemestre
|
|
where formsemestre_id in (select id from formsemestres_temp)""",
|
|
"""delete from notes_evaluation
|
|
where moduleimpl_id in (select id from moduleimpls_temp)""",
|
|
"""delete from notes_modules_enseignants
|
|
where moduleimpl_id in (select id from moduleimpls_temp)""",
|
|
"""delete from notes_formsemestre_uecoef
|
|
where formsemestre_id in (select id from formsemestres_temp)""",
|
|
"""delete from notes_formsemestre_ue_computation_expr
|
|
where formsemestre_id in (select id from formsemestres_temp)""",
|
|
"""delete from notes_formsemestre_responsables
|
|
where formsemestre_id in (select id from formsemestres_temp)""",
|
|
"""delete from notes_moduleimpl
|
|
where formsemestre_id in (select id from formsemestres_temp)""",
|
|
"""delete from notes_modules_tags
|
|
where tag_id in (select id from tags_temp)""",
|
|
"delete from notes_tags where dept_id = %(dept_id)s",
|
|
"delete from notes_modules where formation_id in (select id from formations_temp)",
|
|
"""delete from notes_matieres
|
|
where ue_id in (select id from notes_ue
|
|
where formation_id in (select id from formations_temp))""",
|
|
"""delete from notes_formsemestre_etapes
|
|
where formsemestre_id in (select id from formsemestres_temp)""",
|
|
"""delete from group_descr where partition_id in
|
|
(select id from partition
|
|
where formsemestre_id in (select id from formsemestres_temp))""",
|
|
"delete from partition where formsemestre_id in (select id from formsemestres_temp)",
|
|
"""delete from notes_formsemestre_custommenu
|
|
where formsemestre_id in (select id from formsemestres_temp)""",
|
|
"delete from notes_ue where formation_id in (select id from formations_temp)",
|
|
"delete from notes_formsemestre where dept_id = %(dept_id)s",
|
|
"delete from scolar_news where dept_id = %(dept_id)s",
|
|
"delete from notes_semset where dept_id = %(dept_id)s",
|
|
"delete from notes_formations where dept_id = %(dept_id)s",
|
|
"delete from itemsuivi_tags where dept_id = %(dept_id)s",
|
|
"delete from identite where dept_id = %(dept_id)s",
|
|
"delete from departement where id = %(dept_id)s",
|
|
"drop table tags_temp",
|
|
"drop table formations_temp",
|
|
"drop table moduleimpls_temp",
|
|
"drop table etudids_temp",
|
|
"drop table formsemestres_temp",
|
|
]
|
|
for r in reqs:
|
|
log(f"delete_dept: {r}")
|
|
cursor.execute(r, {"dept_id": dept_id})
|
|
except Exception as e:
|
|
cnx.rollback()
|
|
return str(e)
|
|
finally:
|
|
cnx.commit()
|
|
app.clear_scodoc_cache()
|
|
return ""
|