2020-09-26 16:19:37 +02:00
|
|
|
# -*- mode: python -*-
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
##############################################################################
|
|
|
|
#
|
|
|
|
# Gestion scolarite IUT
|
|
|
|
#
|
2021-01-01 17:51:08 +01:00
|
|
|
# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
|
2020-09-26 16:19:37 +02:00
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
#
|
|
|
|
# Emmanuel Viennet emmanuel.viennet@viennet.net
|
|
|
|
#
|
|
|
|
##############################################################################
|
|
|
|
|
2021-07-15 15:05:54 +02:00
|
|
|
"""Gestion des caches
|
2020-09-26 16:19:37 +02:00
|
|
|
|
2021-07-15 15:05:54 +02:00
|
|
|
Ré-écrite pour ScoDoc8, utilise flask_caching et memcached
|
|
|
|
|
|
|
|
ScoDoc est maintenant multiprocessus / mono-thread, avec un cache en mémoire partagé.
|
2020-09-26 16:19:37 +02:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
2021-07-15 15:05:54 +02:00
|
|
|
# API ScoDoc8 pour les caches:
|
|
|
|
# sco_core.get_notes_cache(context).get_NotesTable(context, formsemestre_id)
|
|
|
|
# => sco_cache.NotesTableCache.get(formsemestre_id)
|
|
|
|
#
|
|
|
|
# sco_core.inval_cache(context, formsemestre_id=None, pdfonly=False, formsemestre_id_list=None)
|
|
|
|
# => deprecated, sco_cache.inval_cache(formsemestre_id=None, pdfonly=False, formsemestre_id_list=None)
|
|
|
|
#
|
|
|
|
#
|
|
|
|
# Nouvelles fonctions:
|
|
|
|
# sco_cache.NotesTableCache.delete(formsemestre_id)
|
|
|
|
# sco_cache.NotesTableCache.delete_many(formsemestre_id_list)
|
|
|
|
#
|
|
|
|
# Bulletins PDF:
|
|
|
|
# sco_cache.PDFBulCache.get(formsemestre_id, version)
|
|
|
|
# sco_cache.PDFBulCache.set(formsemestre_id, version, filename, pdfdoc)
|
|
|
|
# sco_cache.PDFBulCache.delete(formsemestre_id) suppr. toutes les versions
|
|
|
|
|
|
|
|
# Evaluations:
|
|
|
|
# sco_cache.EvaluationCache.get(evaluation_id), set(evaluation_id, value), delete(evaluation_id),
|
|
|
|
#
|
|
|
|
|
|
|
|
from collections import defaultdict
|
|
|
|
|
|
|
|
from flask import g
|
|
|
|
from flask_caching import Cache
|
|
|
|
|
|
|
|
from app.scodoc.notes_log import log
|
|
|
|
|
|
|
|
CACHE = Cache(config={"CACHE_TYPE": "MemcachedCache"}) # XXX TODO: configuration file
|
|
|
|
|
|
|
|
|
|
|
|
class ScoDocCache:
|
|
|
|
"""Cache for ScoDoc objects.
|
|
|
|
keys are prefixed by the current departement.
|
|
|
|
"""
|
|
|
|
|
|
|
|
prefix = ""
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def _get_key(cls, oid):
|
|
|
|
return cls.prefix + g.scodoc_dept + oid
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get(cls, oid):
|
|
|
|
"""Returns cached evaluation, or None"""
|
|
|
|
return CACHE.get(cls._get_key(oid))
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def set(cls, oid, value):
|
|
|
|
"""Store evaluation"""
|
|
|
|
return CACHE.set(cls._get_key(oid), value)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def delete(cls, oid):
|
|
|
|
"""Remove from cache"""
|
|
|
|
CACHE.delete(cls._get_key(oid))
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def delete_many(cls, oids):
|
|
|
|
"""Remove multiple keys at once"""
|
|
|
|
CACHE.delete_many(oids)
|
|
|
|
|
|
|
|
|
|
|
|
class EvaluationCache(ScoDocCache):
|
|
|
|
"Cache for evaluations"
|
|
|
|
prefix = "EVAL"
|
|
|
|
|
|
|
|
|
|
|
|
class NotesTableCache(ScoDocCache):
|
|
|
|
prefix = "NT"
|
|
|
|
listeners = defaultdict(list) # oid : list of callback functions
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def add_listener(cls, func, oid):
|
|
|
|
"""Add a function which will be called each time and object is modified:
|
|
|
|
the function will be called as func(oid)
|
|
|
|
"""
|
|
|
|
cls.listeners[oid].append(func)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def delete(cls, oid):
|
|
|
|
for listener in cls.listeners[oid]:
|
|
|
|
listener(oid)
|
|
|
|
super().delete(oid)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def delete_many(cls, oids):
|
|
|
|
for oid in oids:
|
|
|
|
for listener in cls.listeners[oid]:
|
|
|
|
listener(oid)
|
|
|
|
super().delete_many(oids)
|
|
|
|
|
|
|
|
|
|
|
|
# XXX OBSOLETE A ENLEVER XXX
|
|
|
|
class simpleCache:
|
|
|
|
"""A simple cache wich cache data for a most "duration" seconds."""
|
|
|
|
|
2020-09-26 16:19:37 +02:00
|
|
|
def __init__(self):
|
2021-01-01 18:40:47 +01:00
|
|
|
self.cache = {}
|
2020-09-26 16:19:37 +02:00
|
|
|
self.inval_cache() # >
|
|
|
|
|
|
|
|
def inval_cache(self, key=None): # >
|
|
|
|
if key:
|
|
|
|
if key in self.cache:
|
|
|
|
del self.cache[key]
|
|
|
|
else:
|
|
|
|
# clear all entries
|
|
|
|
self.cache = {} # key : data
|
|
|
|
|
|
|
|
def set(self, key, data):
|
|
|
|
self.cache[key] = data
|
|
|
|
|
|
|
|
def get(self, key):
|
|
|
|
"""returns None if not in cache"""
|
|
|
|
return self.cache.get(key, None)
|
|
|
|
|
|
|
|
|
|
|
|
class expiringCache(simpleCache):
|
|
|
|
"""A simple cache wich cache data for a most "duration" seconds.
|
|
|
|
|
2021-01-01 18:40:47 +01:00
|
|
|
This is used for users (which may be updated from external
|
2020-09-26 16:19:37 +02:00
|
|
|
information systems)
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, max_validity=60):
|
|
|
|
simpleCache.__init__(self)
|
|
|
|
self.max_validity = max_validity
|
|
|
|
|
|
|
|
def set(self, key, data):
|
|
|
|
simpleCache.set(self, key, (data, time.time()))
|
|
|
|
|
|
|
|
def get(self, key):
|
|
|
|
info = simpleCache.get(self, key)
|
|
|
|
if info:
|
|
|
|
data, t = info
|
|
|
|
if time.time() - t < self.max_validity:
|
|
|
|
return data
|
|
|
|
else:
|
|
|
|
# expired
|
|
|
|
self.inval_cache(key) # >
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return None # not in cache
|