# -*- 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"
{formsemestre.id} | " 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("""Sessions en cours
""")
H.append(_sem_table(_convert_formsemestres_to_dicts(formsemestres, showcodes)))
else:
# aucun semestre courant: affiche aide
H.append(
"""
Aucune session en cours !
Pour ajouter une session, aller dans Formations,
choisissez une formation, puis suivez le lien "UE, modules, semestres".
Là, en bas de page, suivez le lien
"Mettre en place un nouveau semestre de formation..."
"""
)
return "\n".join(H)
def _sem_table(sems: list[dict]) -> str:
"""Affiche liste des semestres, utilisée pour semestres en cours"""
tmpl = f"""%(tmpcode)s
%(lockimg)s %(groupicon)s |
%(mois_debut)s - %(mois_fin)s |
%(titre_num)s
(%(responsable_name)s)
|
"""
# Liste des semestres, groupés par modalités
sems_by_mod, modalites = sco_modalites.group_sems_by_modalite(sems)
H = ['']
for modalite in modalites:
if len(modalites) > 1:
H.append('%s |
' % 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("
")
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"""{sem['anneescolaire_str']}""")
if is_h
else sem["anneescolaire_str"]
)
sem["_dash_mois_fin_class"] = "datesem"
sem["titre_resp"] = (
(
f"""{sem['titre_num']}
({sem['responsable_name']})"""
)
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 ""