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 calendar
from sco_zope import *
from sco_zope import * # pylint: disable=unused-wildcard-import
from sco_permissions import ScoEntrepriseView, ScoEntrepriseChange
# ---------------
from notes_log import log
from scolog import logdb
from sco_utils import SCO_ENCODING

181
ZNotes.py
View File

@ -27,16 +27,39 @@
"""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 notesdb import *
# from sco_utils import *
import sco_utils as scu
import notesdb as ndb
from notes_log import log, sendAlarm
import scolog
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 sco_excel
@ -64,6 +87,7 @@ import sco_edit_module
import sco_tag_module
import sco_bulletins
import sco_bulletins_pdf
import sco_compute_moy
import sco_recapcomplet
import sco_liste_notes
import sco_saisie_notes
@ -94,7 +118,8 @@ import sco_export_results
import sco_formsemestre_exterieurs
from sco_pdf import PDFLOCK
from notes_table import *
import notes_table
from notes_table import NOTES_CACHE_INST, CacheNotesTable
import VERSION
#
@ -231,7 +256,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
]
return "\n".join(H)
# 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:
security.declareProtected(ScoView, "formsemestre_status")
@ -347,15 +372,6 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
def index_html(self, REQUEST=None):
"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)
@ -386,7 +402,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
# --------------------------------------------------------------------
# --- Formations
_formationEditor = EditableTable(
_formationEditor = ndb.EditableTable(
"notes_formations",
"formation_id",
(
@ -473,7 +489,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
cnx = self.GetDBConnexion()
r = self._formationEditor.list(cnx, args=args)
# 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")
@ -517,9 +533,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(REQUEST.URL1)
else:
formation_id, junk, junk = self.formation_import_xml(
tf[2]["xmlfile"], REQUEST
)
formation_id, _, _ = self.formation_import_xml(tf[2]["xmlfile"], REQUEST)
return (
"\n".join(H)
@ -556,7 +570,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
return new_id, modules_old2new, ues_old2new
# --- UE
_ueEditor = EditableTable(
_ueEditor = ndb.EditableTable(
"notes_ue",
"ue_id",
(
@ -573,11 +587,11 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
"coefficient",
),
sortkey="numero",
input_formators={"type": int_null_is_zero},
input_formators={"type": ndb.int_null_is_zero},
output_formators={
"numero": int_null_is_zero,
"ects": float_null_is_null,
"coefficient": float_null_is_zero,
"numero": ndb.int_null_is_zero,
"ects": ndb.float_null_is_null,
"coefficient": ndb.float_null_is_zero,
},
)
@ -637,7 +651,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
)
if delete_validations:
log("deleting all validations of UE %s" % ue_id)
SimpleQuery(
ndb.SimpleQuery(
self,
"DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s",
{"ue_id": ue_id},
@ -648,12 +662,12 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
for mat in mats:
self.do_matiere_delete(mat["matiere_id"], REQUEST)
# delete uecoef and events
SimpleQuery(
ndb.SimpleQuery(
self,
"DELETE FROM notes_formsemestre_uecoef WHERE ue_id=%(ue_id)s",
{"ue_id": ue_id},
)
SimpleQuery(
ndb.SimpleQuery(
self, "DELETE FROM scolar_events WHERE ue_id=%(ue_id)s", {"ue_id": ue_id}
)
cnx = self.GetDBConnexion()
@ -684,12 +698,12 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
return self._ueEditor.list(cnx, *args, **kw)
# --- Matieres
_matiereEditor = EditableTable(
_matiereEditor = ndb.EditableTable(
"notes_matieres",
"matiere_id",
("matiere_id", "ue_id", "numero", "titre"),
sortkey="numero",
output_formators={"numero": int_null_is_zero},
output_formators={"numero": ndb.int_null_is_zero},
)
security.declareProtected(ScoChangeFormation, "do_matiere_create")
@ -758,7 +772,6 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
cnx = self.GetDBConnexion()
# check
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"]):
raise ScoLockedFormError()
# edit
@ -770,7 +783,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
def do_matiere_formation_id(self, matiere_id):
"get formation_id from matiere"
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
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",
{"matiere_id": matiere_id},
@ -779,7 +792,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
return res[0][0]
# --- Modules
_moduleEditor = EditableTable(
_moduleEditor = ndb.EditableTable(
"notes_modules",
"module_id",
(
@ -802,13 +815,13 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
),
sortkey="numero, code, titre",
output_formators={
"heures_cours": float_null_is_zero,
"heures_td": float_null_is_zero,
"heures_tp": float_null_is_zero,
"numero": int_null_is_zero,
"coefficient": float_null_is_zero,
"module_type": int_null_is_zero
#'ects' : float_null_is_null
"heures_cours": ndb.float_null_is_zero,
"heures_td": ndb.float_null_is_zero,
"heures_tp": ndb.float_null_is_zero,
"numero": ndb.int_null_is_zero,
"coefficient": ndb.float_null_is_zero,
"module_type": ndb.int_null_is_zero
#'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
(used in a locked formsemestre)
"""
r = SimpleDictFetch(
r = ndb.SimpleDictFetch(
self,
"""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
@ -943,7 +956,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
"""True if matiere should not be modified
(contains modules used in a locked formsemestre)
"""
r = SimpleDictFetch(
r = ndb.SimpleDictFetch(
self,
"""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
@ -959,7 +972,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
"""True if module should not be modified
(contains modules used in a locked formsemestre)
"""
r = SimpleDictFetch(
r = ndb.SimpleDictFetch(
self,
"""SELECT ue.* FROM notes_ue ue, notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi
WHERE ue.ue_id = mod.ue_id
@ -1100,7 +1113,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
args[argname] = L[argname]
sems = sco_formsemestre.do_formsemestre_list(self, args=args)
# 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")
@ -1115,8 +1128,8 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
if formsemestre_id:
args["formsemestre_id"] = formsemestre_id
if REQUEST:
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE)
doc = jaxml.XML_document(encoding=SCO_ENCODING)
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
doc = jaxml.XML_document(encoding=scu.SCO_ENCODING)
doc.formsemestrelist()
for sem in sco_formsemestre.do_formsemestre_list(self, args=args):
doc._push()
@ -1497,8 +1510,8 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
self, moduleimpl_id=moduleimpl_id
)[0]
sem = sco_formsemestre.get_formsemestre(self, M["formsemestre_id"])
debut_sem = DateDMYtoISO(sem["date_debut"])
fin_sem = DateDMYtoISO(sem["date_fin"])
debut_sem = ndb.DateDMYtoISO(sem["date_debut"])
fin_sem = ndb.DateDMYtoISO(sem["date_fin"])
list_insc = sco_moduleimpl.do_moduleimpl_listeetuds(self, moduleimpl_id)
T = []
@ -1551,7 +1564,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
rows=T,
html_class="table_leftalign",
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"],
preferences=self.get_preferences(),
)
@ -1656,15 +1669,15 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
sem_ens[ensd["ens_id"]]["mods"].append(mod)
# compte les absences ajoutées par chacun dans tout le semestre
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
for ens in sem_ens:
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",
{
"authenticated_user": ens,
"formsemestre_id": formsemestre_id,
"date_debut": DateDMYtoISO(sem["date_debut"]),
"date_fin": DateDMYtoISO(sem["date_fin"]),
"date_debut": ndb.DateDMYtoISO(sem["date_debut"]),
"date_fin": ndb.DateDMYtoISO(sem["date_fin"]),
},
)
@ -1700,7 +1713,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
rows=sem_ens_list,
html_sortable=True,
html_class="table_leftalign",
filename=make_filename("Enseignants-" + sem["titreannee"]),
filename=scu.make_filename("Enseignants-" + sem["titreannee"]),
html_title=self.html_sem_header(
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):
"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
ok = False
for ens in M["ens"]:
@ -1731,7 +1744,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
)
# --- Gestion des inscriptions aux modules
_formsemestre_inscriptionEditor = EditableTable(
_formsemestre_inscriptionEditor = ndb.EditableTable(
"notes_formsemestre_inscription",
"formsemestre_inscription_id",
("formsemestre_inscription_id", "etudid", "formsemestre_id", "etat", "etape"),
@ -1937,7 +1950,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
insem = insem[0]
# -- desinscription de tous les modules
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
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",
{"etudid": etudid, "formsemestre_id": formsemestre_id},
@ -2049,7 +2062,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
)
# --- Evaluations
_evaluationEditor = EditableTable(
_evaluationEditor = ndb.EditableTable(
"notes_evaluation",
"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
output_formators={
"jour": DateISOtoDMY,
"jour": ndb.DateISOtoDMY,
"visibulletin": str,
"publish_incomplete": str,
"numero": int_null_is_zero,
"numero": ndb.int_null_is_zero,
},
input_formators={
"jour": DateDMYtoISO,
"heure_debut": TimetoISO8601, # converti par do_evaluation_list
"heure_fin": TimetoISO8601, # converti par do_evaluation_list
"jour": ndb.DateDMYtoISO,
"heure_debut": ndb.TimetoISO8601, # converti par do_evaluation_list
"heure_fin": ndb.TimetoISO8601, # converti par do_evaluation_list
"visibulletin": int,
"publish_incomplete": int,
},
@ -2144,9 +2157,15 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
)
if args["jour"]:
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:
if (DateDMYtoISO(e["jour"]), TimetoISO8601(e["heure_debut"])) > t:
if (
ndb.DateDMYtoISO(e["jour"]),
ndb.TimetoISO8601(e["heure_debut"]),
) > t:
next_eval = e
break
if next_eval:
@ -2218,8 +2237,8 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
date_debut = datetime.date(y, m, d)
d, m, y = [int(x) for x in sem["date_fin"].split("/")]
date_fin = datetime.date(y, m, d)
# passe par DateDMYtoISO pour avoir date pivot
y, m, d = [int(x) for x in DateDMYtoISO(jour).split("-")]
# passe par ndb.DateDMYtoISO pour avoir date pivot
y, m, d = [int(x) for x in ndb.DateDMYtoISO(jour).split("-")]
jour = datetime.date(y, m, d)
if (jour > date_fin) or (jour < date_debut):
raise ScoValueError(
@ -2232,7 +2251,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
args["heure_fin"] = heure_fin
if jour and ((not heure_debut) or (not heure_fin)):
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)):
raise ScoValueError("Heures de l'évaluation incohérentes !")
@ -2332,11 +2351,11 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
8, 00
) # au cas ou pas d'heure (note externe?)
heure_fin_dt = e["heure_fin"] or datetime.time(8, 00)
e["heure_debut"] = TimefromISO8601(e["heure_debut"])
e["heure_fin"] = TimefromISO8601(e["heure_fin"])
e["jouriso"] = DateDMYtoISO(e["jour"])
e["heure_debut"] = ndb.TimefromISO8601(e["heure_debut"])
e["heure_fin"] = ndb.TimefromISO8601(e["heure_fin"])
e["jouriso"] = ndb.DateDMYtoISO(e["jour"])
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:
m = 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):
"list evaluations in this formsemestre"
cnx = self.GetDBConnexion()
mods = sco_moduleimpl.do_moduleimpl_list(self, formsemestre_id=formsemestre_id)
evals = []
for mod in mods:
@ -2528,7 +2546,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
if r != None:
return r
cnx = self.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
cond = " where evaluation_id=%(evaluation_id)s"
if by_uid:
cond += " and uid=%(by_uid)s"
@ -2541,7 +2559,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
d = {}
if filter_suppressed:
for x in res:
if x["value"] != NOTES_SUPPRESS:
if x["value"] != scu.NOTES_SUPPRESS:
d[x["etudid"]] = x
else:
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(
self, formsemestre_id, REQUEST, version=version
)
return sendPDFFile(REQUEST, pdfdoc, filename)
return scu.sendPDFFile(REQUEST, pdfdoc, filename)
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(
self, etudid, REQUEST, version=version
)
return sendPDFFile(REQUEST, pdfdoc, filename)
return scu.sendPDFFile(REQUEST, pdfdoc, filename)
security.declareProtected(ScoView, "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"
prefer_mail_perso = int(prefer_mail_perso)
sem = sco_formsemestre.get_formsemestre(self, formsemestre_id)
nt = self._getNotesCache().get_NotesTable(
self, formsemestre_id
) # > get_etudids
@ -2617,7 +2634,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
# Make each bulletin
nb_send = 0
for etudid in etudids:
h, i = sco_bulletins.do_formsemestre_bulletinetud(
h, _ = sco_bulletins.do_formsemestre_bulletinetud(
self,
formsemestre_id,
etudid,
@ -3190,7 +3207,6 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl
def check_form_integrity(self, formation_id, fix=False, REQUEST=None):
"debug"
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})
bad = []
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.
"""
import time, string, glob, re, inspect
import urllib, urllib2, cgi, xml
import time
import datetime
import string
import glob
import re
import inspect
import urllib
import urllib2
import cgi
import xml
try:
from cStringIO import StringIO
except:
from StringIO import StringIO
from cStringIO import StringIO
from zipfile import ZipFile
import os.path, glob
import os.path
import traceback
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header
from email.mime.base import MIMEBase
from email.MIMEMultipart import ( # pylint: disable=no-name-in-module,import-error
MIMEMultipart,
)
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 (
ObjectManager,
@ -60,30 +67,33 @@ from sco_zope import (
Globals,
)
#
try:
import Products.ZPsycopgDA.DA as ZopeDA
except:
import ZPsycopgDA.DA as ZopeDA # interp.py
import sco_utils
from sco_utils import (
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
import sco_utils as scu
import VERSION
from notes_log import log
import sco_find_etud
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):
@ -129,7 +139,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
def _check_users_folder(self, REQUEST=None):
"""Vérifie UserFolder et le crée s'il le faut"""
try:
self.UsersDB
_ = self.UsersDB
return "<!-- uf ok -->"
except:
e = self._check_admin_perm(REQUEST)
@ -182,7 +192,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
pass
# add missing getAuthFailedMessage (bug in exUserFolder ?)
try:
self.getAuthFailedMessage
_ = self.getAuthFailedMessage
except:
log("adding getAuthFailedMessage to Zope install")
parent = self.aq_parent
@ -205,7 +215,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
da = ZopeDA.Connection(
oid,
"Cnx bd utilisateurs",
SCO_DEFAULT_SQL_USERS_CNX,
scu.SCO_DEFAULT_SQL_USERS_CNX,
False,
check=1,
tilevel=2,
@ -380,10 +390,10 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
def scodoc_top_html_header(self, REQUEST, page_title="ScoDoc"):
H = [
self._html_begin
% {"page_title": "ScoDoc: bienvenue", "encoding": SCO_ENCODING},
% {"page_title": "ScoDoc: bienvenue", "encoding": scu.SCO_ENCODING},
self._top_level_css,
"""</head><body class="gtrcontent" id="gtrcontent">""",
CUSTOM_HTML_HEADER_CNX,
scu.CUSTOM_HTML_HEADER_CNX,
]
return "\n".join(H)
@ -496,7 +506,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp
E. Viennet (Université Paris 13).</p>
</div>
</div>"""
% (SCO_WEBSITE,)
% (scu.SCO_WEBSITE,)
)
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"/>
</head><body>%s""" % (
SCO_ENCODING,
CUSTOM_HTML_HEADER_CNX,
scu.SCO_ENCODING,
scu.CUSTOM_HTML_HEADER_CNX,
)
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><em>ScoDoc est un logiciel libre développé par Emmanuel Viennet.</em></p>
</body></html>""" % (
SCO_USERS_LIST,
SCO_USERS_LIST,
scu.SCO_USERS_LIST,
scu.SCO_USERS_LIST,
)
# 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(
self,
error_value=None,
error_message=None,
error_message=None, # unused ?
error_type=None,
error_traceback=None,
error_tb=None,
**kv
):
"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
# 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.
@ -682,9 +690,6 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
# Authentication uses exceptions, pass them up
HTTP_X_FORWARDED_FOR = REQUEST.get("HTTP_X_FORWARDED_FOR", "")
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)
self.login_page = 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(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":
return "<p>" + str(error_value) + "</p>"
elif error_type in ("ScoValueError", "FormatError"):
@ -732,12 +745,10 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
</p>
</td></tr>
</table> """
% vars()
% params
)
# display error traceback (? may open a security risk via xss attack ?)
# log('exc B')
# pylint: disable=unused-variable
txt_html = self._report_request(REQUEST, fmt="html")
params["txt_html"] = self._report_request(REQUEST, fmt="html")
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;">
%(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>
"""
% vars()
% params
)
try:
H.append(self.standard_html_footer(REQUEST))
@ -757,15 +768,14 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
pass
# --- Mail:
# pylint: disable=unused-variable
error_traceback_txt = scodoc_html2txt(error_tb)
params["error_traceback_txt"] = scu.scodoc_html2txt(error_tb)
txt = (
"""
ErrorType: %(error_type)s
%(error_traceback_txt)s
"""
% vars()
% params
)
self.send_debug_alert(txt, REQUEST=REQUEST)
@ -776,25 +786,29 @@ ErrorType: %(error_type)s
def _report_request(self, REQUEST, fmt="txt"):
"""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", "")
if QUERY_STRING:
QUERY_STRING = "?" + QUERY_STRING
METHOD = REQUEST.get("REQUEST_METHOD", "")
if fmt == "txt":
REFERER = REQUEST.get("HTTP_REFERER", "")
HTTP_USER_AGENT = REQUEST.get("HTTP_USER_AGENT", "")
else:
REFERER = "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 = (
"""
Version: %(SCOVERSION)s
@ -808,7 +822,7 @@ Form: %(form)s
Origin: %(HTTP_X_FORWARDED_FOR)s
Agent: %(HTTP_USER_AGENT)s
"""
% vars()
% params
)
if fmt == "html":
txt = txt.replace("\n", "<br/>")
@ -820,7 +834,7 @@ Agent: %(HTTP_USER_AGENT)s
def send_debug_alert(self, txt, REQUEST=None):
"""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")
return
if REQUEST:
@ -829,13 +843,13 @@ Agent: %(HTTP_USER_AGENT)s
else:
URL = "send_debug_alert"
msg = MIMEMultipart()
subj = Header("[scodoc] exc %s" % URL, SCO_ENCODING)
subj = Header("[scodoc] exc %s" % URL, scu.SCO_ENCODING)
msg["Subject"] = subj
recipients = [SCO_EXC_MAIL]
recipients = [scu.SCO_EXC_MAIL]
msg["To"] = " ,".join(recipients)
msg["From"] = "scodoc-alert"
msg.epilogue = ""
msg.attach(MIMEText(txt, "plain", SCO_ENCODING))
msg.attach(MIMEText(txt, "plain", scu.SCO_ENCODING))
self.sendEmailFromException(msg)
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
(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]
return ids

View File

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

View File

@ -139,7 +139,7 @@ class NotesTable:
- identdict: { etudid : ident }
- sem : le formsemestre
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
- 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").
"""
from notesdb import *
from sco_utils import *
import notesdb as ndb
import sco_utils as scu
from notes_log import log
from TrivialFormulator import TrivialFormulator, TF
import sco_codes_parcours
@ -56,7 +56,7 @@ def group_sems_by_modalite(context, sems):
"""Given the list of fromsemestre, group them by modalite,
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)
for modalite in modalites:
for sem in sems:
@ -75,12 +75,12 @@ def group_sems_by_modalite(context, sems):
# ------ Low level interface (database) ------
_modaliteEditor = EditableTable(
_modaliteEditor = ndb.EditableTable(
"notes_form_modalites",
"form_modalite_id",
("form_modalite_id", "modalite", "titre", "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)
def do_modalite_create(context, args, REQUEST):
def do_modalite_create(context, args, REQUEST=None):
"create a modalite"
cnx = self.GetDBConnexion()
cnx = context.GetDBConnexion()
r = _modaliteEditor.create(cnx, args)
return r
def do_modalite_delete(context, oid, REQUEST=None):
"delete a modalite"
cnx = self.GetDBConnexion()
cnx = context.GetDBConnexion()
log("do_modalite_delete: form_modalite_id=%s" % oid)
_modaliteEditor.delete(cnx, oid)
def do_modalite_edit(context, *args, **kw):
"edit a modalite"
cnx = self.GetDBConnexion()
cnx = context.GetDBConnexion()
# check
m = do_modalite_list(context, {"form_modalite_id": args[0]["form_modalite_id"]})[0]
_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
):
"""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()
del args["context"]

View File

@ -132,7 +132,7 @@ class ScoFake:
code_specialite=None,
):
"""Crée une formation"""
if acronyme == "":
if not acronyme:
acronyme = "TEST" + str(random.randint(100000, 999999))
oid = self.context.do_formation_create(locals(), REQUEST=REQUEST)
oids = self.context.formation_list(args={"formation_id": oid})
@ -295,7 +295,14 @@ class ScoFake:
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.
Formation avec `nb_semestre` comportant chacun `nb_ue_per_semestre` UE
@ -304,7 +311,7 @@ class ScoFake:
Returns:
formation (dict), liste d'ue (dicts), liste de modules.
"""
f = self.create_formation(acronyme="")
f = self.create_formation(acronyme=acronyme, titre=titre)
ue_list = []
mod_list = []
for semestre_id in range(1, nb_semestre + 1):
@ -337,6 +344,9 @@ class ScoFake:
date_debut="01/01/2020",
date_fin="30/06/2020",
nb_evaluations_per_module=1,
titre=None,
responsables=["bach"],
modalite=None,
):
"""Création semestre, avec modules et évaluations."""
sem = self.create_formsemestre(
@ -344,6 +354,9 @@ class ScoFake:
semestre_id=semestre_id,
date_debut=date_debut,
date_fin=date_fin,
titre=titre,
responsables=responsables,
modalite=modalite,
)
eval_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 sco_utils
import sco_codes_parcours
import sco_modalites
G = sco_fake_gen.ScoFake(context.Notes) # pylint: disable=undefined-variable
G.verbose = False
@ -21,8 +22,29 @@ G.verbose = False
# --- Création d'étudiants
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
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
sems, evals = [], []
@ -33,7 +55,12 @@ for semestre_id, date_debut, date_fin in [
(4, "16/01/2021", "30/06/2021"),
]:
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)
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"),
]:
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)
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"