1
0
forked from ScoDoc/ScoDoc

Merge branch 'master' of https://scodoc.org/git/viennet/ScoDoc into ScoDoc8

This commit is contained in:
Emmanuel Viennet 2021-01-29 09:18:20 +01:00
commit 2154b60cde
10 changed files with 311 additions and 167 deletions

View File

@ -38,11 +38,9 @@ import re
import time import time
import calendar import calendar
from sco_zope import * from sco_zope import * # pylint: disable=unused-wildcard-import
from sco_permissions import ScoEntrepriseView, ScoEntrepriseChange from sco_permissions import ScoEntrepriseView, ScoEntrepriseChange
# ---------------
from notes_log import log from notes_log import log
from scolog import logdb from scolog import logdb
from sco_utils import SCO_ENCODING from sco_utils import SCO_ENCODING

181
ZNotes.py
View File

@ -27,16 +27,39 @@
"""Interface Zope <-> Notes """Interface Zope <-> Notes
""" """
import time
import datetime
import jaxml
import pprint
from sco_zope import * from sco_zope import * # pylint: disable=unused-wildcard-import
# --------------- # ---------------
# from sco_utils import *
from notesdb import * import sco_utils as scu
import notesdb as ndb
from notes_log import log, sendAlarm from notes_log import log, sendAlarm
import scolog import scolog
from scolog import logdb from scolog import logdb
from sco_utils import * from sco_permissions import (
ScoView,
ScoEnsView,
ScoImplement,
ScoChangeFormation,
ScoObservateur,
ScoEtudInscrit,
ScoEtudChangeGroups,
ScoEtudChangeAdr,
ScoEtudSupprAnnotations,
ScoEditAllEvals,
ScoEditAllNotes,
ScoEditFormationTags,
ScoEditApo,
ScoSuperAdmin,
)
from sco_exceptions import ScoValueError, ScoLockedFormError, ScoGenError, AccessDenied
from TrivialFormulator import TrivialFormulator
import htmlutils import htmlutils
import sco_excel import sco_excel
@ -64,6 +87,7 @@ import sco_edit_module
import sco_tag_module import sco_tag_module
import sco_bulletins import sco_bulletins
import sco_bulletins_pdf import sco_bulletins_pdf
import sco_compute_moy
import sco_recapcomplet import sco_recapcomplet
import sco_liste_notes import sco_liste_notes
import sco_saisie_notes import sco_saisie_notes
@ -94,7 +118,8 @@ import sco_export_results
import sco_formsemestre_exterieurs import sco_formsemestre_exterieurs
from sco_pdf import PDFLOCK from sco_pdf import PDFLOCK
from notes_table import * import notes_table
from notes_table import NOTES_CACHE_INST, CacheNotesTable
import VERSION import VERSION
# #
@ -231,7 +256,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
] ]
return "\n".join(H) return "\n".join(H)
# return pdfbulletins.essaipdf(REQUEST) # return pdfbulletins.essaipdf(REQUEST)
# return sendPDFFile(REQUEST, pdfbulletins.pdftrombino(0,0), 'toto.pdf' ) # return scu.sendPDFFile(REQUEST, pdfbulletins.pdftrombino(0,0), 'toto.pdf' )
# Python methods: # Python methods:
security.declareProtected(ScoView, "formsemestre_status") security.declareProtected(ScoView, "formsemestre_status")
@ -347,15 +372,6 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
def index_html(self, REQUEST=None): def index_html(self, REQUEST=None):
"Page accueil formations" "Page accueil formations"
lockicon = icontag(
"lock32_img", title="Comporte des semestres verrouillés", border="0"
)
suppricon = icontag(
"delete_small_img", border="0", alt="supprimer", title="Supprimer"
)
editicon = icontag(
"edit_img", border="0", alt="modifier", title="Modifier titres et code"
)
editable = REQUEST.AUTHENTICATED_USER.has_permission(ScoChangeFormation, self) editable = REQUEST.AUTHENTICATED_USER.has_permission(ScoChangeFormation, self)
@ -386,7 +402,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# --- Formations # --- Formations
_formationEditor = EditableTable( _formationEditor = ndb.EditableTable(
"notes_formations", "notes_formations",
"formation_id", "formation_id",
( (
@ -473,7 +489,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
r = self._formationEditor.list(cnx, args=args) r = self._formationEditor.list(cnx, args=args)
# log('%d formations found' % len(r)) # log('%d formations found' % len(r))
return sendResult(REQUEST, r, name="formation", format=format) return scu.sendResult(REQUEST, r, name="formation", format=format)
security.declareProtected(ScoView, "formation_export") security.declareProtected(ScoView, "formation_export")
@ -517,9 +533,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
elif tf[0] == -1: elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(REQUEST.URL1) return REQUEST.RESPONSE.redirect(REQUEST.URL1)
else: else:
formation_id, junk, junk = self.formation_import_xml( formation_id, _, _ = self.formation_import_xml(tf[2]["xmlfile"], REQUEST)
tf[2]["xmlfile"], REQUEST
)
return ( return (
"\n".join(H) "\n".join(H)
@ -556,7 +570,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
return new_id, modules_old2new, ues_old2new return new_id, modules_old2new, ues_old2new
# --- UE # --- UE
_ueEditor = EditableTable( _ueEditor = ndb.EditableTable(
"notes_ue", "notes_ue",
"ue_id", "ue_id",
( (
@ -573,11 +587,11 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
"coefficient", "coefficient",
), ),
sortkey="numero", sortkey="numero",
input_formators={"type": int_null_is_zero}, input_formators={"type": ndb.int_null_is_zero},
output_formators={ output_formators={
"numero": int_null_is_zero, "numero": ndb.int_null_is_zero,
"ects": float_null_is_null, "ects": ndb.float_null_is_null,
"coefficient": float_null_is_zero, "coefficient": ndb.float_null_is_zero,
}, },
) )
@ -637,7 +651,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
) )
if delete_validations: if delete_validations:
log("deleting all validations of UE %s" % ue_id) log("deleting all validations of UE %s" % ue_id)
SimpleQuery( ndb.SimpleQuery(
self, self,
"DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s", "DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s",
{"ue_id": ue_id}, {"ue_id": ue_id},
@ -648,12 +662,12 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
for mat in mats: for mat in mats:
self.do_matiere_delete(mat["matiere_id"], REQUEST) self.do_matiere_delete(mat["matiere_id"], REQUEST)
# delete uecoef and events # delete uecoef and events
SimpleQuery( ndb.SimpleQuery(
self, self,
"DELETE FROM notes_formsemestre_uecoef WHERE ue_id=%(ue_id)s", "DELETE FROM notes_formsemestre_uecoef WHERE ue_id=%(ue_id)s",
{"ue_id": ue_id}, {"ue_id": ue_id},
) )
SimpleQuery( ndb.SimpleQuery(
self, "DELETE FROM scolar_events WHERE ue_id=%(ue_id)s", {"ue_id": ue_id} self, "DELETE FROM scolar_events WHERE ue_id=%(ue_id)s", {"ue_id": ue_id}
) )
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
@ -684,12 +698,12 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
return self._ueEditor.list(cnx, *args, **kw) return self._ueEditor.list(cnx, *args, **kw)
# --- Matieres # --- Matieres
_matiereEditor = EditableTable( _matiereEditor = ndb.EditableTable(
"notes_matieres", "notes_matieres",
"matiere_id", "matiere_id",
("matiere_id", "ue_id", "numero", "titre"), ("matiere_id", "ue_id", "numero", "titre"),
sortkey="numero", sortkey="numero",
output_formators={"numero": int_null_is_zero}, output_formators={"numero": ndb.int_null_is_zero},
) )
security.declareProtected(ScoChangeFormation, "do_matiere_create") security.declareProtected(ScoChangeFormation, "do_matiere_create")
@ -758,7 +772,6 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
# check # check
mat = self.do_matiere_list({"matiere_id": args[0]["matiere_id"]})[0] mat = self.do_matiere_list({"matiere_id": args[0]["matiere_id"]})[0]
ue = self.do_ue_list({"ue_id": mat["ue_id"]})[0]
if self.matiere_is_locked(mat["matiere_id"]): if self.matiere_is_locked(mat["matiere_id"]):
raise ScoLockedFormError() raise ScoLockedFormError()
# edit # edit
@ -770,7 +783,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
def do_matiere_formation_id(self, matiere_id): def do_matiere_formation_id(self, matiere_id):
"get formation_id from matiere" "get formation_id from matiere"
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
cursor.execute( cursor.execute(
"select UE.formation_id from notes_matieres M, notes_ue UE where M.matiere_id = %(matiere_id)s and M.ue_id = UE.ue_id", "select UE.formation_id from notes_matieres M, notes_ue UE where M.matiere_id = %(matiere_id)s and M.ue_id = UE.ue_id",
{"matiere_id": matiere_id}, {"matiere_id": matiere_id},
@ -779,7 +792,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
return res[0][0] return res[0][0]
# --- Modules # --- Modules
_moduleEditor = EditableTable( _moduleEditor = ndb.EditableTable(
"notes_modules", "notes_modules",
"module_id", "module_id",
( (
@ -802,13 +815,13 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
), ),
sortkey="numero, code, titre", sortkey="numero, code, titre",
output_formators={ output_formators={
"heures_cours": float_null_is_zero, "heures_cours": ndb.float_null_is_zero,
"heures_td": float_null_is_zero, "heures_td": ndb.float_null_is_zero,
"heures_tp": float_null_is_zero, "heures_tp": ndb.float_null_is_zero,
"numero": int_null_is_zero, "numero": ndb.int_null_is_zero,
"coefficient": float_null_is_zero, "coefficient": ndb.float_null_is_zero,
"module_type": int_null_is_zero "module_type": ndb.int_null_is_zero
#'ects' : float_null_is_null #'ects' : ndb.float_null_is_null
}, },
) )
@ -927,7 +940,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
"""True if UE should not be modified """True if UE should not be modified
(used in a locked formsemestre) (used in a locked formsemestre)
""" """
r = SimpleDictFetch( r = ndb.SimpleDictFetch(
self, self,
"""SELECT mi.* from notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi """SELECT mi.* from notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi
WHERE mi.module_id = mod.module_id AND mi.formsemestre_id = sem.formsemestre_id WHERE mi.module_id = mod.module_id AND mi.formsemestre_id = sem.formsemestre_id
@ -943,7 +956,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
"""True if matiere should not be modified """True if matiere should not be modified
(contains modules used in a locked formsemestre) (contains modules used in a locked formsemestre)
""" """
r = SimpleDictFetch( r = ndb.SimpleDictFetch(
self, self,
"""SELECT ma.* from notes_matieres ma, notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi """SELECT ma.* from notes_matieres ma, notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi
WHERE ma.matiere_id = mod.matiere_id AND mi.module_id = mod.module_id AND mi.formsemestre_id = sem.formsemestre_id WHERE ma.matiere_id = mod.matiere_id AND mi.module_id = mod.module_id AND mi.formsemestre_id = sem.formsemestre_id
@ -959,7 +972,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
"""True if module should not be modified """True if module should not be modified
(contains modules used in a locked formsemestre) (contains modules used in a locked formsemestre)
""" """
r = SimpleDictFetch( r = ndb.SimpleDictFetch(
self, self,
"""SELECT ue.* FROM notes_ue ue, notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi """SELECT ue.* FROM notes_ue ue, notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi
WHERE ue.ue_id = mod.ue_id WHERE ue.ue_id = mod.ue_id
@ -1100,7 +1113,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
args[argname] = L[argname] args[argname] = L[argname]
sems = sco_formsemestre.do_formsemestre_list(self, args=args) sems = sco_formsemestre.do_formsemestre_list(self, args=args)
# log('formsemestre_list: format="%s", %s semestres found' % (format,len(sems))) # log('formsemestre_list: format="%s", %s semestres found' % (format,len(sems)))
return sendResult(REQUEST, sems, name="formsemestre", format=format) return scu.sendResult(REQUEST, sems, name="formsemestre", format=format)
security.declareProtected(ScoView, "XMLgetFormsemestres") security.declareProtected(ScoView, "XMLgetFormsemestres")
@ -1115,8 +1128,8 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
if formsemestre_id: if formsemestre_id:
args["formsemestre_id"] = formsemestre_id args["formsemestre_id"] = formsemestre_id
if REQUEST: if REQUEST:
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE) REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
doc = jaxml.XML_document(encoding=SCO_ENCODING) doc = jaxml.XML_document(encoding=scu.SCO_ENCODING)
doc.formsemestrelist() doc.formsemestrelist()
for sem in sco_formsemestre.do_formsemestre_list(self, args=args): for sem in sco_formsemestre.do_formsemestre_list(self, args=args):
doc._push() doc._push()
@ -1497,8 +1510,8 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
self, moduleimpl_id=moduleimpl_id self, moduleimpl_id=moduleimpl_id
)[0] )[0]
sem = sco_formsemestre.get_formsemestre(self, M["formsemestre_id"]) sem = sco_formsemestre.get_formsemestre(self, M["formsemestre_id"])
debut_sem = DateDMYtoISO(sem["date_debut"]) debut_sem = ndb.DateDMYtoISO(sem["date_debut"])
fin_sem = DateDMYtoISO(sem["date_fin"]) fin_sem = ndb.DateDMYtoISO(sem["date_fin"])
list_insc = sco_moduleimpl.do_moduleimpl_listeetuds(self, moduleimpl_id) list_insc = sco_moduleimpl.do_moduleimpl_listeetuds(self, moduleimpl_id)
T = [] T = []
@ -1551,7 +1564,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
rows=T, rows=T,
html_class="table_leftalign", html_class="table_leftalign",
base_url="%s?moduleimpl_id=%s" % (REQUEST.URL0, moduleimpl_id), base_url="%s?moduleimpl_id=%s" % (REQUEST.URL0, moduleimpl_id),
filename="absmodule_" + make_filename(M["module"]["titre"]), filename="absmodule_" + scu.make_filename(M["module"]["titre"]),
caption="Absences dans le module %s" % M["module"]["titre"], caption="Absences dans le module %s" % M["module"]["titre"],
preferences=self.get_preferences(), preferences=self.get_preferences(),
) )
@ -1656,15 +1669,15 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
sem_ens[ensd["ens_id"]]["mods"].append(mod) sem_ens[ensd["ens_id"]]["mods"].append(mod)
# compte les absences ajoutées par chacun dans tout le semestre # compte les absences ajoutées par chacun dans tout le semestre
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
for ens in sem_ens: for ens in sem_ens:
cursor.execute( cursor.execute(
"select * from scolog L, notes_formsemestre_inscription I where method='AddAbsence' and authenticated_user=%(authenticated_user)s and L.etudid = I.etudid and I.formsemestre_id=%(formsemestre_id)s and date > %(date_debut)s and date < %(date_fin)s", "select * from scolog L, notes_formsemestre_inscription I where method='AddAbsence' and authenticated_user=%(authenticated_user)s and L.etudid = I.etudid and I.formsemestre_id=%(formsemestre_id)s and date > %(date_debut)s and date < %(date_fin)s",
{ {
"authenticated_user": ens, "authenticated_user": ens,
"formsemestre_id": formsemestre_id, "formsemestre_id": formsemestre_id,
"date_debut": DateDMYtoISO(sem["date_debut"]), "date_debut": ndb.DateDMYtoISO(sem["date_debut"]),
"date_fin": DateDMYtoISO(sem["date_fin"]), "date_fin": ndb.DateDMYtoISO(sem["date_fin"]),
}, },
) )
@ -1700,7 +1713,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
rows=sem_ens_list, rows=sem_ens_list,
html_sortable=True, html_sortable=True,
html_class="table_leftalign", html_class="table_leftalign",
filename=make_filename("Enseignants-" + sem["titreannee"]), filename=scu.make_filename("Enseignants-" + sem["titreannee"]),
html_title=self.html_sem_header( html_title=self.html_sem_header(
REQUEST, "Enseignants du semestre", sem, with_page_header=False REQUEST, "Enseignants du semestre", sem, with_page_header=False
), ),
@ -1716,7 +1729,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
def edit_enseignants_form_delete(self, REQUEST, moduleimpl_id, ens_id): def edit_enseignants_form_delete(self, REQUEST, moduleimpl_id, ens_id):
"remove ens" "remove ens"
M, sem = sco_moduleimpl.can_change_ens(self, REQUEST, moduleimpl_id) M, _ = sco_moduleimpl.can_change_ens(self, REQUEST, moduleimpl_id)
# search ens_id # search ens_id
ok = False ok = False
for ens in M["ens"]: for ens in M["ens"]:
@ -1731,7 +1744,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
) )
# --- Gestion des inscriptions aux modules # --- Gestion des inscriptions aux modules
_formsemestre_inscriptionEditor = EditableTable( _formsemestre_inscriptionEditor = ndb.EditableTable(
"notes_formsemestre_inscription", "notes_formsemestre_inscription",
"formsemestre_inscription_id", "formsemestre_inscription_id",
("formsemestre_inscription_id", "etudid", "formsemestre_id", "etat", "etape"), ("formsemestre_inscription_id", "etudid", "formsemestre_id", "etat", "etape"),
@ -1937,7 +1950,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
insem = insem[0] insem = insem[0]
# -- desinscription de tous les modules # -- desinscription de tous les modules
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
cursor.execute( cursor.execute(
"select moduleimpl_inscription_id from notes_moduleimpl_inscription Im, notes_moduleimpl M where Im.etudid=%(etudid)s and Im.moduleimpl_id = M.moduleimpl_id and M.formsemestre_id = %(formsemestre_id)s", "select moduleimpl_inscription_id from notes_moduleimpl_inscription Im, notes_moduleimpl M where Im.etudid=%(etudid)s and Im.moduleimpl_id = M.moduleimpl_id and M.formsemestre_id = %(formsemestre_id)s",
{"etudid": etudid, "formsemestre_id": formsemestre_id}, {"etudid": etudid, "formsemestre_id": formsemestre_id},
@ -2049,7 +2062,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
) )
# --- Evaluations # --- Evaluations
_evaluationEditor = EditableTable( _evaluationEditor = ndb.EditableTable(
"notes_evaluation", "notes_evaluation",
"evaluation_id", "evaluation_id",
( (
@ -2068,15 +2081,15 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
), ),
sortkey="numero desc, jour desc, heure_debut desc", # plus recente d'abord sortkey="numero desc, jour desc, heure_debut desc", # plus recente d'abord
output_formators={ output_formators={
"jour": DateISOtoDMY, "jour": ndb.DateISOtoDMY,
"visibulletin": str, "visibulletin": str,
"publish_incomplete": str, "publish_incomplete": str,
"numero": int_null_is_zero, "numero": ndb.int_null_is_zero,
}, },
input_formators={ input_formators={
"jour": DateDMYtoISO, "jour": ndb.DateDMYtoISO,
"heure_debut": TimetoISO8601, # converti par do_evaluation_list "heure_debut": ndb.TimetoISO8601, # converti par do_evaluation_list
"heure_fin": TimetoISO8601, # converti par do_evaluation_list "heure_fin": ndb.TimetoISO8601, # converti par do_evaluation_list
"visibulletin": int, "visibulletin": int,
"publish_incomplete": int, "publish_incomplete": int,
}, },
@ -2144,9 +2157,15 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
) )
if args["jour"]: if args["jour"]:
next_eval = None next_eval = None
t = (DateDMYtoISO(args["jour"]), TimetoISO8601(args["heure_debut"])) t = (
ndb.DateDMYtoISO(args["jour"]),
ndb.TimetoISO8601(args["heure_debut"]),
)
for e in ModEvals: for e in ModEvals:
if (DateDMYtoISO(e["jour"]), TimetoISO8601(e["heure_debut"])) > t: if (
ndb.DateDMYtoISO(e["jour"]),
ndb.TimetoISO8601(e["heure_debut"]),
) > t:
next_eval = e next_eval = e
break break
if next_eval: if next_eval:
@ -2218,8 +2237,8 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
date_debut = datetime.date(y, m, d) date_debut = datetime.date(y, m, d)
d, m, y = [int(x) for x in sem["date_fin"].split("/")] d, m, y = [int(x) for x in sem["date_fin"].split("/")]
date_fin = datetime.date(y, m, d) date_fin = datetime.date(y, m, d)
# passe par DateDMYtoISO pour avoir date pivot # passe par ndb.DateDMYtoISO pour avoir date pivot
y, m, d = [int(x) for x in DateDMYtoISO(jour).split("-")] y, m, d = [int(x) for x in ndb.DateDMYtoISO(jour).split("-")]
jour = datetime.date(y, m, d) jour = datetime.date(y, m, d)
if (jour > date_fin) or (jour < date_debut): if (jour > date_fin) or (jour < date_debut):
raise ScoValueError( raise ScoValueError(
@ -2232,7 +2251,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
args["heure_fin"] = heure_fin args["heure_fin"] = heure_fin
if jour and ((not heure_debut) or (not heure_fin)): if jour and ((not heure_debut) or (not heure_fin)):
raise ScoValueError("Les heures doivent être précisées") raise ScoValueError("Les heures doivent être précisées")
d = TimeDuration(heure_debut, heure_fin) d = ndb.TimeDuration(heure_debut, heure_fin)
if d and ((d < 0) or (d > 60 * 12)): if d and ((d < 0) or (d > 60 * 12)):
raise ScoValueError("Heures de l'évaluation incohérentes !") raise ScoValueError("Heures de l'évaluation incohérentes !")
@ -2332,11 +2351,11 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
8, 00 8, 00
) # au cas ou pas d'heure (note externe?) ) # au cas ou pas d'heure (note externe?)
heure_fin_dt = e["heure_fin"] or datetime.time(8, 00) heure_fin_dt = e["heure_fin"] or datetime.time(8, 00)
e["heure_debut"] = TimefromISO8601(e["heure_debut"]) e["heure_debut"] = ndb.TimefromISO8601(e["heure_debut"])
e["heure_fin"] = TimefromISO8601(e["heure_fin"]) e["heure_fin"] = ndb.TimefromISO8601(e["heure_fin"])
e["jouriso"] = DateDMYtoISO(e["jour"]) e["jouriso"] = ndb.DateDMYtoISO(e["jour"])
heure_debut, heure_fin = e["heure_debut"], e["heure_fin"] heure_debut, heure_fin = e["heure_debut"], e["heure_fin"]
d = TimeDuration(heure_debut, heure_fin) d = ndb.TimeDuration(heure_debut, heure_fin)
if d is not None: if d is not None:
m = d % 60 m = d % 60
e["duree"] = "%dh" % (d / 60) e["duree"] = "%dh" % (d / 60)
@ -2366,7 +2385,6 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
def do_evaluation_list_in_formsemestre(self, formsemestre_id): def do_evaluation_list_in_formsemestre(self, formsemestre_id):
"list evaluations in this formsemestre" "list evaluations in this formsemestre"
cnx = self.GetDBConnexion()
mods = sco_moduleimpl.do_moduleimpl_list(self, formsemestre_id=formsemestre_id) mods = sco_moduleimpl.do_moduleimpl_list(self, formsemestre_id=formsemestre_id)
evals = [] evals = []
for mod in mods: for mod in mods:
@ -2528,7 +2546,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
if r != None: if r != None:
return r return r
cnx = self.GetDBConnexion() cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
cond = " where evaluation_id=%(evaluation_id)s" cond = " where evaluation_id=%(evaluation_id)s"
if by_uid: if by_uid:
cond += " and uid=%(by_uid)s" cond += " and uid=%(by_uid)s"
@ -2541,7 +2559,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
d = {} d = {}
if filter_suppressed: if filter_suppressed:
for x in res: for x in res:
if x["value"] != NOTES_SUPPRESS: if x["value"] != scu.NOTES_SUPPRESS:
d[x["etudid"]] = x d[x["etudid"]] = x
else: else:
for x in res: for x in res:
@ -2560,7 +2578,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf( pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf(
self, formsemestre_id, REQUEST, version=version self, formsemestre_id, REQUEST, version=version
) )
return sendPDFFile(REQUEST, pdfdoc, filename) return scu.sendPDFFile(REQUEST, pdfdoc, filename)
security.declareProtected(ScoView, "etud_bulletins_pdf") security.declareProtected(ScoView, "etud_bulletins_pdf")
@ -2569,7 +2587,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
pdfdoc, filename = sco_bulletins_pdf.get_etud_bulletins_pdf( pdfdoc, filename = sco_bulletins_pdf.get_etud_bulletins_pdf(
self, etudid, REQUEST, version=version self, etudid, REQUEST, version=version
) )
return sendPDFFile(REQUEST, pdfdoc, filename) return scu.sendPDFFile(REQUEST, pdfdoc, filename)
security.declareProtected(ScoView, "formsemestre_bulletins_pdf_choice") security.declareProtected(ScoView, "formsemestre_bulletins_pdf_choice")
formsemestre_bulletins_pdf_choice = sco_bulletins.formsemestre_bulletins_pdf_choice formsemestre_bulletins_pdf_choice = sco_bulletins.formsemestre_bulletins_pdf_choice
@ -2591,7 +2609,6 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
): ):
"envoi a chaque etudiant (inscrit et ayant un mail) son bulletin" "envoi a chaque etudiant (inscrit et ayant un mail) son bulletin"
prefer_mail_perso = int(prefer_mail_perso) prefer_mail_perso = int(prefer_mail_perso)
sem = sco_formsemestre.get_formsemestre(self, formsemestre_id)
nt = self._getNotesCache().get_NotesTable( nt = self._getNotesCache().get_NotesTable(
self, formsemestre_id self, formsemestre_id
) # > get_etudids ) # > get_etudids
@ -2617,7 +2634,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
# Make each bulletin # Make each bulletin
nb_send = 0 nb_send = 0
for etudid in etudids: for etudid in etudids:
h, i = sco_bulletins.do_formsemestre_bulletinetud( h, _ = sco_bulletins.do_formsemestre_bulletinetud(
self, self,
formsemestre_id, formsemestre_id,
etudid, etudid,
@ -3190,7 +3207,6 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
def check_form_integrity(self, formation_id, fix=False, REQUEST=None): def check_form_integrity(self, formation_id, fix=False, REQUEST=None):
"debug" "debug"
log("check_form_integrity: formation_id=%s fix=%s" % (formation_id, fix)) log("check_form_integrity: formation_id=%s fix=%s" % (formation_id, fix))
F = self.formation_list(args={"formation_id": formation_id})[0]
ues = self.do_ue_list(args={"formation_id": formation_id}) ues = self.do_ue_list(args={"formation_id": formation_id})
bad = [] bad = []
for ue in ues: for ue in ues:
@ -3291,3 +3307,10 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
) )
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Support for legacy ScoDoc 7 API
# --------------------------------------------------------------------
security.declareProtected(ScoView, "do_moduleimpl_list")
do_moduleimpl_list = sco_moduleimpl.do_moduleimpl_list
security.declareProtected(ScoView, "do_moduleimpl_withmodule_list")
do_moduleimpl_withmodule_list = sco_moduleimpl.do_moduleimpl_withmodule_list

View File

@ -31,22 +31,29 @@
Chaque departement est géré par un ZScolar sous ZScoDoc. Chaque departement est géré par un ZScolar sous ZScoDoc.
""" """
import time, string, glob, re, inspect import time
import urllib, urllib2, cgi, xml
import datetime import datetime
import string
import glob
import re
import inspect
import urllib
import urllib2
import cgi
import xml
try:
from cStringIO import StringIO from cStringIO import StringIO
except:
from StringIO import StringIO
from zipfile import ZipFile from zipfile import ZipFile
import os.path, glob import os.path
import traceback import traceback
from email.mime.multipart import MIMEMultipart from email.MIMEMultipart import ( # pylint: disable=no-name-in-module,import-error
from email.mime.text import MIMEText MIMEMultipart,
from email.header import Header )
from email.mime.base import MIMEBase from email.MIMEText import MIMEText # pylint: disable=no-name-in-module,import-error
from email.MIMEBase import MIMEBase # pylint: disable=no-name-in-module,import-error
from email.Header import Header # pylint: disable=no-name-in-module,import-error
from email import Encoders # pylint: disable=no-name-in-module,import-error
from sco_zope import ( from sco_zope import (
ObjectManager, ObjectManager,
@ -60,30 +67,33 @@ from sco_zope import (
Globals, Globals,
) )
#
try: try:
import Products.ZPsycopgDA.DA as ZopeDA import Products.ZPsycopgDA.DA as ZopeDA
except: except:
import ZPsycopgDA.DA as ZopeDA # interp.py import ZPsycopgDA.DA as ZopeDA # interp.py
import sco_utils import sco_utils as scu
from sco_utils import ( import VERSION
SCO_DEFAULT_SQL_USERS_CNX,
SCO_ENCODING,
CUSTOM_HTML_HEADER_CNX,
SCO_USERS_LIST,
SCO_WEBSITE,
SCO_EXC_MAIL,
SCO_DEV_MAIL,
scodoc_html2txt,
VERSION,
SCODOC_CFG_DIR,
)
from sco_permissions import ScoView, ScoSuperAdmin
from sco_exceptions import AccessDenied
from notes_log import log from notes_log import log
import sco_find_etud import sco_find_etud
import sco_users import sco_users
from sco_permissions import (
ScoView,
ScoEnsView,
ScoImplement,
ScoChangeFormation,
ScoObservateur,
ScoEtudInscrit,
ScoEtudChangeGroups,
ScoEtudChangeAdr,
ScoEtudSupprAnnotations,
ScoEditAllEvals,
ScoEditAllNotes,
ScoEditFormationTags,
ScoEditApo,
ScoSuperAdmin,
)
from sco_exceptions import ScoValueError, ScoLockedFormError, ScoGenError, AccessDenied
class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Implicit): class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Implicit):
@ -129,7 +139,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
def _check_users_folder(self, REQUEST=None): def _check_users_folder(self, REQUEST=None):
"""Vérifie UserFolder et le crée s'il le faut""" """Vérifie UserFolder et le crée s'il le faut"""
try: try:
self.UsersDB _ = self.UsersDB
return "<!-- uf ok -->" return "<!-- uf ok -->"
except: except:
e = self._check_admin_perm(REQUEST) e = self._check_admin_perm(REQUEST)
@ -182,7 +192,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
pass pass
# add missing getAuthFailedMessage (bug in exUserFolder ?) # add missing getAuthFailedMessage (bug in exUserFolder ?)
try: try:
self.getAuthFailedMessage _ = self.getAuthFailedMessage
except: except:
log("adding getAuthFailedMessage to Zope install") log("adding getAuthFailedMessage to Zope install")
parent = self.aq_parent parent = self.aq_parent
@ -205,7 +215,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
da = ZopeDA.Connection( da = ZopeDA.Connection(
oid, oid,
"Cnx bd utilisateurs", "Cnx bd utilisateurs",
SCO_DEFAULT_SQL_USERS_CNX, scu.SCO_DEFAULT_SQL_USERS_CNX,
False, False,
check=1, check=1,
tilevel=2, tilevel=2,
@ -380,10 +390,10 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
def scodoc_top_html_header(self, REQUEST, page_title="ScoDoc"): def scodoc_top_html_header(self, REQUEST, page_title="ScoDoc"):
H = [ H = [
self._html_begin self._html_begin
% {"page_title": "ScoDoc: bienvenue", "encoding": SCO_ENCODING}, % {"page_title": "ScoDoc: bienvenue", "encoding": scu.SCO_ENCODING},
self._top_level_css, self._top_level_css,
"""</head><body class="gtrcontent" id="gtrcontent">""", """</head><body class="gtrcontent" id="gtrcontent">""",
CUSTOM_HTML_HEADER_CNX, scu.CUSTOM_HTML_HEADER_CNX,
] ]
return "\n".join(H) return "\n".join(H)
@ -496,7 +506,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
E. Viennet (Université Paris 13).</p> E. Viennet (Université Paris 13).</p>
</div> </div>
</div>""" </div>"""
% (SCO_WEBSITE,) % (scu.SCO_WEBSITE,)
) )
H.append("""</body></html>""") H.append("""</body></html>""")
@ -609,8 +619,8 @@ E. Viennet (Université Paris 13).</p>
<link HREF="/ScoDoc/static/css/scodoc.css" rel="stylesheet" type="text/css"/> <link HREF="/ScoDoc/static/css/scodoc.css" rel="stylesheet" type="text/css"/>
</head><body>%s""" % ( </head><body>%s""" % (
SCO_ENCODING, scu.SCO_ENCODING,
CUSTOM_HTML_HEADER_CNX, scu.CUSTOM_HTML_HEADER_CNX,
) )
security.declareProtected("View", "standard_html_footer") security.declareProtected("View", "standard_html_footer")
@ -622,8 +632,8 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
<p>Probl&egrave;mes et suggestions sur le logiciel: <a href="mailto:%s">%s</a></p> <p>Probl&egrave;mes et suggestions sur le logiciel: <a href="mailto:%s">%s</a></p>
<p><em>ScoDoc est un logiciel libre développé par Emmanuel Viennet.</em></p> <p><em>ScoDoc est un logiciel libre développé par Emmanuel Viennet.</em></p>
</body></html>""" % ( </body></html>""" % (
SCO_USERS_LIST, scu.SCO_USERS_LIST,
SCO_USERS_LIST, scu.SCO_USERS_LIST,
) )
# sendEmail is not used through the web # sendEmail is not used through the web
@ -661,15 +671,13 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
def standard_error_message( def standard_error_message(
self, self,
error_value=None, error_value=None,
error_message=None, error_message=None, # unused ?
error_type=None, error_type=None,
error_traceback=None, error_traceback=None,
error_tb=None, error_tb=None,
**kv **kv
): ):
"Recuperation des exceptions Zope" "Recuperation des exceptions Zope"
sco_exc_mail = SCO_EXC_MAIL # pylint: disable=unused-variable
sco_dev_mail = SCO_DEV_MAIL # pylint: disable=unused-variable
# neat (or should I say dirty ?) hack to get REQUEST # neat (or should I say dirty ?) hack to get REQUEST
# in fact, our caller (probably SimpleItem.py) has the REQUEST variable # in fact, our caller (probably SimpleItem.py) has the REQUEST variable
# that we'd like to use for our logs, but does not pass it as an argument. # that we'd like to use for our logs, but does not pass it as an argument.
@ -682,9 +690,6 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
# Authentication uses exceptions, pass them up # Authentication uses exceptions, pass them up
HTTP_X_FORWARDED_FOR = REQUEST.get("HTTP_X_FORWARDED_FOR", "") HTTP_X_FORWARDED_FOR = REQUEST.get("HTTP_X_FORWARDED_FOR", "")
if error_type == "LoginRequired": if error_type == "LoginRequired":
# raise 'LoginRequired', '' # copied from exuserFolder (beurk, old style exception...)
# if REQUEST:
# REQUEST.response.setStatus( 401, "Unauthorized") # ??????
log("LoginRequired from %s" % HTTP_X_FORWARDED_FOR) log("LoginRequired from %s" % HTTP_X_FORWARDED_FOR)
self.login_page = error_value self.login_page = error_value
return error_value return error_value
@ -695,6 +700,14 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
log("exception caught: %s" % error_type) log("exception caught: %s" % error_type)
log(traceback.format_exc()) log(traceback.format_exc())
params = {
"error_type": error_type,
"error_value": error_value,
"error_tb": error_tb,
"sco_exc_mail": scu.SCO_EXC_MAIL,
"sco_dev_mail": scu.SCO_DEV_MAIL,
}
if error_type == "ScoGenError": if error_type == "ScoGenError":
return "<p>" + str(error_value) + "</p>" return "<p>" + str(error_value) + "</p>"
elif error_type in ("ScoValueError", "FormatError"): elif error_type in ("ScoValueError", "FormatError"):
@ -732,12 +745,10 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
</p> </p>
</td></tr> </td></tr>
</table> """ </table> """
% vars() % params
) )
# display error traceback (? may open a security risk via xss attack ?) # display error traceback (? may open a security risk via xss attack ?)
# log('exc B') params["txt_html"] = self._report_request(REQUEST, fmt="html")
# pylint: disable=unused-variable
txt_html = self._report_request(REQUEST, fmt="html")
H.append( H.append(
"""<h4 class="scodoc">Zope Traceback (à envoyer par mail à <a href="mailto:%(sco_dev_mail)s">%(sco_dev_mail)s</a>)</h4><div style="background-color: rgb(153,153,204); border: 1px;"> """<h4 class="scodoc">Zope Traceback (à envoyer par mail à <a href="mailto:%(sco_dev_mail)s">%(sco_dev_mail)s</a>)</h4><div style="background-color: rgb(153,153,204); border: 1px;">
%(error_tb)s %(error_tb)s
@ -748,7 +759,7 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
<p>Merci de votre patience !</p> <p>Merci de votre patience !</p>
""" """
% vars() % params
) )
try: try:
H.append(self.standard_html_footer(REQUEST)) H.append(self.standard_html_footer(REQUEST))
@ -757,15 +768,14 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
pass pass
# --- Mail: # --- Mail:
# pylint: disable=unused-variable params["error_traceback_txt"] = scu.scodoc_html2txt(error_tb)
error_traceback_txt = scodoc_html2txt(error_tb)
txt = ( txt = (
""" """
ErrorType: %(error_type)s ErrorType: %(error_type)s
%(error_traceback_txt)s %(error_traceback_txt)s
""" """
% vars() % params
) )
self.send_debug_alert(txt, REQUEST=REQUEST) self.send_debug_alert(txt, REQUEST=REQUEST)
@ -776,25 +786,29 @@ ErrorType: %(error_type)s
def _report_request(self, REQUEST, fmt="txt"): def _report_request(self, REQUEST, fmt="txt"):
"""string describing current request for bug reports""" """string describing current request for bug reports"""
# pylint: disable=unused-variable
AUTHENTICATED_USER = REQUEST.get("AUTHENTICATED_USER", "")
dt = time.asctime()
URL = REQUEST.get("URL", "")
QUERY_STRING = REQUEST.get("QUERY_STRING", "") QUERY_STRING = REQUEST.get("QUERY_STRING", "")
if QUERY_STRING: if QUERY_STRING:
QUERY_STRING = "?" + QUERY_STRING QUERY_STRING = "?" + QUERY_STRING
METHOD = REQUEST.get("REQUEST_METHOD", "")
if fmt == "txt": if fmt == "txt":
REFERER = REQUEST.get("HTTP_REFERER", "") REFERER = REQUEST.get("HTTP_REFERER", "")
HTTP_USER_AGENT = REQUEST.get("HTTP_USER_AGENT", "") HTTP_USER_AGENT = REQUEST.get("HTTP_USER_AGENT", "")
else: else:
REFERER = "na" REFERER = "na"
HTTP_USER_AGENT = "na" HTTP_USER_AGENT = "na"
form = REQUEST.get("form", "")
HTTP_X_FORWARDED_FOR = REQUEST.get("HTTP_X_FORWARDED_FOR", "")
SCOVERSION = sco_utils.get_scodoc_version() or VERSION.SCOVERSION
params = dict(
AUTHENTICATED_USER=REQUEST.get("AUTHENTICATED_USER", ""),
dt=time.asctime(),
URL=REQUEST.get("URL", ""),
QUERY_STRING=QUERY_STRING,
METHOD=REQUEST.get("REQUEST_METHOD", ""),
REFERER=REFERER,
HTTP_USER_AGENT=HTTP_USER_AGENT,
form=REQUEST.get("form", ""),
HTTP_X_FORWARDED_FOR=REQUEST.get("HTTP_X_FORWARDED_FOR", ""),
svn_version=scu.get_svn_version(self.file_path),
SCOVERSION=VERSION.SCOVERSION,
)
txt = ( txt = (
""" """
Version: %(SCOVERSION)s Version: %(SCOVERSION)s
@ -808,7 +822,7 @@ Form: %(form)s
Origin: %(HTTP_X_FORWARDED_FOR)s Origin: %(HTTP_X_FORWARDED_FOR)s
Agent: %(HTTP_USER_AGENT)s Agent: %(HTTP_USER_AGENT)s
""" """
% vars() % params
) )
if fmt == "html": if fmt == "html":
txt = txt.replace("\n", "<br/>") txt = txt.replace("\n", "<br/>")
@ -820,7 +834,7 @@ Agent: %(HTTP_USER_AGENT)s
def send_debug_alert(self, txt, REQUEST=None): def send_debug_alert(self, txt, REQUEST=None):
"""Send an alert email (bug report) to ScoDoc developpers""" """Send an alert email (bug report) to ScoDoc developpers"""
if not SCO_EXC_MAIL: if not scu.SCO_EXC_MAIL:
log("send_debug_alert: email disabled") log("send_debug_alert: email disabled")
return return
if REQUEST: if REQUEST:
@ -829,13 +843,13 @@ Agent: %(HTTP_USER_AGENT)s
else: else:
URL = "send_debug_alert" URL = "send_debug_alert"
msg = MIMEMultipart() msg = MIMEMultipart()
subj = Header("[scodoc] exc %s" % URL, SCO_ENCODING) subj = Header("[scodoc] exc %s" % URL, scu.SCO_ENCODING)
msg["Subject"] = subj msg["Subject"] = subj
recipients = [SCO_EXC_MAIL] recipients = [scu.SCO_EXC_MAIL]
msg["To"] = " ,".join(recipients) msg["To"] = " ,".join(recipients)
msg["From"] = "scodoc-alert" msg["From"] = "scodoc-alert"
msg.epilogue = "" msg.epilogue = ""
msg.attach(MIMEText(txt, "plain", SCO_ENCODING)) msg.attach(MIMEText(txt, "plain", scu.SCO_ENCODING))
self.sendEmailFromException(msg) self.sendEmailFromException(msg)
log("Sent mail alert:\n" + txt) log("Sent mail alert:\n" + txt)
@ -909,7 +923,7 @@ Agent: %(HTTP_USER_AGENT)s
"""Liste de id de departements definis par create_dept.sh """Liste de id de departements definis par create_dept.sh
(fichiers depts/*.cfg) (fichiers depts/*.cfg)
""" """
filenames = glob.glob(SCODOC_CFG_DIR + "/config/depts/*.cfg") filenames = glob.glob(scu.SCODOC_VAR_DIR + "/config/depts/*.cfg")
ids = [os.path.split(os.path.splitext(f)[0])[1] for f in filenames] ids = [os.path.split(os.path.splitext(f)[0])[1] for f in filenames]
return ids return ids

View File

@ -87,7 +87,8 @@ import ZNotes
import ZAbsences import ZAbsences
import ZEntreprises import ZEntreprises
import ZScoUsers import ZScoUsers
import sco_modalites
# import sco_modalites
import ImportScolars import ImportScolars
import sco_abs import sco_abs
import sco_portal_apogee import sco_portal_apogee

View File

@ -139,7 +139,7 @@ class NotesTable:
- identdict: { etudid : ident } - identdict: { etudid : ident }
- sem : le formsemestre - sem : le formsemestre
get_table_moyennes_triees: [ (moy_gen, moy_ue1, moy_ue2, ... moy_ues, moy_mod1, ..., moy_modn, etudid) ] get_table_moyennes_triees: [ (moy_gen, moy_ue1, moy_ue2, ... moy_ues, moy_mod1, ..., moy_modn, etudid) ]
( toutes les valeurs sont soit des nombrs soit des chaines spéciales comme 'NA', 'NI'), ( toutes les valeurs sont soit des nombres soit des chaines spéciales comme 'NA', 'NI'),
incluant les UE de sport incluant les UE de sport
- bonus[etudid] : valeur du bonus "sport". - bonus[etudid] : valeur du bonus "sport".

View File

@ -33,8 +33,8 @@ Elle n'est pas utilisée pour les parcours, ni pour rien d'autre
(c'est donc un attribut "cosmétique"). (c'est donc un attribut "cosmétique").
""" """
from notesdb import * import notesdb as ndb
from sco_utils import * import sco_utils as scu
from notes_log import log from notes_log import log
from TrivialFormulator import TrivialFormulator, TF from TrivialFormulator import TrivialFormulator, TF
import sco_codes_parcours import sco_codes_parcours
@ -56,7 +56,7 @@ def group_sems_by_modalite(context, sems):
"""Given the list of fromsemestre, group them by modalite, """Given the list of fromsemestre, group them by modalite,
sorted in each one by semestre id and date sorted in each one by semestre id and date
""" """
sems_by_mod = DictDefault(defaultvalue=[]) sems_by_mod = scu.DictDefault(defaultvalue=[])
modalites = list_formsemestres_modalites(context, sems) modalites = list_formsemestres_modalites(context, sems)
for modalite in modalites: for modalite in modalites:
for sem in sems: for sem in sems:
@ -75,12 +75,12 @@ def group_sems_by_modalite(context, sems):
# ------ Low level interface (database) ------ # ------ Low level interface (database) ------
_modaliteEditor = EditableTable( _modaliteEditor = ndb.EditableTable(
"notes_form_modalites", "notes_form_modalites",
"form_modalite_id", "form_modalite_id",
("form_modalite_id", "modalite", "titre", "numero"), ("form_modalite_id", "modalite", "titre", "numero"),
sortkey="numero", sortkey="numero",
output_formators={"numero": int_null_is_zero}, output_formators={"numero": ndb.int_null_is_zero},
) )
@ -90,23 +90,23 @@ def do_modalite_list(context, *args, **kw):
return _modaliteEditor.list(cnx, *args, **kw) return _modaliteEditor.list(cnx, *args, **kw)
def do_modalite_create(context, args, REQUEST): def do_modalite_create(context, args, REQUEST=None):
"create a modalite" "create a modalite"
cnx = self.GetDBConnexion() cnx = context.GetDBConnexion()
r = _modaliteEditor.create(cnx, args) r = _modaliteEditor.create(cnx, args)
return r return r
def do_modalite_delete(context, oid, REQUEST=None): def do_modalite_delete(context, oid, REQUEST=None):
"delete a modalite" "delete a modalite"
cnx = self.GetDBConnexion() cnx = context.GetDBConnexion()
log("do_modalite_delete: form_modalite_id=%s" % oid) log("do_modalite_delete: form_modalite_id=%s" % oid)
_modaliteEditor.delete(cnx, oid) _modaliteEditor.delete(cnx, oid)
def do_modalite_edit(context, *args, **kw): def do_modalite_edit(context, *args, **kw):
"edit a modalite" "edit a modalite"
cnx = self.GetDBConnexion() cnx = context.GetDBConnexion()
# check # check
m = do_modalite_list(context, {"form_modalite_id": args[0]["form_modalite_id"]})[0] m = do_modalite_list(context, {"form_modalite_id": args[0]["form_modalite_id"]})[0]
_modaliteEditor.edit(cnx, *args, **kw) _modaliteEditor.edit(cnx, *args, **kw)

View File

@ -120,7 +120,9 @@ def do_moduleimpl_withmodule_list(
context, moduleimpl_id=None, formsemestre_id=None, module_id=None, REQUEST=None context, moduleimpl_id=None, formsemestre_id=None, module_id=None, REQUEST=None
): ):
"""Liste les moduleimpls et ajoute dans chacun le module correspondant """Liste les moduleimpls et ajoute dans chacun le module correspondant
Tri la liste par semestre/UE/numero_matiere/numero_module Tri la liste par semestre/UE/numero_matiere/numero_module.
Attention: Cette fonction fait partie de l'API ScoDoc 7 et est publiée.
""" """
args = locals() args = locals()
del args["context"] del args["context"]

View File

@ -132,7 +132,7 @@ class ScoFake:
code_specialite=None, code_specialite=None,
): ):
"""Crée une formation""" """Crée une formation"""
if acronyme == "": if not acronyme:
acronyme = "TEST" + str(random.randint(100000, 999999)) acronyme = "TEST" + str(random.randint(100000, 999999))
oid = self.context.do_formation_create(locals(), REQUEST=REQUEST) oid = self.context.do_formation_create(locals(), REQUEST=REQUEST)
oids = self.context.formation_list(args={"formation_id": oid}) oids = self.context.formation_list(args={"formation_id": oid})
@ -295,7 +295,14 @@ class ScoFake:
comment=comment, comment=comment,
) )
def setup_formation(self, nb_semestre=1, nb_ue_per_semestre=2, nb_module_per_ue=2): def setup_formation(
self,
nb_semestre=1,
nb_ue_per_semestre=2,
nb_module_per_ue=2,
acronyme=None,
titre=None,
):
"""Création d'une formation, avec UE, modules et évaluations. """Création d'une formation, avec UE, modules et évaluations.
Formation avec `nb_semestre` comportant chacun `nb_ue_per_semestre` UE Formation avec `nb_semestre` comportant chacun `nb_ue_per_semestre` UE
@ -304,7 +311,7 @@ class ScoFake:
Returns: Returns:
formation (dict), liste d'ue (dicts), liste de modules. formation (dict), liste d'ue (dicts), liste de modules.
""" """
f = self.create_formation(acronyme="") f = self.create_formation(acronyme=acronyme, titre=titre)
ue_list = [] ue_list = []
mod_list = [] mod_list = []
for semestre_id in range(1, nb_semestre + 1): for semestre_id in range(1, nb_semestre + 1):
@ -337,6 +344,9 @@ class ScoFake:
date_debut="01/01/2020", date_debut="01/01/2020",
date_fin="30/06/2020", date_fin="30/06/2020",
nb_evaluations_per_module=1, nb_evaluations_per_module=1,
titre=None,
responsables=["bach"],
modalite=None,
): ):
"""Création semestre, avec modules et évaluations.""" """Création semestre, avec modules et évaluations."""
sem = self.create_formsemestre( sem = self.create_formsemestre(
@ -344,6 +354,9 @@ class ScoFake:
semestre_id=semestre_id, semestre_id=semestre_id,
date_debut=date_debut, date_debut=date_debut,
date_fin=date_fin, date_fin=date_fin,
titre=titre,
responsables=responsables,
modalite=modalite,
) )
eval_list = [] eval_list = []
for mod in mod_list: for mod in mod_list:

View File

@ -14,6 +14,7 @@ Utiliser comme:
import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error
import sco_utils import sco_utils
import sco_codes_parcours import sco_codes_parcours
import sco_modalites
G = sco_fake_gen.ScoFake(context.Notes) # pylint: disable=undefined-variable G = sco_fake_gen.ScoFake(context.Notes) # pylint: disable=undefined-variable
G.verbose = False G.verbose = False
@ -21,8 +22,29 @@ G.verbose = False
# --- Création d'étudiants # --- Création d'étudiants
etuds = [G.create_etud(code_nip=None) for _ in range(10)] etuds = [G.create_etud(code_nip=None) for _ in range(10)]
# Deux modalités
# sco_modalites.do_modalite_create(context, {})
# --- Mise en place formation 4 semestres # --- Mise en place formation 4 semestres
f, ue_list, mod_list = G.setup_formation(nb_semestre=4) form_dut, ue_list, mod_list = G.setup_formation(
nb_semestre=4, titre="DUT RT", acronyme="DUT-RT"
)
# --- et une formation LP en 2 semestres
form_lp, ue_list_lp, mod_list_lp = G.setup_formation(
nb_semestre=2, titre="Licence Pro Ingéniérie Pédagogique", acronyme="LP IP"
)
# et un semestre que l'on ne va pas utiliser ici:
_, _ = G.setup_formsemestre(
form_lp,
mod_list_lp,
semestre_id=1,
date_debut="01/09/2021",
date_fin="15/01/2022",
titre="Licence Pro Ingéniérie Pédagogique",
responsables=["callas"],
modalite="FAP",
)
# --- Crée les 4 semestres et affecte des notes aléatoires # --- Crée les 4 semestres et affecte des notes aléatoires
sems, evals = [], [] sems, evals = [], []
@ -33,7 +55,12 @@ for semestre_id, date_debut, date_fin in [
(4, "16/01/2021", "30/06/2021"), (4, "16/01/2021", "30/06/2021"),
]: ]:
sem, eval_list = G.setup_formsemestre( sem, eval_list = G.setup_formsemestre(
f, mod_list, semestre_id=semestre_id, date_debut=date_debut, date_fin=date_fin form_dut,
mod_list,
semestre_id=semestre_id,
date_debut=date_debut,
date_fin=date_fin,
titre="BUT Réseaux et Tests",
) )
sems.append(sem) sems.append(sem)
evals.append(eval_list) # liste des listes d'evaluations evals.append(eval_list) # liste des listes d'evaluations
@ -55,7 +82,12 @@ for semestre_id, date_debut, date_fin in [
(4, "16/01/2023", "30/06/2023"), (4, "16/01/2023", "30/06/2023"),
]: ]:
sem, eval_list = G.setup_formsemestre( sem, eval_list = G.setup_formsemestre(
f, mod_list, semestre_id=semestre_id, date_debut=date_debut, date_fin=date_fin form_dut,
mod_list,
semestre_id=semestre_id,
date_debut=date_debut,
date_fin=date_fin,
titre="BUT Réseaux et Tests",
) )
sems.append(sem) sems.append(sem)
evals.append(eval_list) # liste des listes d'evaluations evals.append(eval_list) # liste des listes d'evaluations

View File

@ -0,0 +1,61 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
"""Test inscriptions / demissions / affichages notes
- Création 2 étudiants, puis formation en 1 semestre.
- Saisie de 2 notes
- Demission d'un étudiant
- bulletins
Utiliser comme:
scotests/scointeractive.sh -r TEST00 scotests/test_demissions.py
"""
import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error
import sco_utils
import sco_bulletins
G = sco_fake_gen.ScoFake(context.Notes) # pylint: disable=undefined-variable
G.verbose = False
# --- Création d'étudiants
etuds = [G.create_etud(code_nip=None) for _ in range(2)]
# --- Mise en place formation
form, ue_list, mod_list = G.setup_formation(
nb_semestre=1, titre="Essai 1", acronyme="ESS01"
)
# Mise en place semestre
sem, eval_list = G.setup_formsemestre(
form,
mod_list,
semestre_id=1,
date_debut="01/01/2021",
date_fin="31/12/2021",
titre="Essai démissions",
)
# Inscriptions
for etud in etuds:
G.inscrit_etudiant(sem, etud)
# Notes
G.set_etud_notes_sem(sem, eval_list, etuds)
# Bulletins
bul = sco_bulletins.formsemestre_bulletinetud_dict(
context.Notes, sem["formsemestre_id"], etuds[0]["etudid"]
)
print(bul["moy_gen"])
assert bul["ins"][0]["etat"] == "I"
# Démission:
context.doDemEtudiant(
etuds[0]["etudid"], sem["formsemestre_id"], event_date="15/12/2021"
)
bul = sco_bulletins.formsemestre_bulletinetud_dict(
context.Notes, sem["formsemestre_id"], etuds[0]["etudid"]
)
print(bul["moy_gen"])
assert bul["moy_gen"] == "NA"
assert bul["ins"][0]["etat"] == "D"