Modernisation code démission/défaillance...

This commit is contained in:
Emmanuel Viennet 2022-09-30 22:43:39 +02:00 committed by iziram
parent 9d29af3ac7
commit a81b86efff
22 changed files with 22234 additions and 22210 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,163 +1,171 @@
# -*- coding: UTF-8 -*
"""Notes, décisions de jury, évènements scolaires
"""
from app import db
from app.models import SHORT_STR_LEN
from app.models import CODE_STR_LEN
from app.models.events import Scolog
class ScolarFormSemestreValidation(db.Model):
"""Décisions de jury"""
__tablename__ = "scolar_formsemestre_validation"
# Assure unicité de la décision:
__table_args__ = (db.UniqueConstraint("etudid", "formsemestre_id", "ue_id"),)
id = db.Column(db.Integer, primary_key=True)
formsemestre_validation_id = db.synonym("id")
etudid = db.Column(
db.Integer,
db.ForeignKey("identite.id", ondelete="CASCADE"),
index=True,
)
formsemestre_id = db.Column(
db.Integer,
db.ForeignKey("notes_formsemestre.id"),
index=True,
)
ue_id = db.Column(
db.Integer,
db.ForeignKey("notes_ue.id"),
index=True,
)
code = db.Column(db.String(CODE_STR_LEN), nullable=False, index=True)
# NULL pour les UE, True|False pour les semestres:
assidu = db.Column(db.Boolean)
event_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
# NULL sauf si compense un semestre: (pas utilisé pour BUT)
compense_formsemestre_id = db.Column(
db.Integer,
db.ForeignKey("notes_formsemestre.id"),
)
moy_ue = db.Column(db.Float)
# (normalement NULL) indice du semestre, utile seulement pour
# UE "antérieures" et si la formation définit des UE utilisées
# dans plusieurs semestres (cas R&T IUTV v2)
semestre_id = db.Column(db.Integer)
# Si UE validée dans le cursus d'un autre etablissement
is_external = db.Column(
db.Boolean, default=False, server_default="false", index=True
)
ue = db.relationship("UniteEns", lazy="select", uselist=False)
def __repr__(self):
return f"{self.__class__.__name__}({self.formsemestre_id}, {self.etudid}, code={self.code}, ue={self.ue}, moy_ue={self.moy_ue})"
def to_dict(self) -> dict:
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
return d
class ScolarAutorisationInscription(db.Model):
"""Autorisation d'inscription dans un semestre"""
__tablename__ = "scolar_autorisation_inscription"
id = db.Column(db.Integer, primary_key=True)
autorisation_inscription_id = db.synonym("id")
etudid = db.Column(
db.Integer,
db.ForeignKey("identite.id", ondelete="CASCADE"),
)
formation_code = db.Column(db.String(SHORT_STR_LEN), nullable=False)
# Indice du semestre où on peut s'inscrire:
semestre_id = db.Column(db.Integer)
date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
origin_formsemestre_id = db.Column(
db.Integer,
db.ForeignKey("notes_formsemestre.id"),
)
def to_dict(self) -> dict:
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
return d
@classmethod
def autorise_etud(
cls,
etudid: int,
formation_code: str,
origin_formsemestre_id: int,
semestre_id: int,
):
"""Enregistre une autorisation, remplace celle émanant du même semestre si elle existe."""
cls.delete_autorisation_etud(etudid, origin_formsemestre_id)
autorisation = cls(
etudid=etudid,
formation_code=formation_code,
origin_formsemestre_id=origin_formsemestre_id,
semestre_id=semestre_id,
)
db.session.add(autorisation)
Scolog.logdb("autorise_etud", etudid=etudid, msg=f"passage vers S{semestre_id}")
@classmethod
def delete_autorisation_etud(
cls,
etudid: int,
origin_formsemestre_id: int,
):
"""Efface les autorisations de cette étudiant venant du sem. origine"""
autorisations = cls.query.filter_by(
etudid=etudid, origin_formsemestre_id=origin_formsemestre_id
)
for autorisation in autorisations:
db.session.delete(autorisation)
Scolog.logdb(
"autorise_etud",
etudid=etudid,
msg=f"annule passage vers S{autorisation.semestre_id}",
)
db.session.flush()
class ScolarEvent(db.Model):
"""Evenement dans le parcours scolaire d'un étudiant"""
__tablename__ = "scolar_events"
id = db.Column(db.Integer, primary_key=True)
event_id = db.synonym("id")
etudid = db.Column(
db.Integer,
db.ForeignKey("identite.id", ondelete="CASCADE"),
)
event_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
formsemestre_id = db.Column(
db.Integer,
db.ForeignKey("notes_formsemestre.id"),
)
ue_id = db.Column(
db.Integer,
db.ForeignKey("notes_ue.id"),
)
# 'CREATION', 'INSCRIPTION', 'DEMISSION',
# 'AUT_RED', 'EXCLUS', 'VALID_UE', 'VALID_SEM'
# 'ECHEC_SEM'
# 'UTIL_COMPENSATION'
event_type = db.Column(db.String(SHORT_STR_LEN))
# Semestre compensé par formsemestre_id:
comp_formsemestre_id = db.Column(
db.Integer,
db.ForeignKey("notes_formsemestre.id"),
)
def to_dict(self) -> dict:
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
return d
# -*- coding: UTF-8 -*
"""Notes, décisions de jury, évènements scolaires
"""
from app import db
from app.models import SHORT_STR_LEN
from app.models import CODE_STR_LEN
from app.models.events import Scolog
class ScolarFormSemestreValidation(db.Model):
"""Décisions de jury"""
__tablename__ = "scolar_formsemestre_validation"
# Assure unicité de la décision:
__table_args__ = (db.UniqueConstraint("etudid", "formsemestre_id", "ue_id"),)
id = db.Column(db.Integer, primary_key=True)
formsemestre_validation_id = db.synonym("id")
etudid = db.Column(
db.Integer,
db.ForeignKey("identite.id", ondelete="CASCADE"),
index=True,
)
formsemestre_id = db.Column(
db.Integer,
db.ForeignKey("notes_formsemestre.id"),
index=True,
)
ue_id = db.Column(
db.Integer,
db.ForeignKey("notes_ue.id"),
index=True,
)
code = db.Column(db.String(CODE_STR_LEN), nullable=False, index=True)
# NULL pour les UE, True|False pour les semestres:
assidu = db.Column(db.Boolean)
event_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
# NULL sauf si compense un semestre: (pas utilisé pour BUT)
compense_formsemestre_id = db.Column(
db.Integer,
db.ForeignKey("notes_formsemestre.id"),
)
moy_ue = db.Column(db.Float)
# (normalement NULL) indice du semestre, utile seulement pour
# UE "antérieures" et si la formation définit des UE utilisées
# dans plusieurs semestres (cas R&T IUTV v2)
semestre_id = db.Column(db.Integer)
# Si UE validée dans le cursus d'un autre etablissement
is_external = db.Column(
db.Boolean, default=False, server_default="false", index=True
)
ue = db.relationship("UniteEns", lazy="select", uselist=False)
def __repr__(self):
return f"{self.__class__.__name__}({self.formsemestre_id}, {self.etudid}, code={self.code}, ue={self.ue}, moy_ue={self.moy_ue})"
def to_dict(self) -> dict:
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
return d
class ScolarAutorisationInscription(db.Model):
"""Autorisation d'inscription dans un semestre"""
__tablename__ = "scolar_autorisation_inscription"
id = db.Column(db.Integer, primary_key=True)
autorisation_inscription_id = db.synonym("id")
etudid = db.Column(
db.Integer,
db.ForeignKey("identite.id", ondelete="CASCADE"),
)
formation_code = db.Column(db.String(SHORT_STR_LEN), nullable=False)
# Indice du semestre où on peut s'inscrire:
semestre_id = db.Column(db.Integer)
date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
origin_formsemestre_id = db.Column(
db.Integer,
db.ForeignKey("notes_formsemestre.id"),
)
def to_dict(self) -> dict:
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
return d
@classmethod
def autorise_etud(
cls,
etudid: int,
formation_code: str,
origin_formsemestre_id: int,
semestre_id: int,
):
"""Enregistre une autorisation, remplace celle émanant du même semestre si elle existe."""
cls.delete_autorisation_etud(etudid, origin_formsemestre_id)
autorisation = cls(
etudid=etudid,
formation_code=formation_code,
origin_formsemestre_id=origin_formsemestre_id,
semestre_id=semestre_id,
)
db.session.add(autorisation)
Scolog.logdb("autorise_etud", etudid=etudid, msg=f"passage vers S{semestre_id}")
@classmethod
def delete_autorisation_etud(
cls,
etudid: int,
origin_formsemestre_id: int,
):
"""Efface les autorisations de cette étudiant venant du sem. origine"""
autorisations = cls.query.filter_by(
etudid=etudid, origin_formsemestre_id=origin_formsemestre_id
)
for autorisation in autorisations:
db.session.delete(autorisation)
Scolog.logdb(
"autorise_etud",
etudid=etudid,
msg=f"annule passage vers S{autorisation.semestre_id}",
)
db.session.flush()
class ScolarEvent(db.Model):
"""Evenement dans le parcours scolaire d'un étudiant"""
__tablename__ = "scolar_events"
id = db.Column(db.Integer, primary_key=True)
event_id = db.synonym("id")
etudid = db.Column(
db.Integer,
db.ForeignKey("identite.id", ondelete="CASCADE"),
)
event_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
formsemestre_id = db.Column(
db.Integer,
db.ForeignKey("notes_formsemestre.id"),
)
ue_id = db.Column(
db.Integer,
db.ForeignKey("notes_ue.id"),
)
# 'CREATION', 'INSCRIPTION', 'DEMISSION',
# 'AUT_RED', 'EXCLUS', 'VALID_UE', 'VALID_SEM'
# 'ECHEC_SEM'
# 'UTIL_COMPENSATION'
event_type = db.Column(db.String(SHORT_STR_LEN))
# Semestre compensé par formsemestre_id:
comp_formsemestre_id = db.Column(
db.Integer,
db.ForeignKey("notes_formsemestre.id"),
)
etud = db.relationship("Identite", lazy="select", backref="events", uselist=False)
formsemestre = db.relationship(
"FormSemestre", lazy="select", uselist=False, foreign_keys=[formsemestre_id]
)
def to_dict(self) -> dict:
"as a dict"
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
return d
def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.event_type}, {self.event_date.isoformat()}, {self.formsemestre})"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -117,7 +117,7 @@ def formsemestre_ext_create_form(etudid, formsemestre_id):
# Les autres situations (eg redoublements en changeant d'établissement)
# doivent être gérées par les validations de semestres "antérieurs"
insem = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
args={"etudid": etudid, "etat": "I"}
args={"etudid": etudid, "etat": scu.INSCRIT}
)
semlist = [sco_formsemestre.get_formsemestre(i["formsemestre_id"]) for i in insem]
existing_semestre_ids = {s["semestre_id"] for s in semlist}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -308,9 +308,9 @@ def get_group_infos(group_id, etat=None): # was _getlisteetud
# add human readable description of state:
nbdem = 0
for t in members:
if t["etat"] == "I":
if t["etat"] == scu.INSCRIT:
t["etath"] = "" # etudiant inscrit, ne l'indique pas dans la liste HTML
elif t["etat"] == "D":
elif t["etat"] == scu.DEMISSION:
events = sco_etud.scolar_events_list(
cnx,
args={

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,235 +1,235 @@
# -*- 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@gmail.com
#
##############################################################################
"""Extraction de données pour poursuites d'études
Recapitule tous les semestres validés dans une feuille excel.
"""
import collections
from flask import url_for, g, request
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models import FormSemestre
import app.scodoc.sco_utils as scu
from app.scodoc import sco_abs
from app.scodoc import sco_cache
from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups
from app.scodoc import sco_preferences
from app.scodoc import sco_etud
import sco_version
from app.scodoc.gen_tables import GenTable
from app.scodoc.sco_codes_parcours import code_semestre_validant, code_semestre_attente
def etud_get_poursuite_info(sem, etud):
"""{ 'nom' : ..., 'semlist' : [ { 'semestre_id': , 'moy' : ... }, {}, ...] }"""
I = {}
I.update(etud) # copie nom, prenom, civilite, ...
# Now add each semester, starting from the first one
semlist = []
current_id = sem["semestre_id"]
for sem_id in range(1, current_id + 1):
sem_descr = None
for s in etud["sems"]:
if s["semestre_id"] == sem_id:
etudid = etud["etudid"]
formsemestre = FormSemestre.query.get_or_404(s["formsemestre_id"])
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
dec = nt.get_etud_decision_sem(etudid)
# Moyennes et rangs des UE
ues = nt.get_ues_stat_dict(filter_sport=True)
moy_ues = []
for ue in ues:
ue_status = nt.get_etud_ue_status(etudid, ue["ue_id"])
if ue_status:
moy_ues.append(
(
ue["acronyme"],
scu.fmt_note(
nt.get_etud_ue_status(etudid, ue["ue_id"])["moy"]
),
)
)
else:
moy_ues.append((ue["acronyme"], ""))
rg_ues = [
("rang_" + ue["acronyme"], nt.ue_rangs[ue["ue_id"]][0][etudid])
for ue in ues
]
# Moyennes et rang des modules
modimpls = nt.get_modimpls_dict() # recupération des modules
modules = []
rangs = []
for ue in ues: # on parcourt chaque UE
for modimpl in modimpls: # dans chaque UE les modules
if modimpl["module"]["ue_id"] == ue["ue_id"]:
codeModule = modimpl["module"]["code"] or ""
noteModule = scu.fmt_note(
nt.get_etud_mod_moy(modimpl["moduleimpl_id"], etudid)
)
if noteModule != "NI": # si étudiant inscrit au module
if nt.mod_rangs is not None:
rangModule = nt.mod_rangs[modimpl["moduleimpl_id"]][
0
][etudid]
else:
rangModule = ""
modules.append([codeModule, noteModule])
rangs.append(["rang_" + codeModule, rangModule])
# Absences
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, nt.sem)
if (
dec
and not sem_descr # not sem_descr pour ne prendre que le semestre validé le plus récent
and (
code_semestre_validant(dec["code"])
or code_semestre_attente(dec["code"])
)
and nt.get_etud_etat(etudid) == "I"
):
d = [
("moy", scu.fmt_note(nt.get_etud_moy_gen(etudid))),
("moy_promo", scu.fmt_note(nt.moy_moy)),
("rang", nt.get_etud_rang(etudid)),
("effectif", len(nt.T)),
("date_debut", s["date_debut"]),
("date_fin", s["date_fin"]),
("periode", "%s - %s" % (s["mois_debut"], s["mois_fin"])),
("AbsNonJust", nbabs - nbabsjust),
("AbsJust", nbabsjust),
]
d += (
moy_ues + rg_ues + modules + rangs
) # ajout des 2 champs notes des modules et classement dans chaque module
sem_descr = collections.OrderedDict(d)
if not sem_descr:
sem_descr = collections.OrderedDict(
[
("moy", ""),
("moy_promo", ""),
("rang", ""),
("effectif", ""),
("date_debut", ""),
("date_fin", ""),
("periode", ""),
]
)
sem_descr["semestre_id"] = sem_id
semlist.append(sem_descr)
I["semlist"] = semlist
return I
def _flatten_info(info):
# met la liste des infos semestres "a plat"
# S1_moy, S1_rang, ..., S2_moy, ...
ids = []
for s in info["semlist"]:
for k, v in s.items():
if k != "semestre_id":
label = "S%s_%s" % (s["semestre_id"], k)
info[label] = v
ids.append(label)
return ids
def _getEtudInfoGroupes(group_ids, etat=None):
"""liste triée d'infos (dict) sur les etudiants du groupe indiqué.
Attention: lent, car plusieurs requetes SQL par etudiant !
"""
etuds = []
for group_id in group_ids:
members = sco_groups.get_group_members(group_id, etat=etat)
for m in members:
etud = sco_etud.get_etud_info(etudid=m["etudid"], filled=True)[0]
etuds.append(etud)
return etuds
def formsemestre_poursuite_report(formsemestre_id, format="html"):
"""Table avec informations "poursuite" """
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
etuds = _getEtudInfoGroupes([sco_groups.get_default_group(formsemestre_id)])
infos = []
ids = []
for etud in etuds:
fiche_url = url_for(
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud["etudid"]
)
etud["_nom_target"] = fiche_url
etud["_prenom_target"] = fiche_url
etud["_nom_td_attrs"] = 'id="%s" class="etudinfo"' % (etud["etudid"])
info = etud_get_poursuite_info(sem, etud)
idd = _flatten_info(info)
# On recupere la totalite des UEs dans ids
for id in idd:
if id not in ids:
ids += [id]
infos.append(info)
#
column_ids = (
("civilite_str", "nom", "prenom", "annee", "date_naissance")
+ tuple(ids)
+ ("debouche",)
)
titles = {}
for c in column_ids:
titles[c] = c
tab = GenTable(
titles=titles,
columns_ids=column_ids,
rows=infos,
# html_col_width='4em',
html_sortable=True,
html_class="table_leftalign table_listegroupe",
pdf_link=False, # pas d'export pdf
preferences=sco_preferences.SemPreferences(formsemestre_id),
)
tab.filename = scu.make_filename("poursuite " + sem["titreannee"])
tab.origin = (
"Généré par %s le " % sco_version.SCONAME + scu.timedate_human_repr() + ""
)
tab.caption = "Récapitulatif %s." % sem["titreannee"]
tab.html_caption = "Récapitulatif %s." % sem["titreannee"]
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
return tab.make_page(
title="""<h2 class="formsemestre">Poursuite d'études</h2>""",
init_qtip=True,
javascripts=["js/etud_info.js"],
format=format,
with_html_headers=True,
)
# -*- 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@gmail.com
#
##############################################################################
"""Extraction de données pour poursuites d'études
Recapitule tous les semestres validés dans une feuille excel.
"""
import collections
from flask import url_for, g, request
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models import FormSemestre
import app.scodoc.sco_utils as scu
from app.scodoc import sco_abs
from app.scodoc import sco_cache
from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups
from app.scodoc import sco_preferences
from app.scodoc import sco_etud
import sco_version
from app.scodoc.gen_tables import GenTable
from app.scodoc.sco_codes_parcours import code_semestre_validant, code_semestre_attente
def etud_get_poursuite_info(sem, etud):
"""{ 'nom' : ..., 'semlist' : [ { 'semestre_id': , 'moy' : ... }, {}, ...] }"""
I = {}
I.update(etud) # copie nom, prenom, civilite, ...
# Now add each semester, starting from the first one
semlist = []
current_id = sem["semestre_id"]
for sem_id in range(1, current_id + 1):
sem_descr = None
for s in etud["sems"]:
if s["semestre_id"] == sem_id:
etudid = etud["etudid"]
formsemestre = FormSemestre.query.get_or_404(s["formsemestre_id"])
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
dec = nt.get_etud_decision_sem(etudid)
# Moyennes et rangs des UE
ues = nt.get_ues_stat_dict(filter_sport=True)
moy_ues = []
for ue in ues:
ue_status = nt.get_etud_ue_status(etudid, ue["ue_id"])
if ue_status:
moy_ues.append(
(
ue["acronyme"],
scu.fmt_note(
nt.get_etud_ue_status(etudid, ue["ue_id"])["moy"]
),
)
)
else:
moy_ues.append((ue["acronyme"], ""))
rg_ues = [
("rang_" + ue["acronyme"], nt.ue_rangs[ue["ue_id"]][0][etudid])
for ue in ues
]
# Moyennes et rang des modules
modimpls = nt.get_modimpls_dict() # recupération des modules
modules = []
rangs = []
for ue in ues: # on parcourt chaque UE
for modimpl in modimpls: # dans chaque UE les modules
if modimpl["module"]["ue_id"] == ue["ue_id"]:
codeModule = modimpl["module"]["code"] or ""
noteModule = scu.fmt_note(
nt.get_etud_mod_moy(modimpl["moduleimpl_id"], etudid)
)
if noteModule != "NI": # si étudiant inscrit au module
if nt.mod_rangs is not None:
rangModule = nt.mod_rangs[modimpl["moduleimpl_id"]][
0
][etudid]
else:
rangModule = ""
modules.append([codeModule, noteModule])
rangs.append(["rang_" + codeModule, rangModule])
# Absences
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, nt.sem)
if (
dec
and not sem_descr # not sem_descr pour ne prendre que le semestre validé le plus récent
and (
code_semestre_validant(dec["code"])
or code_semestre_attente(dec["code"])
)
and nt.get_etud_etat(etudid) == scu.INSCRIT
):
d = [
("moy", scu.fmt_note(nt.get_etud_moy_gen(etudid))),
("moy_promo", scu.fmt_note(nt.moy_moy)),
("rang", nt.get_etud_rang(etudid)),
("effectif", len(nt.T)),
("date_debut", s["date_debut"]),
("date_fin", s["date_fin"]),
("periode", "%s - %s" % (s["mois_debut"], s["mois_fin"])),
("AbsNonJust", nbabs - nbabsjust),
("AbsJust", nbabsjust),
]
d += (
moy_ues + rg_ues + modules + rangs
) # ajout des 2 champs notes des modules et classement dans chaque module
sem_descr = collections.OrderedDict(d)
if not sem_descr:
sem_descr = collections.OrderedDict(
[
("moy", ""),
("moy_promo", ""),
("rang", ""),
("effectif", ""),
("date_debut", ""),
("date_fin", ""),
("periode", ""),
]
)
sem_descr["semestre_id"] = sem_id
semlist.append(sem_descr)
I["semlist"] = semlist
return I
def _flatten_info(info):
# met la liste des infos semestres "a plat"
# S1_moy, S1_rang, ..., S2_moy, ...
ids = []
for s in info["semlist"]:
for k, v in s.items():
if k != "semestre_id":
label = "S%s_%s" % (s["semestre_id"], k)
info[label] = v
ids.append(label)
return ids
def _getEtudInfoGroupes(group_ids, etat=None):
"""liste triée d'infos (dict) sur les etudiants du groupe indiqué.
Attention: lent, car plusieurs requetes SQL par etudiant !
"""
etuds = []
for group_id in group_ids:
members = sco_groups.get_group_members(group_id, etat=etat)
for m in members:
etud = sco_etud.get_etud_info(etudid=m["etudid"], filled=True)[0]
etuds.append(etud)
return etuds
def formsemestre_poursuite_report(formsemestre_id, format="html"):
"""Table avec informations "poursuite" """
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
etuds = _getEtudInfoGroupes([sco_groups.get_default_group(formsemestre_id)])
infos = []
ids = []
for etud in etuds:
fiche_url = url_for(
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud["etudid"]
)
etud["_nom_target"] = fiche_url
etud["_prenom_target"] = fiche_url
etud["_nom_td_attrs"] = 'id="%s" class="etudinfo"' % (etud["etudid"])
info = etud_get_poursuite_info(sem, etud)
idd = _flatten_info(info)
# On recupere la totalite des UEs dans ids
for id in idd:
if id not in ids:
ids += [id]
infos.append(info)
#
column_ids = (
("civilite_str", "nom", "prenom", "annee", "date_naissance")
+ tuple(ids)
+ ("debouche",)
)
titles = {}
for c in column_ids:
titles[c] = c
tab = GenTable(
titles=titles,
columns_ids=column_ids,
rows=infos,
# html_col_width='4em',
html_sortable=True,
html_class="table_leftalign table_listegroupe",
pdf_link=False, # pas d'export pdf
preferences=sco_preferences.SemPreferences(formsemestre_id),
)
tab.filename = scu.make_filename("poursuite " + sem["titreannee"])
tab.origin = (
"Généré par %s le " % sco_version.SCONAME + scu.timedate_human_repr() + ""
)
tab.caption = "Récapitulatif %s." % sem["titreannee"]
tab.html_caption = "Récapitulatif %s." % sem["titreannee"]
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
return tab.make_page(
title="""<h2 class="formsemestre">Poursuite d'études</h2>""",
init_qtip=True,
javascripts=["js/etud_info.js"],
format=format,
with_html_headers=True,
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,478 +1,480 @@
# -*- 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
#
##############################################################################
"""Photos: trombinoscopes - Version IUT Tours
Code contribué par Jérôme Billoue, IUT de Tours, 2014
Modification Jérome Billoue,Vincent Grimaud, IUT de Tours, 2017
"""
import io
from reportlab.lib import colors
from reportlab.lib.colors import black
from reportlab.lib.pagesizes import A4, A3
from reportlab.lib import styles
from reportlab.lib.pagesizes import landscape
from reportlab.lib.units import cm
from reportlab.platypus import KeepInFrame, Paragraph, Table, TableStyle
from reportlab.platypus.doctemplate import BaseDocTemplate
from app.scodoc import sco_abs
from app.scodoc import sco_etud
from app.scodoc.sco_exceptions import ScoPDFFormatError
from app.scodoc import sco_groups
from app.scodoc import sco_groups_view
from app.scodoc import sco_preferences
from app.scodoc import sco_trombino
import app.scodoc.sco_utils as scu
from app.scodoc.sco_pdf import SU, ScoDocPageTemplate
# Paramétrage de l'aspect graphique:
PHOTOWIDTH = 2.8 * cm
COLWIDTH = 3.4 * cm
N_PER_ROW = 5
def pdf_trombino_tours(
group_ids=(), # liste des groupes à afficher
formsemestre_id=None, # utilisé si pas de groupes selectionné
):
"""Generation du trombinoscope en fichier PDF"""
# Informations sur les groupes à afficher:
groups_infos = sco_groups_view.DisplayedGroupsInfos(
group_ids, formsemestre_id=formsemestre_id
)
DeptName = sco_preferences.get_preference("DeptName")
DeptFullName = sco_preferences.get_preference("DeptFullName")
InstituteName = sco_preferences.get_preference("InstituteName")
# Generate PDF page
StyleSheet = styles.getSampleStyleSheet()
objects = []
T = Table(
[
[Paragraph(SU(InstituteName), StyleSheet["Heading3"])],
[
Paragraph(
SU("Département " + DeptFullName or "(?)"), StyleSheet["Heading3"]
)
],
[
Paragraph(
SU("Date ............ / ............ / ......................"),
StyleSheet["Normal"],
),
Paragraph(
SU(
"Module ......................................................."
),
StyleSheet["Normal"],
),
],
[
Paragraph(
SU("de ............h............ à ............h............ "),
StyleSheet["Normal"],
),
Paragraph(
SU("Enseignant ................................................."),
StyleSheet["Normal"],
),
],
[
Table(
[
[
"Séance notée :",
" ",
"DS ",
" ",
"TP Contrôle ",
" ",
"Autre cas (TD ou TP noté, QCM, etc...)",
]
],
style=TableStyle(
[
("ALIGN", (0, 0), (-1, -1), "LEFT"),
("BOX", (1, 0), (1, 0), 0.75, black),
("BOX", (3, 0), (3, 0), 0.75, black),
("BOX", (5, 0), (5, 0), 0.75, black),
]
),
)
],
],
colWidths=(COLWIDTH * N_PER_ROW) / 2,
style=TableStyle(
[
("ALIGN", (0, 0), (-1, -1), "LEFT"),
("SPAN", (0, 1), (1, 1)),
("SPAN", (0, 4), (1, 4)),
("BOTTOMPADDING", (0, -1), (-1, -1), 10),
("BOX", (0, 0), (-1, -1), 0.75, black),
]
),
)
objects.append(T)
groups = ""
for group_id in groups_infos.group_ids:
if group_id != "None":
members, _, group_tit, sem, _ = sco_groups.get_group_infos(group_id, "I")
groups += " %s" % group_tit
L = []
currow = []
if sem["semestre_id"] != -1:
currow = [
Paragraph(
SU(
"<para align=center>Semestre %s</para>" % sem["semestre_id"]
),
StyleSheet["Normal"],
)
]
currow += [" "] * (N_PER_ROW - len(currow) - 1)
currow += [
Paragraph(
SU("<para align=center>%s</para>" % sem["anneescolaire"]),
StyleSheet["Normal"],
)
]
L.append(currow)
currow = [" "] * N_PER_ROW
L.append(currow)
currow = []
currow.append(
Paragraph(
SU("<para align=center><b>" + group_tit + "</b></para>"),
StyleSheet["Heading3"],
)
)
n = 1
for m in members:
img = sco_trombino._get_etud_platypus_image(m, image_width=PHOTOWIDTH)
etud_main_group = sco_groups.get_etud_main_group(
m["etudid"], sem["formsemestre_id"]
)
if group_id != etud_main_group["group_id"]:
text_group = " (" + etud_main_group["group_name"] + ")"
else:
text_group = ""
elem = Table(
[
[img],
[
Paragraph(
SU(
"<para align=center><font size=8>"
+ sco_etud.format_prenom(m["prenom"])
+ " "
+ sco_etud.format_nom(m["nom"])
+ text_group
+ "</font></para>"
),
StyleSheet["Normal"],
)
],
],
colWidths=[COLWIDTH],
style=TableStyle([("ALIGN", (0, 0), (-1, -1), "CENTER")]),
)
currow.append(elem)
if n == (N_PER_ROW - 1):
L.append(currow)
currow = []
n = (n + 1) % N_PER_ROW
if currow:
currow += [" "] * (N_PER_ROW - len(currow))
L.append(currow)
if not L:
T = Paragraph(SU("Aucune photo à exporter !"), StyleSheet["Normal"])
else:
T = Table(
L,
colWidths=[COLWIDTH] * N_PER_ROW,
style=TableStyle(
[
("LEFTPADDING", (0, 0), (-1, -1), 0),
("RIGHTPADDING", (0, 0), (-1, -1), 0),
("BOTTOMPADDING", (0, 0), (-1, -1), 0),
("TOPPADDING", (0, 1), (-1, -1), 0),
("TOPPADDING", (0, 0), (-1, 0), 10),
("LINEBELOW", (1, 0), (-2, 0), 0.75, black),
("VALIGN", (0, 0), (-1, 1), "MIDDLE"),
("VALIGN", (0, 2), (-1, -1), "TOP"),
("VALIGN", (0, 2), (0, 2), "MIDDLE"),
("SPAN", (0, 0), (0, 1)),
("SPAN", (-1, 0), (-1, 1)),
]
),
)
objects.append(T)
T = Table(
[
[
Paragraph(
SU(
"Nombre d'absents : ................. (Merci d'entourer les absents SVP)"
),
StyleSheet["Normal"],
)
]
],
colWidths=(COLWIDTH * N_PER_ROW),
style=TableStyle(
[
("ALIGN", (0, 0), (-1, -1), "CENTER"),
("BOTTOMPADDING", (0, -1), (-1, -1), 10),
("BOX", (0, 0), (-1, -1), 0.75, black),
]
),
)
objects.append(T)
# Réduit sur une page
objects = [KeepInFrame(0, 0, objects, mode="shrink")]
# Build document
report = io.BytesIO() # in-memory document, no disk file
filename = "trombino-%s-%s.pdf" % (DeptName, groups_infos.groups_filename)
document = BaseDocTemplate(report)
document.addPageTemplates(
ScoDocPageTemplate(
document,
preferences=sco_preferences.SemPreferences(),
)
)
try:
document.build(objects)
except (ValueError, KeyError) as exc:
raise ScoPDFFormatError(str(exc)) from exc
data = report.getvalue()
return scu.sendPDFFile(data, filename)
# Feuille d'absences en pdf avec photos:
def pdf_feuille_releve_absences(
group_ids=(), # liste des groupes à afficher
formsemestre_id=None, # utilisé si pas de groupes selectionné
):
"""Generation de la feuille d'absence en fichier PDF, avec photos"""
NB_CELL_AM = sco_preferences.get_preference("feuille_releve_abs_AM")
NB_CELL_PM = sco_preferences.get_preference("feuille_releve_abs_PM")
col_width = 0.85 * cm
if sco_preferences.get_preference("feuille_releve_abs_samedi"):
days = sco_abs.DAYNAMES[:6] # Lundi, ..., Samedi
else:
days = sco_abs.DAYNAMES[:5] # Lundi, ..., Vendredi
nb_days = len(days)
# Informations sur les groupes à afficher:
groups_infos = sco_groups_view.DisplayedGroupsInfos(
group_ids, formsemestre_id=formsemestre_id
)
DeptName = sco_preferences.get_preference("DeptName")
DeptFullName = sco_preferences.get_preference("DeptFullName")
InstituteName = sco_preferences.get_preference("InstituteName")
# Generate PDF page
StyleSheet = styles.getSampleStyleSheet()
objects = [
Table(
[
[
Paragraph(SU(InstituteName), StyleSheet["Heading3"]),
Paragraph(
SU(
"<para align=right>Semaine ..................................................................</para>"
),
StyleSheet["Normal"],
),
],
[
Paragraph(
SU("Département " + (DeptFullName or "(?)")),
StyleSheet["Heading3"],
),
"",
],
],
style=TableStyle(
[("SPAN", (0, 1), (1, 1)), ("BOTTOMPADDING", (0, -1), (-1, -1), 10)]
),
)
]
currow = [""] * (NB_CELL_AM + 1 + NB_CELL_PM + 1)
elem_day = Table(
[currow],
colWidths=([col_width] * (NB_CELL_AM + 1 + NB_CELL_PM + 1)),
style=TableStyle(
[
("GRID", (0, 0), (NB_CELL_AM - 1, 0), 0.25, black),
(
"GRID",
(NB_CELL_AM + 1, 0),
(NB_CELL_AM + NB_CELL_PM, 0),
0.25,
black,
),
]
),
)
W = []
currow = []
for n in range(nb_days):
currow.append(elem_day)
W.append(currow)
elem_week = Table(
W,
colWidths=([col_width * (NB_CELL_AM + 1 + NB_CELL_PM + 1)] * nb_days),
style=TableStyle(
[
("LEFTPADDING", (0, 0), (-1, -1), 0),
("RIGHTPADDING", (0, 0), (-1, -1), 0),
("BOTTOMPADDING", (0, 0), (-1, -1), 0),
("TOPPADDING", (0, 0), (-1, -1), 0),
]
),
)
currow = []
for n in range(nb_days):
currow += [Paragraph(SU("<b>" + days[n] + "</b>"), StyleSheet["Normal"])]
elem_day_name = Table(
[currow],
colWidths=([col_width * (NB_CELL_AM + 1 + NB_CELL_PM + 1)] * nb_days),
style=TableStyle(
[
("LEFTPADDING", (0, 0), (-1, -1), 0),
("RIGHTPADDING", (0, 0), (-1, -1), 0),
("BOTTOMPADDING", (0, 0), (-1, -1), 0),
("TOPPADDING", (0, 0), (-1, -1), 0),
]
),
)
for group_id in groups_infos.group_ids:
members, _, group_tit, _, _ = sco_groups.get_group_infos(group_id, "I")
L = []
currow = [
Paragraph(SU("<b>Groupe " + group_tit + "</b>"), StyleSheet["Normal"])
]
currow.append(elem_day_name)
L.append(currow)
currow = [Paragraph(SU("Initiales enseignant :"), StyleSheet["Normal"])]
currow.append(elem_week)
L.append(currow)
currow = [Paragraph(SU("Initiales module :"), StyleSheet["Normal"])]
currow.append(elem_week)
L.append(currow)
for m in members:
currow = [
Paragraph(
SU(
sco_etud.format_nom(m["nom"])
+ " "
+ sco_etud.format_prenom(m["prenom"])
),
StyleSheet["Normal"],
)
]
currow.append(elem_week)
L.append(currow)
if not L:
T = Paragraph(SU("Aucun étudiant !"), StyleSheet["Normal"])
else:
T = Table(
L,
colWidths=(
[
5.0 * cm,
(col_width * (NB_CELL_AM + 1 + NB_CELL_PM + 1) * nb_days),
]
),
style=TableStyle(
[
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("LEFTPADDING", (0, 0), (-1, -1), 0),
("RIGHTPADDING", (0, 0), (-1, -1), 0),
("BOTTOMPADDING", (0, 0), (-1, -1), 3),
("TOPPADDING", (0, 0), (-1, -1), 3),
("BOTTOMPADDING", (0, -1), (-1, -1), 10),
(
"ROWBACKGROUNDS",
(0, 2),
(-1, -1),
(colors.white, colors.lightgrey),
),
]
),
)
objects.append(T)
# Réduit sur une page
objects = [KeepInFrame(0, 0, objects, mode="shrink")]
# Build document
report = io.BytesIO() # in-memory document, no disk file
filename = "absences-%s-%s.pdf" % (DeptName, groups_infos.groups_filename)
if sco_preferences.get_preference("feuille_releve_abs_taille") == "A3":
taille = A3
elif sco_preferences.get_preference("feuille_releve_abs_taille") == "A4":
taille = A4
if sco_preferences.get_preference("feuille_releve_abs_format") == "Paysage":
document = BaseDocTemplate(report, pagesize=landscape(taille))
else:
document = BaseDocTemplate(report, pagesize=taille)
document.addPageTemplates(
ScoDocPageTemplate(
document,
preferences=sco_preferences.SemPreferences(),
)
)
document.build(objects)
data = report.getvalue()
return scu.sendPDFFile(data, filename)
# -*- 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
#
##############################################################################
"""Photos: trombinoscopes - Version IUT Tours
Code contribué par Jérôme Billoue, IUT de Tours, 2014
Modification Jérome Billoue,Vincent Grimaud, IUT de Tours, 2017
"""
import io
from reportlab.lib import colors
from reportlab.lib.colors import black
from reportlab.lib.pagesizes import A4, A3
from reportlab.lib import styles
from reportlab.lib.pagesizes import landscape
from reportlab.lib.units import cm
from reportlab.platypus import KeepInFrame, Paragraph, Table, TableStyle
from reportlab.platypus.doctemplate import BaseDocTemplate
from app.scodoc import sco_abs
from app.scodoc import sco_etud
from app.scodoc.sco_exceptions import ScoPDFFormatError
from app.scodoc import sco_groups
from app.scodoc import sco_groups_view
from app.scodoc import sco_preferences
from app.scodoc import sco_trombino
import app.scodoc.sco_utils as scu
from app.scodoc.sco_pdf import SU, ScoDocPageTemplate
# Paramétrage de l'aspect graphique:
PHOTOWIDTH = 2.8 * cm
COLWIDTH = 3.4 * cm
N_PER_ROW = 5
def pdf_trombino_tours(
group_ids=(), # liste des groupes à afficher
formsemestre_id=None, # utilisé si pas de groupes selectionné
):
"""Generation du trombinoscope en fichier PDF"""
# Informations sur les groupes à afficher:
groups_infos = sco_groups_view.DisplayedGroupsInfos(
group_ids, formsemestre_id=formsemestre_id
)
DeptName = sco_preferences.get_preference("DeptName")
DeptFullName = sco_preferences.get_preference("DeptFullName")
InstituteName = sco_preferences.get_preference("InstituteName")
# Generate PDF page
StyleSheet = styles.getSampleStyleSheet()
objects = []
T = Table(
[
[Paragraph(SU(InstituteName), StyleSheet["Heading3"])],
[
Paragraph(
SU("Département " + DeptFullName or "(?)"), StyleSheet["Heading3"]
)
],
[
Paragraph(
SU("Date ............ / ............ / ......................"),
StyleSheet["Normal"],
),
Paragraph(
SU(
"Module ......................................................."
),
StyleSheet["Normal"],
),
],
[
Paragraph(
SU("de ............h............ à ............h............ "),
StyleSheet["Normal"],
),
Paragraph(
SU("Enseignant ................................................."),
StyleSheet["Normal"],
),
],
[
Table(
[
[
"Séance notée :",
" ",
"DS ",
" ",
"TP Contrôle ",
" ",
"Autre cas (TD ou TP noté, QCM, etc...)",
]
],
style=TableStyle(
[
("ALIGN", (0, 0), (-1, -1), "LEFT"),
("BOX", (1, 0), (1, 0), 0.75, black),
("BOX", (3, 0), (3, 0), 0.75, black),
("BOX", (5, 0), (5, 0), 0.75, black),
]
),
)
],
],
colWidths=(COLWIDTH * N_PER_ROW) / 2,
style=TableStyle(
[
("ALIGN", (0, 0), (-1, -1), "LEFT"),
("SPAN", (0, 1), (1, 1)),
("SPAN", (0, 4), (1, 4)),
("BOTTOMPADDING", (0, -1), (-1, -1), 10),
("BOX", (0, 0), (-1, -1), 0.75, black),
]
),
)
objects.append(T)
groups = ""
for group_id in groups_infos.group_ids:
if group_id != "None":
members, _, group_tit, sem, _ = sco_groups.get_group_infos(
group_id, scu.INSCRIT
)
groups += " %s" % group_tit
L = []
currow = []
if sem["semestre_id"] != -1:
currow = [
Paragraph(
SU(
"<para align=center>Semestre %s</para>" % sem["semestre_id"]
),
StyleSheet["Normal"],
)
]
currow += [" "] * (N_PER_ROW - len(currow) - 1)
currow += [
Paragraph(
SU("<para align=center>%s</para>" % sem["anneescolaire"]),
StyleSheet["Normal"],
)
]
L.append(currow)
currow = [" "] * N_PER_ROW
L.append(currow)
currow = []
currow.append(
Paragraph(
SU("<para align=center><b>" + group_tit + "</b></para>"),
StyleSheet["Heading3"],
)
)
n = 1
for m in members:
img = sco_trombino._get_etud_platypus_image(m, image_width=PHOTOWIDTH)
etud_main_group = sco_groups.get_etud_main_group(
m["etudid"], sem["formsemestre_id"]
)
if group_id != etud_main_group["group_id"]:
text_group = " (" + etud_main_group["group_name"] + ")"
else:
text_group = ""
elem = Table(
[
[img],
[
Paragraph(
SU(
"<para align=center><font size=8>"
+ sco_etud.format_prenom(m["prenom"])
+ " "
+ sco_etud.format_nom(m["nom"])
+ text_group
+ "</font></para>"
),
StyleSheet["Normal"],
)
],
],
colWidths=[COLWIDTH],
style=TableStyle([("ALIGN", (0, 0), (-1, -1), "CENTER")]),
)
currow.append(elem)
if n == (N_PER_ROW - 1):
L.append(currow)
currow = []
n = (n + 1) % N_PER_ROW
if currow:
currow += [" "] * (N_PER_ROW - len(currow))
L.append(currow)
if not L:
T = Paragraph(SU("Aucune photo à exporter !"), StyleSheet["Normal"])
else:
T = Table(
L,
colWidths=[COLWIDTH] * N_PER_ROW,
style=TableStyle(
[
("LEFTPADDING", (0, 0), (-1, -1), 0),
("RIGHTPADDING", (0, 0), (-1, -1), 0),
("BOTTOMPADDING", (0, 0), (-1, -1), 0),
("TOPPADDING", (0, 1), (-1, -1), 0),
("TOPPADDING", (0, 0), (-1, 0), 10),
("LINEBELOW", (1, 0), (-2, 0), 0.75, black),
("VALIGN", (0, 0), (-1, 1), "MIDDLE"),
("VALIGN", (0, 2), (-1, -1), "TOP"),
("VALIGN", (0, 2), (0, 2), "MIDDLE"),
("SPAN", (0, 0), (0, 1)),
("SPAN", (-1, 0), (-1, 1)),
]
),
)
objects.append(T)
T = Table(
[
[
Paragraph(
SU(
"Nombre d'absents : ................. (Merci d'entourer les absents SVP)"
),
StyleSheet["Normal"],
)
]
],
colWidths=(COLWIDTH * N_PER_ROW),
style=TableStyle(
[
("ALIGN", (0, 0), (-1, -1), "CENTER"),
("BOTTOMPADDING", (0, -1), (-1, -1), 10),
("BOX", (0, 0), (-1, -1), 0.75, black),
]
),
)
objects.append(T)
# Réduit sur une page
objects = [KeepInFrame(0, 0, objects, mode="shrink")]
# Build document
report = io.BytesIO() # in-memory document, no disk file
filename = "trombino-%s-%s.pdf" % (DeptName, groups_infos.groups_filename)
document = BaseDocTemplate(report)
document.addPageTemplates(
ScoDocPageTemplate(
document,
preferences=sco_preferences.SemPreferences(),
)
)
try:
document.build(objects)
except (ValueError, KeyError) as exc:
raise ScoPDFFormatError(str(exc)) from exc
data = report.getvalue()
return scu.sendPDFFile(data, filename)
# Feuille d'absences en pdf avec photos:
def pdf_feuille_releve_absences(
group_ids=(), # liste des groupes à afficher
formsemestre_id=None, # utilisé si pas de groupes selectionné
):
"""Generation de la feuille d'absence en fichier PDF, avec photos"""
NB_CELL_AM = sco_preferences.get_preference("feuille_releve_abs_AM")
NB_CELL_PM = sco_preferences.get_preference("feuille_releve_abs_PM")
col_width = 0.85 * cm
if sco_preferences.get_preference("feuille_releve_abs_samedi"):
days = sco_abs.DAYNAMES[:6] # Lundi, ..., Samedi
else:
days = sco_abs.DAYNAMES[:5] # Lundi, ..., Vendredi
nb_days = len(days)
# Informations sur les groupes à afficher:
groups_infos = sco_groups_view.DisplayedGroupsInfos(
group_ids, formsemestre_id=formsemestre_id
)
DeptName = sco_preferences.get_preference("DeptName")
DeptFullName = sco_preferences.get_preference("DeptFullName")
InstituteName = sco_preferences.get_preference("InstituteName")
# Generate PDF page
StyleSheet = styles.getSampleStyleSheet()
objects = [
Table(
[
[
Paragraph(SU(InstituteName), StyleSheet["Heading3"]),
Paragraph(
SU(
"<para align=right>Semaine ..................................................................</para>"
),
StyleSheet["Normal"],
),
],
[
Paragraph(
SU("Département " + (DeptFullName or "(?)")),
StyleSheet["Heading3"],
),
"",
],
],
style=TableStyle(
[("SPAN", (0, 1), (1, 1)), ("BOTTOMPADDING", (0, -1), (-1, -1), 10)]
),
)
]
currow = [""] * (NB_CELL_AM + 1 + NB_CELL_PM + 1)
elem_day = Table(
[currow],
colWidths=([col_width] * (NB_CELL_AM + 1 + NB_CELL_PM + 1)),
style=TableStyle(
[
("GRID", (0, 0), (NB_CELL_AM - 1, 0), 0.25, black),
(
"GRID",
(NB_CELL_AM + 1, 0),
(NB_CELL_AM + NB_CELL_PM, 0),
0.25,
black,
),
]
),
)
W = []
currow = []
for n in range(nb_days):
currow.append(elem_day)
W.append(currow)
elem_week = Table(
W,
colWidths=([col_width * (NB_CELL_AM + 1 + NB_CELL_PM + 1)] * nb_days),
style=TableStyle(
[
("LEFTPADDING", (0, 0), (-1, -1), 0),
("RIGHTPADDING", (0, 0), (-1, -1), 0),
("BOTTOMPADDING", (0, 0), (-1, -1), 0),
("TOPPADDING", (0, 0), (-1, -1), 0),
]
),
)
currow = []
for n in range(nb_days):
currow += [Paragraph(SU("<b>" + days[n] + "</b>"), StyleSheet["Normal"])]
elem_day_name = Table(
[currow],
colWidths=([col_width * (NB_CELL_AM + 1 + NB_CELL_PM + 1)] * nb_days),
style=TableStyle(
[
("LEFTPADDING", (0, 0), (-1, -1), 0),
("RIGHTPADDING", (0, 0), (-1, -1), 0),
("BOTTOMPADDING", (0, 0), (-1, -1), 0),
("TOPPADDING", (0, 0), (-1, -1), 0),
]
),
)
for group_id in groups_infos.group_ids:
members, _, group_tit, _, _ = sco_groups.get_group_infos(group_id, scu.INSCRIT)
L = []
currow = [
Paragraph(SU("<b>Groupe " + group_tit + "</b>"), StyleSheet["Normal"])
]
currow.append(elem_day_name)
L.append(currow)
currow = [Paragraph(SU("Initiales enseignant :"), StyleSheet["Normal"])]
currow.append(elem_week)
L.append(currow)
currow = [Paragraph(SU("Initiales module :"), StyleSheet["Normal"])]
currow.append(elem_week)
L.append(currow)
for m in members:
currow = [
Paragraph(
SU(
sco_etud.format_nom(m["nom"])
+ " "
+ sco_etud.format_prenom(m["prenom"])
),
StyleSheet["Normal"],
)
]
currow.append(elem_week)
L.append(currow)
if not L:
T = Paragraph(SU("Aucun étudiant !"), StyleSheet["Normal"])
else:
T = Table(
L,
colWidths=(
[
5.0 * cm,
(col_width * (NB_CELL_AM + 1 + NB_CELL_PM + 1) * nb_days),
]
),
style=TableStyle(
[
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("LEFTPADDING", (0, 0), (-1, -1), 0),
("RIGHTPADDING", (0, 0), (-1, -1), 0),
("BOTTOMPADDING", (0, 0), (-1, -1), 3),
("TOPPADDING", (0, 0), (-1, -1), 3),
("BOTTOMPADDING", (0, -1), (-1, -1), 10),
(
"ROWBACKGROUNDS",
(0, 2),
(-1, -1),
(colors.white, colors.lightgrey),
),
]
),
)
objects.append(T)
# Réduit sur une page
objects = [KeepInFrame(0, 0, objects, mode="shrink")]
# Build document
report = io.BytesIO() # in-memory document, no disk file
filename = "absences-%s-%s.pdf" % (DeptName, groups_infos.groups_filename)
if sco_preferences.get_preference("feuille_releve_abs_taille") == "A3":
taille = A3
elif sco_preferences.get_preference("feuille_releve_abs_taille") == "A4":
taille = A4
if sco_preferences.get_preference("feuille_releve_abs_format") == "Paysage":
document = BaseDocTemplate(report, pagesize=landscape(taille))
else:
document = BaseDocTemplate(report, pagesize=taille)
document.addPageTemplates(
ScoDocPageTemplate(
document,
preferences=sco_preferences.SemPreferences(),
)
)
document.build(objects)
data = report.getvalue()
return scu.sendPDFFile(data, filename)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff