MonScoDocEssai/app/scodoc/sco_cache.py

179 lines
5.1 KiB
Python

# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Emmanuel Viennet emmanuel.viennet@viennet.net
#
##############################################################################
"""Gestion des caches
Ré-écrite pour ScoDoc8, utilise flask_caching et memcached
ScoDoc est maintenant multiprocessus / mono-thread, avec un cache en mémoire partagé.
"""
# 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."""
def __init__(self):
self.cache = {}
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.
This is used for users (which may be updated from external
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