diff --git a/ZNotes.py b/ZNotes.py
index f88a9042e..d2dd87a2f 100644
--- a/ZNotes.py
+++ b/ZNotes.py
@@ -249,6 +249,9 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
security.declareProtected(ScoView, "formsemestre_recapcomplet")
formsemestre_recapcomplet = sco_recapcomplet.formsemestre_recapcomplet
+ security.declareProtected(ScoObservateur, "formsemestres_bulletins")
+ formsemestres_bulletins = sco_recapcomplet.formsemestres_bulletins
+
security.declareProtected(ScoView, "moduleimpl_status")
moduleimpl_status = sco_moduleimpl_status.moduleimpl_status
diff --git a/sco_formsemestre.py b/sco_formsemestre.py
index 140530f81..e7ad4a18c 100644
--- a/sco_formsemestre.py
+++ b/sco_formsemestre.py
@@ -124,8 +124,7 @@ def do_formsemestre_list(context, *a, **kw):
def formsemestre_enrich(context, sem):
- """Ajoute champs souvent utiles: titre + annee et dateord (pour tris)
- """
+ """Ajoute champs souvent utiles: titre + annee et dateord (pour tris)"""
# imports ici pour eviter refs circulaires
import sco_formsemestre_edit
import scolars
@@ -294,8 +293,7 @@ class ApoEtapeVDI:
_ETAPE_VDI_SEP = "!"
def __init__(self, etape_vdi=None, etape="", vdi=""):
- """Build from string representation, e.g. 'V1RT!111'
- """
+ """Build from string representation, e.g. 'V1RT!111'"""
if etape_vdi:
self.etape_vdi = etape_vdi
self.etape, self.vdi = self.split_etape_vdi(etape_vdi)
@@ -371,11 +369,11 @@ def sem_set_responsable_name(context, sem):
def sem_in_semestre_scolaire(context, sem, year=False, saison=0, REQUEST=None):
"""n'utilise que la date de debut, pivot au 1er aout
si annee non specifiée, année scolaire courante
- Patch Jmp: ajout du parametre optionnel saison
+ Patch Jmp: ajout du parametre optionnel saison
1 = sept, 0 = janvier, None = année complète
si saison non spécifiée: année complète
pivot de saison au 1er décembre
- XXX TODO: la période (ici appelée "saison" devrait être éditable
+ XXX TODO: la période (ici appelée "saison" devrait être éditable
manuellement dans le formsemestre_edit afin de couvrir els cas particulier
comme un semestre S2 qui commecerait en décembre... voire novembre.
)
@@ -417,17 +415,20 @@ def sem_une_annee(context, sem):
pivot au 1er août.
"""
if sem["date_debut_iso"] > sem["date_fin_iso"]:
- log('Warning: semestre %(formsemestre_id)s begins after ending !' % sem)
+ log("Warning: semestre %(formsemestre_id)s begins after ending !" % sem)
return False
-
+
debut = int(sem["annee_debut"])
- if sem["mois_debut_ord"] < 8: # considere que debut sur l'anne scolaire precedente
+ if sem["mois_debut_ord"] < 8: # considere que debut sur l'anne scolaire precedente
debut -= 1
fin = int(sem["annee_fin"])
- if sem["mois_fin_ord"] < 9: # 9 (sept) pour autoriser un début en sept et une fin en aout
+ if (
+ sem["mois_fin_ord"] < 9
+ ): # 9 (sept) pour autoriser un début en sept et une fin en aout
fin -= 1
return debut == fin
+
def scodoc_get_all_unlocked_sems(context):
"""Liste de tous les semestres non verrouillés de tous les départements"""
depts = context.list_depts()
@@ -450,8 +451,7 @@ def table_formsemestres(
html_next_section="",
REQUEST=None,
):
- """Une table presentant des semestres
- """
+ """Une table presentant des semestres"""
for sem in sems:
sem_set_responsable_name(context, sem)
sem["_titre_num_target"] = (
@@ -498,17 +498,16 @@ def table_formsemestres(
def list_formsemestre_by_etape(
- context, etape_apo=None, annee_scolaire=False, REQUEST=None
+ context, etape_apo=False, annee_scolaire=False, REQUEST=None
):
- """Liste des semestres de cette etape, pour l'annee scolaire indiquée (sinon, pour toutes)
- """
+ """Liste des semestres de cette etape, pour l'annee scolaire indiquée (sinon, pour toutes)"""
ds = {} # formsemestre_id : sem
if etape_apo:
sems = do_formsemestre_list(context, args={"etape_apo": etape_apo})
for sem in sems:
if annee_scolaire: # restriction annee scolaire
if sem_in_annee_scolaire(
- context, sem, year=annee_scolaire, REQUEST=REQUEST
+ context, sem, year=int(annee_scolaire), REQUEST=REQUEST
):
ds[sem["formsemestre_id"]] = sem
sems = ds.values()
@@ -519,7 +518,7 @@ def list_formsemestre_by_etape(
sem
for sem in sems
if sem_in_annee_scolaire(
- context, sem, year=annee_scolaire, REQUEST=REQUEST
+ context, sem, year=int(annee_scolaire), REQUEST=REQUEST
)
]
@@ -528,8 +527,7 @@ def list_formsemestre_by_etape(
def view_formsemestre_by_etape(context, etape_apo=None, format="html", REQUEST=None):
- """Affiche table des semestres correspondants à l'étape
- """
+ """Affiche table des semestres correspondants à l'étape"""
if etape_apo:
html_title = (
"""
Semestres courants de l'étape %s
""" % etape_apo
diff --git a/sco_permissions.py b/sco_permissions.py
index b656db15d..9ec7f1147 100644
--- a/sco_permissions.py
+++ b/sco_permissions.py
@@ -35,7 +35,7 @@ ScoEditFormationTags = (
ScoView = "Sco View"
ScoEnsView = "Sco View Ens" # parties visibles par enseignants slt
-
+ScoObservateur = "Sco Observateur" # accès lecture restreint aux bulletins
ScoUsersAdmin = "Sco Users Manage"
ScoUsersView = "Sco Users View"
@@ -51,6 +51,7 @@ ScoSuperAdmin = "Sco Super Admin"
# Default permissions for default roles
# (set once on instance creation):
Sco_Default_Permissions = {
+ ScoObservateur: ("Ens", "Secr", "Admin", "RespPe"),
ScoView: ("Ens", "Secr", "Admin", "RespPe"),
ScoEnsView: ("Ens", "Admin", "RespPe"),
ScoUsersView: ("Ens", "Secr", "Admin", "RespPe"),
diff --git a/sco_recapcomplet.py b/sco_recapcomplet.py
index fb28e8715..22bdc89eb 100644
--- a/sco_recapcomplet.py
+++ b/sco_recapcomplet.py
@@ -51,6 +51,7 @@ def formsemestre_recapcomplet(
xml_with_decisions=False, # XML avec decisions
rank_partition_id=None, # si None, calcul rang global
pref_override=True, # si vrai, les prefs ont la priorite sur le param hidebac
+ force_publishing=True, # publie les XML/JSON meme si bulletins non publiés
REQUEST=None,
):
"""Page récapitulant les notes d'un semestre.
@@ -72,6 +73,7 @@ def formsemestre_recapcomplet(
else:
hidebac = int(hidebac)
xml_with_decisions = int(xml_with_decisions)
+ force_publishing = int(force_publishing)
isFile = tabformat in ("csv", "xls", "xml", "xlsall", "json")
H = []
if not isFile:
@@ -144,6 +146,7 @@ def formsemestre_recapcomplet(
sortcol=sortcol,
xml_with_decisions=xml_with_decisions,
rank_partition_id=rank_partition_id,
+ force_publishing=force_publishing,
)
)
@@ -191,6 +194,7 @@ def do_formsemestre_recapcomplet(
xml_with_decisions=False,
disable_etudlink=False,
rank_partition_id=None, # si None, calcul rang global
+ force_publishing=True,
):
"""Calcule et renvoie le tableau récapitulatif."""
data, filename, format = make_formsemestre_recapcomplet(**vars())
@@ -219,6 +223,7 @@ def make_formsemestre_recapcomplet(
xml_with_decisions=False,
disable_etudlink=False,
rank_partition_id=None, # si None, calcul rang global
+ force_publishing=True, # donne bulletins JSON/XML meme si non publiés
):
"""Grand tableau récapitulatif avec toutes les notes de modules
pour tous les étudiants, les moyennes par UE et générale,
@@ -226,11 +231,19 @@ def make_formsemestre_recapcomplet(
"""
if format == "xml":
return _formsemestre_recapcomplet_xml(
- context, formsemestre_id, xml_nodate, xml_with_decisions=xml_with_decisions
+ context,
+ formsemestre_id,
+ xml_nodate,
+ xml_with_decisions=xml_with_decisions,
+ force_publishing=force_publishing,
)
elif format == "json":
return _formsemestre_recapcomplet_json(
- context, formsemestre_id, xml_nodate, xml_with_decisions=xml_with_decisions
+ context,
+ formsemestre_id,
+ xml_nodate=xml_nodate,
+ xml_with_decisions=xml_with_decisions,
+ force_publishing=force_publishing,
)
if format[:3] == "xls":
keep_numeric = True # pas de conversion des notes en strings
@@ -791,7 +804,11 @@ def _list_notes_evals_stats(context, evals, key):
def _formsemestre_recapcomplet_xml(
- context, formsemestre_id, xml_nodate, xml_with_decisions=False
+ context,
+ formsemestre_id,
+ xml_nodate,
+ xml_with_decisions=False,
+ force_publishing=True,
):
"XML export: liste tous les bulletins XML."
@@ -825,7 +842,7 @@ def _formsemestre_recapcomplet_xml(
formsemestre_id,
etudid,
doc=doc,
- force_publishing=True,
+ force_publishing=force_publishing,
xml_nodate=xml_nodate,
xml_with_decisions=xml_with_decisions,
)
@@ -834,9 +851,17 @@ def _formsemestre_recapcomplet_xml(
def _formsemestre_recapcomplet_json(
- context, formsemestre_id, xml_nodate, xml_with_decisions=False
+ context,
+ formsemestre_id,
+ xml_nodate=False,
+ xml_with_decisions=False,
+ force_publishing=True,
):
- "JSON export: liste tous les bulletins JSON"
+ """JSON export: liste tous les bulletins JSON
+ :param xml_nodate(bool): indique la date courante (attribut docdate)
+ :param force_publishing: donne les bulletins même si non "publiés sur portail"
+ :returns: dict, "", "json"
+ """
if xml_nodate:
docdate = ""
else:
@@ -865,8 +890,27 @@ def _formsemestre_recapcomplet_json(
context,
formsemestre_id,
etudid,
- force_publishing=True,
+ force_publishing=force_publishing,
xml_with_decisions=xml_with_decisions,
)
)
return J, "", "json"
+
+
+def formsemestres_bulletins(context, annee_scolaire, REQUEST=None):
+ """Tous les bulletins des semestres publiés des semestres de l'année indiquée.
+ :param annee_scolaire(int): année de début de l'année scoalaire
+ :returns: JSON
+ """
+ jslist = []
+ sems = sco_formsemestre.list_formsemestre_by_etape(
+ context, annee_scolaire=annee_scolaire
+ )
+ log("formsemestres_bulletins(%s): %d sems" % (annee_scolaire, len(sems)))
+ for sem in sems:
+ J, _, _ = _formsemestre_recapcomplet_json(
+ context, sem["formsemestre_id"], force_publishing=False
+ )
+ jslist.append(J)
+
+ return sendJSON(REQUEST, jslist)