forked from ScoDoc/ScoDoc
611 lines
20 KiB
Python
611 lines
20 KiB
Python
# -*- mode: python -*-
|
|
# -*- coding: utf-8 -*-
|
|
|
|
##############################################################################
|
|
#
|
|
# Gestion scolarite IUT
|
|
#
|
|
# Copyright (c) 1999 - 2021 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
|
|
#
|
|
##############################################################################
|
|
|
|
"""Operations de base sur les formsemestres
|
|
"""
|
|
|
|
from sco_utils import *
|
|
from notesdb import *
|
|
from notes_log import log
|
|
from gen_tables import GenTable
|
|
|
|
import sco_codes_parcours
|
|
from sco_codes_parcours import NO_SEMESTRE_ID
|
|
|
|
_formsemestreEditor = EditableTable(
|
|
"notes_formsemestre",
|
|
"formsemestre_id",
|
|
(
|
|
"formsemestre_id",
|
|
"semestre_id",
|
|
"formation_id",
|
|
"titre",
|
|
"date_debut",
|
|
"date_fin",
|
|
"gestion_compensation",
|
|
"gestion_semestrielle",
|
|
"etat",
|
|
"bul_hide_xml",
|
|
"bul_bgcolor",
|
|
"modalite",
|
|
"resp_can_edit",
|
|
"resp_can_change_ens",
|
|
"ens_can_edit_eval",
|
|
"elt_sem_apo",
|
|
"elt_annee_apo",
|
|
),
|
|
sortkey="date_debut",
|
|
output_formators={
|
|
"date_debut": DateISOtoDMY,
|
|
"date_fin": DateISOtoDMY,
|
|
"gestion_compensation": str,
|
|
"gestion_semestrielle": str,
|
|
"etat": str,
|
|
"bul_hide_xml": str,
|
|
},
|
|
input_formators={
|
|
"date_debut": DateDMYtoISO,
|
|
"date_fin": DateDMYtoISO,
|
|
"gestion_compensation": int,
|
|
"gestion_semestrielle": int,
|
|
"etat": int,
|
|
"bul_hide_xml": int,
|
|
},
|
|
)
|
|
|
|
|
|
def get_formsemestre(context, formsemestre_id):
|
|
"list ONE formsemestre"
|
|
try:
|
|
sem = do_formsemestre_list(context, args={"formsemestre_id": formsemestre_id})[
|
|
0
|
|
]
|
|
return sem
|
|
except:
|
|
log("get_formsemestre: invalid formsemestre_id (%s)" % formsemestre_id)
|
|
raise
|
|
|
|
|
|
def do_formsemestre_list(context, *a, **kw):
|
|
"list formsemestres"
|
|
# log('do_formsemestre_list: a=%s kw=%s' % (str(a),str(kw)))
|
|
cnx = context.GetDBConnexion()
|
|
|
|
sems = _formsemestreEditor.list(cnx, *a, **kw)
|
|
|
|
# Ajoute les étapes Apogee et les responsables:
|
|
for sem in sems:
|
|
sem["etapes"] = read_formsemestre_etapes(context, sem["formsemestre_id"])
|
|
sem["responsables"] = read_formsemestre_responsables(
|
|
context, sem["formsemestre_id"]
|
|
)
|
|
|
|
# Filtre sur code etape si indiqué:
|
|
if "args" in kw:
|
|
etape = kw["args"].get("etape_apo", None)
|
|
if etape:
|
|
sems = [sem for sem in sems if etape in sem["etapes"]]
|
|
|
|
for sem in sems:
|
|
formsemestre_enrich(context, sem)
|
|
|
|
# tri par date
|
|
sems.sort(
|
|
lambda x, y: cmp(
|
|
(y["dateord"], y["semestre_id"]), (x["dateord"], x["semestre_id"])
|
|
)
|
|
)
|
|
|
|
return sems
|
|
|
|
|
|
def formsemestre_enrich(context, sem):
|
|
"""Ajoute champs souvent utiles: titre + annee et dateord (pour tris)"""
|
|
# imports ici pour eviter refs circulaires
|
|
import sco_formsemestre_edit
|
|
import scolars
|
|
|
|
F = context.Notes.formation_list(args={"formation_id": sem["formation_id"]})[0]
|
|
parcours = sco_codes_parcours.get_parcours_from_code(F["type_parcours"])
|
|
# 'S1', 'S2', ... ou '' pour les monosemestres
|
|
if sem["semestre_id"] != NO_SEMESTRE_ID:
|
|
sem["sem_id_txt"] = "S%s" % sem["semestre_id"]
|
|
else:
|
|
sem["sem_id_txt"] = ""
|
|
# Nom avec numero semestre:
|
|
sem["titre_num"] = sem["titre"] # eg "DUT Informatique"
|
|
if sem["semestre_id"] != NO_SEMESTRE_ID:
|
|
sem["titre_num"] += " %s %s" % (
|
|
parcours.SESSION_NAME,
|
|
sem["semestre_id"],
|
|
) # eg "DUT Informatique semestre 2"
|
|
|
|
sem["dateord"] = DateDMYtoISO(sem["date_debut"])
|
|
sem["date_debut_iso"] = DateDMYtoISO(sem["date_debut"])
|
|
sem["date_fin_iso"] = DateDMYtoISO(sem["date_fin"])
|
|
try:
|
|
mois_debut, annee_debut = sem["date_debut"].split("/")[1:]
|
|
except:
|
|
mois_debut, annee_debut = "", ""
|
|
try:
|
|
mois_fin, annee_fin = sem["date_fin"].split("/")[1:]
|
|
except:
|
|
mois_fin, annee_fin = "", ""
|
|
sem["annee_debut"] = annee_debut
|
|
sem["annee_fin"] = annee_fin
|
|
sem["mois_debut_ord"] = int(mois_debut)
|
|
sem["mois_fin_ord"] = int(mois_fin)
|
|
|
|
sem["annee"] = annee_debut
|
|
# 2007 ou 2007-2008:
|
|
sem["anneescolaire"] = annee_scolaire_repr(int(annee_debut), sem["mois_debut_ord"])
|
|
# La période: considère comme "S1" (ou S3) les débuts en aout-sept-octobre
|
|
# devrait sans doute pouvoir etre changé...
|
|
if sem["mois_debut_ord"] >= 8 and sem["mois_debut_ord"] <= 10:
|
|
sem["periode"] = 1 # typiquement, début en septembre: S1, S3...
|
|
else:
|
|
sem["periode"] = 2 # typiquement, début en février: S2, S4...
|
|
|
|
sem["titreannee"] = "%s %s %s" % (
|
|
sem["titre_num"],
|
|
sem.get("modalite", ""),
|
|
annee_debut,
|
|
)
|
|
if annee_fin != annee_debut:
|
|
sem["titreannee"] += "-" + annee_fin
|
|
sem["annee"] += "-" + annee_fin
|
|
# et les dates sous la forme "oct 2007 - fev 2008"
|
|
months = scolars.abbrvmonthsnames
|
|
if mois_debut:
|
|
mois_debut = months[int(mois_debut) - 1]
|
|
if mois_fin:
|
|
mois_fin = months[int(mois_fin) - 1]
|
|
sem["mois_debut"] = mois_debut + " " + annee_debut
|
|
sem["mois_fin"] = mois_fin + " " + annee_fin
|
|
sem["titremois"] = "%s %s (%s - %s)" % (
|
|
sem["titre_num"],
|
|
sem.get("modalite", ""),
|
|
sem["mois_debut"],
|
|
sem["mois_fin"],
|
|
)
|
|
sem["session_id"] = sco_formsemestre_edit.get_formsemestre_session_id(
|
|
context, sem, F, parcours
|
|
)
|
|
sem["etapes"] = read_formsemestre_etapes(context, sem["formsemestre_id"])
|
|
sem["etapes_apo_str"] = formsemestre_etape_apo_str(sem)
|
|
sem["responsables"] = read_formsemestre_responsables(
|
|
context, sem["formsemestre_id"]
|
|
)
|
|
|
|
|
|
def formsemestre_etape_apo_str(sem):
|
|
"chaine décrivant le(s) codes étapes Apogée"
|
|
return etapes_apo_str(sem["etapes"])
|
|
|
|
|
|
def etapes_apo_str(etapes):
|
|
"Chaine decrivant une liste d'instance de ApoEtapeVDI"
|
|
return ", ".join([str(x) for x in etapes])
|
|
|
|
|
|
def do_formsemestre_edit(context, sem, cnx=None, **kw):
|
|
"""Apply modifications to formsemestre.
|
|
Update etapes and resps. Invalidate cache."""
|
|
if not cnx:
|
|
cnx = context.GetDBConnexion()
|
|
|
|
_formsemestreEditor.edit(cnx, sem, **kw)
|
|
write_formsemestre_etapes(context, sem)
|
|
write_formsemestre_responsables(context, sem)
|
|
|
|
context._inval_cache(formsemestre_id=sem["formsemestre_id"]) # > modif formsemestre
|
|
|
|
|
|
def read_formsemestre_responsables(context, formsemestre_id):
|
|
"""recupere liste des responsables de ce semestre
|
|
:returns: liste de chaines
|
|
"""
|
|
r = SimpleDictFetch(
|
|
context,
|
|
"SELECT responsable_id FROM notes_formsemestre_responsables WHERE formsemestre_id = %(formsemestre_id)s",
|
|
{"formsemestre_id": formsemestre_id},
|
|
)
|
|
return [x["responsable_id"] for x in r]
|
|
|
|
|
|
def write_formsemestre_responsables(context, sem):
|
|
return _write_formsemestre_aux(context, sem, "responsables", "responsable_id")
|
|
|
|
|
|
# ---------------------- Coefs des UE
|
|
|
|
_formsemestre_uecoef_editor = EditableTable(
|
|
"notes_formsemestre_uecoef",
|
|
"formsemestre_uecoef_id",
|
|
("formsemestre_uecoef_id", "formsemestre_id", "ue_id", "coefficient"),
|
|
)
|
|
|
|
formsemestre_uecoef_create = _formsemestre_uecoef_editor.create
|
|
formsemestre_uecoef_edit = _formsemestre_uecoef_editor.edit
|
|
formsemestre_uecoef_list = _formsemestre_uecoef_editor.list
|
|
formsemestre_uecoef_delete = _formsemestre_uecoef_editor.delete
|
|
|
|
|
|
def do_formsemestre_uecoef_edit_or_create(context, cnx, formsemestre_id, ue_id, coef):
|
|
"modify or create the coef"
|
|
coefs = formsemestre_uecoef_list(
|
|
cnx, args={"formsemestre_id": formsemestre_id, "ue_id": ue_id}
|
|
)
|
|
if coefs:
|
|
formsemestre_uecoef_edit(
|
|
cnx,
|
|
args={
|
|
"formsemestre_uecoef_id": coefs[0]["formsemestre_uecoef_id"],
|
|
"coefficient": coef,
|
|
},
|
|
)
|
|
else:
|
|
formsemestre_uecoef_create(
|
|
cnx,
|
|
args={
|
|
"formsemestre_id": formsemestre_id,
|
|
"ue_id": ue_id,
|
|
"coefficient": coef,
|
|
},
|
|
)
|
|
|
|
|
|
def do_formsemestre_uecoef_delete(context, cnx, formsemestre_id, ue_id):
|
|
"delete coef for this (ue,sem)"
|
|
coefs = formsemestre_uecoef_list(
|
|
cnx, args={"formsemestre_id": formsemestre_id, "ue_id": ue_id}
|
|
)
|
|
if coefs:
|
|
formsemestre_uecoef_delete(cnx, coefs[0]["formsemestre_uecoef_id"])
|
|
|
|
|
|
def read_formsemestre_etapes(context, formsemestre_id):
|
|
"""recupere liste des codes etapes associés à ce semestre
|
|
:returns: liste d'instance de ApoEtapeVDI
|
|
"""
|
|
r = SimpleDictFetch(
|
|
context,
|
|
"SELECT etape_apo FROM notes_formsemestre_etapes WHERE formsemestre_id = %(formsemestre_id)s",
|
|
{"formsemestre_id": formsemestre_id},
|
|
)
|
|
return [ApoEtapeVDI(x["etape_apo"]) for x in r if x["etape_apo"]]
|
|
|
|
|
|
def write_formsemestre_etapes(context, sem):
|
|
return _write_formsemestre_aux(context, sem, "etapes", "etape_apo")
|
|
|
|
|
|
def _write_formsemestre_aux(context, sem, fieldname, valuename):
|
|
"""fieldname: 'etapes' ou 'responsables'
|
|
valuename: 'etape_apo' ou 'responsable_id'
|
|
"""
|
|
if not "etapes" in sem:
|
|
return
|
|
cnx = context.GetDBConnexion(autocommit=False)
|
|
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
|
|
tablename = "notes_formsemestre_" + fieldname
|
|
try:
|
|
cursor.execute(
|
|
"DELETE from " + tablename + " where formsemestre_id = %(formsemestre_id)s",
|
|
{"formsemestre_id": sem["formsemestre_id"]},
|
|
)
|
|
for item in sem[fieldname]:
|
|
if item:
|
|
cursor.execute(
|
|
"INSERT INTO "
|
|
+ tablename
|
|
+ " (formsemestre_id, "
|
|
+ valuename
|
|
+ ") VALUES (%(formsemestre_id)s, %("
|
|
+ valuename
|
|
+ ")s)",
|
|
{"formsemestre_id": sem["formsemestre_id"], valuename: str(item)},
|
|
)
|
|
except:
|
|
log("Warning: exception in write_formsemestre_aux !")
|
|
cnx.rollback()
|
|
raise
|
|
cnx.commit()
|
|
|
|
|
|
# ------ Utilisé pour stocker le VDI avec le code étape (noms de fichiers maquettes et code semestres)
|
|
class ApoEtapeVDI:
|
|
_ETAPE_VDI_SEP = "!"
|
|
|
|
def __init__(self, etape_vdi=None, etape="", vdi=""):
|
|
"""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)
|
|
elif etape:
|
|
if self._ETAPE_VDI_SEP in etape:
|
|
raise ScoValueError("valeur code etape invalide")
|
|
self.etape, self.vdi = etape, vdi
|
|
self.etape_vdi = self.concat_etape_vdi(etape, vdi)
|
|
else:
|
|
self.etape_vdi, self.etape, self.vdi = "", "", ""
|
|
|
|
def __repr__(self):
|
|
return self.__class__.__name__ + "('" + str(self) + "')"
|
|
|
|
def __str__(self):
|
|
return self.etape_vdi
|
|
|
|
def __cmp__(self, other):
|
|
"""Test égalité de deux codes étapes.
|
|
Si le VDI des deux est spécifié, on l'utilise. Sinon, seul le code étape est pris en compte.
|
|
Donc V1RT == V1RT!111, V1RT!110 == V1RT, V1RT!77 != V1RT!78, ...
|
|
"""
|
|
if other is None:
|
|
return -1
|
|
if type(other) == str:
|
|
other = ApoEtapeVDI(other)
|
|
|
|
if self.vdi and other.vdi:
|
|
return cmp((self.etape, self.vdi), (other.etape, other.vdi))
|
|
else:
|
|
return cmp(self.etape, other.etape)
|
|
|
|
def split_etape_vdi(self, etape_vdi):
|
|
"""Etape Apogee can be stored as 'V1RT' or, including the VDI version,
|
|
as 'V1RT!111'
|
|
Returns etape, VDI
|
|
"""
|
|
if etape_vdi:
|
|
t = etape_vdi.split(self._ETAPE_VDI_SEP)
|
|
if len(t) == 1:
|
|
etape = etape_vdi
|
|
vdi = ""
|
|
elif len(t) == 2:
|
|
etape, vdi = t
|
|
else:
|
|
raise ValueError("invalid code etape")
|
|
return etape, vdi
|
|
else:
|
|
return etape_vdi, ""
|
|
|
|
def concat_etape_vdi(self, etape, vdi=""):
|
|
if vdi:
|
|
return self._ETAPE_VDI_SEP.join([etape, vdi])
|
|
else:
|
|
return etape
|
|
|
|
|
|
"""
|
|
[ ApoEtapeVDI('V1RT!111'), ApoEtapeVDI('V1RT!112'), ApoEtapeVDI('VCRT'), ApoEtapeVDI('V1RT') ]
|
|
"""
|
|
|
|
|
|
def sem_set_responsable_name(context, sem):
|
|
"ajoute champs responsable_name"
|
|
sem["responsable_name"] = ", ".join(
|
|
[
|
|
context.Users.user_info(responsable_id)["nomprenom"]
|
|
for responsable_id in sem["responsables"]
|
|
]
|
|
)
|
|
|
|
|
|
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
|
|
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
|
|
manuellement dans le formsemestre_edit afin de couvrir les cas particulier
|
|
comme un semestre S2 qui commencerait en décembre... voire novembre.
|
|
)
|
|
"""
|
|
if not year:
|
|
year = AnneeScolaire(REQUEST)
|
|
# est-on dans la même année universitaire ?
|
|
if sem["mois_debut_ord"] > 7:
|
|
if sem["annee_debut"] != str(year):
|
|
return False
|
|
else:
|
|
if sem["annee_debut"] != str(year + 1):
|
|
return False
|
|
# rafinement éventuel sur le semestre
|
|
# saison is None => pas de rafinement => True
|
|
if saison == 0:
|
|
return True
|
|
elif saison == 1: # calcul en fonction de la saison
|
|
return sem["mois_debut_ord"] > 7 and sem["mois_debut_ord"] < 12
|
|
else: # saison == 0
|
|
return sem["mois_debut_ord"] <= 7 or sem["mois_debut_ord"] == 12
|
|
|
|
|
|
def sem_in_annee_scolaire(context, sem, year=False, REQUEST=None):
|
|
"""Test si sem appartient à l'année scolaire year (int).
|
|
N'utilise que la date de debut, pivot au 1er août.
|
|
Si annee non specifiée, année scolaire courante
|
|
"""
|
|
if not year:
|
|
year = AnneeScolaire(REQUEST)
|
|
return ((sem["annee_debut"] == str(year)) and (sem["mois_debut_ord"] > 7)) or (
|
|
(sem["annee_debut"] == str(year + 1)) and (sem["mois_debut_ord"] <= 7)
|
|
)
|
|
|
|
|
|
def sem_une_annee(context, sem):
|
|
"""Test si sem est entièrement sur la même année scolaire.
|
|
(ce n'est pas obligatoire mais si ce n'est pas le cas les exports Apogée ne vont pas fonctionner)
|
|
pivot au 1er août.
|
|
"""
|
|
if sem["date_debut_iso"] > sem["date_fin_iso"]:
|
|
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
|
|
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
|
|
fin -= 1
|
|
return debut == fin
|
|
|
|
|
|
def sem_est_courant(context, sem):
|
|
"""Vrai si la date actuelle (now) est dans le semestre (les dates de début et fin sont incluses)"""
|
|
now = time.strftime("%Y-%m-%d")
|
|
debut = DateDMYtoISO(sem["date_debut"])
|
|
fin = DateDMYtoISO(sem["date_fin"])
|
|
return (debut <= now) and (now <= 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()
|
|
semdepts = []
|
|
for dept in depts:
|
|
semdepts += [
|
|
(sem, dept.Scolarite.Notes)
|
|
for sem in do_formsemestre_list(dept.Scolarite.Notes)
|
|
if sem["etat"] == "1"
|
|
]
|
|
return semdepts
|
|
|
|
|
|
def table_formsemestres(
|
|
context,
|
|
sems,
|
|
columns_ids=(),
|
|
sup_columns_ids=(),
|
|
html_title="<h2>Semestres</h2>",
|
|
html_next_section="",
|
|
REQUEST=None,
|
|
):
|
|
"""Une table presentant des semestres"""
|
|
for sem in sems:
|
|
sem_set_responsable_name(context, sem)
|
|
sem["_titre_num_target"] = (
|
|
"formsemestre_status?formsemestre_id=%s" % sem["formsemestre_id"]
|
|
)
|
|
|
|
if not columns_ids:
|
|
columns_ids = (
|
|
"etat",
|
|
"modalite",
|
|
"mois_debut",
|
|
"mois_fin",
|
|
"titre_num",
|
|
"responsable_name",
|
|
"etapes_apo_str",
|
|
)
|
|
columns_ids += sup_columns_ids
|
|
|
|
titles = {
|
|
"modalite": "",
|
|
"mois_debut": "Début",
|
|
"mois_fin": "Fin",
|
|
"titre_num": "Semestre",
|
|
"responsable_name": "Resp.",
|
|
"etapes_apo_str": "Apo.",
|
|
}
|
|
if sems:
|
|
preferences = context.get_preferences(sems[0]["formsemestre_id"])
|
|
else:
|
|
preferences = context.get_preferences()
|
|
tab = GenTable(
|
|
columns_ids=columns_ids,
|
|
rows=sems,
|
|
titles=titles,
|
|
html_class="table_leftalign",
|
|
html_sortable=True,
|
|
html_title=html_title,
|
|
html_next_section=html_next_section,
|
|
html_empty_element="<p><em>aucun résultat</em></p>",
|
|
page_title="Semestres",
|
|
preferences=preferences,
|
|
)
|
|
return tab
|
|
|
|
|
|
def list_formsemestre_by_etape(
|
|
context, etape_apo=False, annee_scolaire=False, REQUEST=None
|
|
):
|
|
"""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=int(annee_scolaire), REQUEST=REQUEST
|
|
):
|
|
ds[sem["formsemestre_id"]] = sem
|
|
sems = ds.values()
|
|
else:
|
|
sems = do_formsemestre_list(context)
|
|
if annee_scolaire:
|
|
sems = [
|
|
sem
|
|
for sem in sems
|
|
if sem_in_annee_scolaire(
|
|
context, sem, year=int(annee_scolaire), REQUEST=REQUEST
|
|
)
|
|
]
|
|
|
|
sems.sort(key=lambda s: (s["modalite"], s["dateord"]))
|
|
return sems
|
|
|
|
|
|
def view_formsemestre_by_etape(context, etape_apo=None, format="html", REQUEST=None):
|
|
"""Affiche table des semestres correspondants à l'étape"""
|
|
if etape_apo:
|
|
html_title = (
|
|
"""<h2>Semestres courants de l'étape <tt>%s</tt></h2>""" % etape_apo
|
|
)
|
|
else:
|
|
html_title = """<h2>Semestres courants</h2>"""
|
|
tab = table_formsemestres(
|
|
context,
|
|
list_formsemestre_by_etape(
|
|
context, etape_apo=etape_apo, annee_scolaire=AnneeScolaire(REQUEST)
|
|
),
|
|
html_title=html_title,
|
|
html_next_section="""<form action="view_formsemestre_by_etape">
|
|
Etape: <input name="etape_apo" type="text" size="8"></input>
|
|
</form>""",
|
|
REQUEST=REQUEST,
|
|
)
|
|
tab.base_url = "%s?etape_apo=%s" % (REQUEST.URL0, etape_apo or "")
|
|
return tab.make_page(context, format=format, REQUEST=REQUEST)
|
|
|
|
|
|
def sem_has_etape(sem, code_etape):
|
|
return code_etape in sem["etapes"]
|