255 lines
9.3 KiB
Python
255 lines
9.3 KiB
Python
# -*- mode: python -*-
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""essai: ceci serait un module scodoc/sco_xxx.py
|
|
"""
|
|
|
|
import time
|
|
import thread
|
|
|
|
from scodoc_manager import sco_mgr
|
|
import app.scodoc.sco_utils as scu
|
|
from app.scodoc.notes_log import log
|
|
from app.scodoc.sco_exceptions import NoteProcessError
|
|
from app.scodoc import sco_cache
|
|
|
|
|
|
#
|
|
# Cache global: chaque instance, repérée par sa connexion db, a un cache
|
|
# qui est recréé à la demande
|
|
#
|
|
NOTES_CACHE_INST = {} # { db_cnx_string : CacheNotesTable instance }
|
|
|
|
CACHE_formsemestre_inscription = {}
|
|
CACHE_evaluations = {}
|
|
|
|
# cache notes evaluations
|
|
def get_evaluations_cache(context):
|
|
"""returns cache for evaluations"""
|
|
u = sco_mgr.get_db_uri()
|
|
if CACHE_evaluations.has_key(u):
|
|
return CACHE_evaluations[u]
|
|
else:
|
|
log("get_evaluations_cache: new simpleCache")
|
|
CACHE_evaluations[u] = sco_cache.simpleCache()
|
|
return CACHE_evaluations[u]
|
|
|
|
|
|
class CacheNotesTable:
|
|
"""gestion rudimentaire de cache pour les NotesTables"""
|
|
|
|
def __init__(self):
|
|
log("new CacheTable (id=%s)" % id(self))
|
|
#
|
|
self.lock = thread.allocate_lock()
|
|
self.owner_thread = None # thread owning this cache
|
|
self.nref = 0
|
|
# Cache des NotesTables
|
|
self.cache = {} # { formsemestre_id : NoteTable instance }
|
|
# Cache des classeur PDF (bulletins)
|
|
self.pdfcache = {} # { formsemestre_id : (filename, pdfdoc) }
|
|
# Listeners:
|
|
self.listeners = scu.DictDefault(
|
|
defaultvalue={}
|
|
) # {formsemestre_id : {listener_id : callback }}
|
|
|
|
def acquire(self):
|
|
"If this thread does not own the cache, acquire the lock"
|
|
if thread.get_ident() != self.owner_thread:
|
|
if self.lock.locked():
|
|
log(
|
|
"acquire: ident=%s waiting for lock" % thread.get_ident()
|
|
) # XXX debug
|
|
self.lock.acquire()
|
|
self.owner_thread = thread.get_ident()
|
|
if self.owner_thread is None: # bug catching
|
|
log("WARNING: None thread id !")
|
|
self.nref += 1
|
|
# log('nref=%d' % self.nref)
|
|
|
|
def release(self):
|
|
"Release the lock"
|
|
cur_owner_thread = self.owner_thread
|
|
# log('release: ident=%s (nref=%d)' % (thread.get_ident(), self.nref))
|
|
self.nref -= 1
|
|
if self.nref == 0:
|
|
self.lock.release()
|
|
self.owner_thread = None
|
|
# Debug:
|
|
if thread.get_ident() != cur_owner_thread:
|
|
log(
|
|
"WARNING: release: ident=%s != owner=%s nref=%d"
|
|
% (thread.get_ident(), cur_owner_thread, self.nref)
|
|
)
|
|
raise NoteProcessError("problem with notes cache")
|
|
|
|
def get_NotesTable(self, context, formsemestre_id): # >
|
|
import notes_table
|
|
|
|
try:
|
|
self.acquire()
|
|
if self.cache.has_key(formsemestre_id):
|
|
# log('cache hit %s (id=%s, thread=%s)'
|
|
# % (formsemestre_id, id(self), thread.get_ident()))
|
|
return self.cache[formsemestre_id]
|
|
else:
|
|
t0 = time.time()
|
|
nt = notes_table.NotesTable(context, formsemestre_id)
|
|
dt = time.time() - t0
|
|
self.cache[formsemestre_id] = nt
|
|
log(
|
|
"caching formsemestre_id=%s (id=%s) (%gs)"
|
|
% (formsemestre_id, id(self), dt)
|
|
)
|
|
return nt
|
|
finally:
|
|
self.release()
|
|
|
|
def get_cached_formsemestre_ids(self):
|
|
"List of currently cached formsemestre_id"
|
|
return self.cache.keys()
|
|
|
|
def inval_cache(self, context, formsemestre_id=None, pdfonly=False): # >
|
|
"expire cache pour un semestre (ou tous si pas d'argument)"
|
|
from app.scodoc import sco_parcours_dut
|
|
|
|
log(
|
|
"inval_cache, formsemestre_id=%s pdfonly=%s (id=%s)"
|
|
% (formsemestre_id, pdfonly, id(self)) # >
|
|
)
|
|
try:
|
|
self.acquire()
|
|
if not hasattr(self, "pdfcache"):
|
|
self.pdfcache = {} # fix for old zope instances...
|
|
if formsemestre_id is None:
|
|
# clear all caches
|
|
log("----- inval_cache: clearing all caches -----")
|
|
# log('cache was containing ' + str(self.cache.keys()))
|
|
# logCallStack() # >>> DEBUG <<<
|
|
if not pdfonly:
|
|
self.cache = {}
|
|
self.pdfcache = {}
|
|
self._call_all_listeners()
|
|
get_evaluations_cache(
|
|
context,
|
|
).inval_cache()
|
|
else:
|
|
# formsemestre_id modifié:
|
|
# on doit virer formsemestre_id et tous les semestres
|
|
# susceptibles d'utiliser des UE capitalisées de ce semestre.
|
|
to_trash = [
|
|
formsemestre_id
|
|
] + sco_parcours_dut.list_formsemestre_utilisateurs_uecap(
|
|
context, formsemestre_id
|
|
)
|
|
if not pdfonly:
|
|
for formsemestre_id in to_trash:
|
|
if self.cache.has_key(formsemestre_id):
|
|
log(
|
|
"delete %s from cache (id=%s)"
|
|
% (formsemestre_id, id(self))
|
|
)
|
|
del self.cache[formsemestre_id]
|
|
self._call_listeners(formsemestre_id)
|
|
get_evaluations_cache(
|
|
context,
|
|
).inval_cache()
|
|
|
|
for formsemestre_id in to_trash:
|
|
for (
|
|
cached_formsemestre_id,
|
|
cached_version,
|
|
) in self.pdfcache.keys():
|
|
if cached_formsemestre_id == formsemestre_id:
|
|
log(
|
|
"delete pdfcache[(%s,%s)]"
|
|
% (formsemestre_id, cached_version)
|
|
)
|
|
del self.pdfcache[(formsemestre_id, cached_version)]
|
|
finally:
|
|
self.release()
|
|
|
|
def store_bulletins_pdf(self, formsemestre_id, version, filename, pdfdoc):
|
|
"cache pdf data"
|
|
log(
|
|
"caching PDF formsemestre_id=%s version=%s (id=%s)"
|
|
% (formsemestre_id, version, id(self))
|
|
)
|
|
try:
|
|
self.acquire()
|
|
self.pdfcache[(formsemestre_id, version)] = (filename, pdfdoc)
|
|
finally:
|
|
self.release()
|
|
|
|
def get_bulletins_pdf(self, formsemestre_id, version):
|
|
"returns cached PDF, or None if not in the cache"
|
|
try:
|
|
self.acquire()
|
|
if not hasattr(self, "pdfcache"):
|
|
self.pdfcache = {} # fix for old zope instances...
|
|
r = self.pdfcache.get((formsemestre_id, version), None)
|
|
if r:
|
|
log(
|
|
"get_bulletins_pdf(%s): cache hit %s (id=%s, thread=%s)"
|
|
% (version, formsemestre_id, id(self), thread.get_ident())
|
|
)
|
|
return r
|
|
finally:
|
|
self.release()
|
|
|
|
def add_listener(self, callback, formsemestre_id, listener_id):
|
|
"""Add a "listener": a function called each time a formsemestre is modified"""
|
|
self.listeners[formsemestre_id][listener_id] = callback
|
|
|
|
def remove_listener(self, formsemestre_id, listener_id):
|
|
"""Remove a listener.
|
|
May raise exception if does not exists.
|
|
"""
|
|
del self.listeners[formsemestre_id][listener_id]
|
|
|
|
def _call_listeners(self, formsemestre_id):
|
|
for listener_id, callback in self.listeners[formsemestre_id].items():
|
|
callback(listener_id)
|
|
|
|
def _call_all_listeners(self):
|
|
for formsemestre_id in self.listeners:
|
|
self._call_listeners(formsemestre_id)
|
|
|
|
|
|
def get_notes_cache(context):
|
|
"returns CacheNotesTable instance for us"
|
|
u = sco_mgr.get_db_uri() # identifie le dept de facon unique
|
|
if not NOTES_CACHE_INST.has_key(u):
|
|
log("getNotesCache: creating cache for %s" % u)
|
|
NOTES_CACHE_INST[u] = CacheNotesTable()
|
|
return NOTES_CACHE_INST[u]
|
|
|
|
|
|
def inval_cache(
|
|
context, formsemestre_id=None, pdfonly=False, formsemestre_id_list=None
|
|
): # >
|
|
"expire cache pour un semestre (ou tous si pas d'argument)"
|
|
if formsemestre_id_list:
|
|
for formsemestre_id in formsemestre_id_list:
|
|
get_notes_cache(context).inval_cache(
|
|
context, formsemestre_id=formsemestre_id, pdfonly=pdfonly
|
|
)
|
|
# Affecte aussi cache inscriptions
|
|
get_formsemestre_inscription_cache(context).inval_cache(key=formsemestre_id)
|
|
else:
|
|
get_notes_cache(context).inval_cache(
|
|
context, formsemestre_id=formsemestre_id, pdfonly=pdfonly
|
|
)
|
|
# Affecte aussi cache inscriptions
|
|
get_formsemestre_inscription_cache(context).inval_cache(key=formsemestre_id)
|
|
|
|
|
|
# Cache inscriptions semestres
|
|
def get_formsemestre_inscription_cache(context, format=None):
|
|
u = sco_mgr.get_db_uri()
|
|
if CACHE_formsemestre_inscription.has_key(u):
|
|
return CACHE_formsemestre_inscription[u]
|
|
else:
|
|
log("get_formsemestre_inscription_cache: new simpleCache")
|
|
CACHE_formsemestre_inscription[u] = sco_cache.simpleCache()
|
|
return CACHE_formsemestre_inscription[u] |