diff options
Diffstat (limited to 'keyedcache')
33 files changed, 1348 insertions, 0 deletions
diff --git a/keyedcache/__init__.py b/keyedcache/__init__.py new file mode 100644 index 00000000..d7dfe9ec --- /dev/null +++ b/keyedcache/__init__.py @@ -0,0 +1,329 @@ +"""A full cache system written on top of Django's rudimentary one.""" + +from django.conf import settings +from django.core.cache import cache +from django.utils.encoding import smart_str +from django.utils.hashcompat import md5_constructor +from keyedcache.utils import is_string_like, is_list_or_tuple +import cPickle as pickle +import logging +import types + +log = logging.getLogger('keyedcache') + +CACHED_KEYS = {} +CACHE_CALLS = 0 +CACHE_HITS = 0 +KEY_DELIM = "::" +REQUEST_CACHE = {'enabled' : False} +try: + CACHE_PREFIX = settings.CACHE_PREFIX +except AttributeError: + CACHE_PREFIX = str(settings.SITE_ID) + log.warn("No CACHE_PREFIX found in settings, using SITE_ID. Please update your settings to add a CACHE_PREFIX") + +try: + CACHE_TIMEOUT = settings.CACHE_TIMEOUT +except AttributeError: + CACHE_TIMEOUT = 0 + log.warn("No CACHE_TIMEOUT found in settings, so we used 0, disabling the cache system. Please update your settings to add a CACHE_TIMEOUT and avoid this warning.") + +_CACHE_ENABLED = CACHE_TIMEOUT > 0 + +class CacheWrapper(object): + def __init__(self, val, inprocess=False): + self.val = val + self.inprocess = inprocess + + def __str__(self): + return str(self.val) + + def __repr__(self): + return repr(self.val) + + def wrap(cls, obj): + if isinstance(obj, cls): + return obj + else: + return cls(obj) + + wrap = classmethod(wrap) + +class MethodNotFinishedError(Exception): + def __init__(self, f): + self.func = f + + +class NotCachedError(Exception): + def __init__(self, k): + self.key = k + +class CacheNotRespondingError(Exception): + pass + +def cache_delete(*keys, **kwargs): + removed = [] + if cache_enabled(): + global CACHED_KEYS + log.debug('cache_delete') + children = kwargs.pop('children',False) + + if (keys or kwargs): + key = cache_key(*keys, **kwargs) + + if CACHED_KEYS.has_key(key): + del CACHED_KEYS[key] + removed.append(key) + + cache.delete(key) + + if children: + key = key + KEY_DELIM + children = [x for x in CACHED_KEYS.keys() if x.startswith(key)] + for k in children: + del CACHED_KEYS[k] + cache.delete(k) + removed.append(k) + else: + key = "All Keys" + deleteneeded = _cache_flush_all() + + removed = CACHED_KEYS.keys() + + if deleteneeded: + for k in CACHED_KEYS: + cache.delete(k) + + CACHED_KEYS = {} + + if removed: + log.debug("Cache delete: %s", removed) + else: + log.debug("No cached objects to delete for %s", key) + + return removed + + +def cache_delete_function(func): + return cache_delete(['func', func.__name__, func.__module__], children=True) + +def cache_enabled(): + global _CACHE_ENABLED + return _CACHE_ENABLED + +def cache_enable(state=True): + global _CACHE_ENABLED + _CACHE_ENABLED=state + +def _cache_flush_all(): + if is_memcached_backend(): + cache._cache.flush_all() + return False + return True + +def cache_function(length=CACHE_TIMEOUT): + """ + A variant of the snippet posted by Jeff Wheeler at + http://www.djangosnippets.org/snippets/109/ + + Caches a function, using the function and its arguments as the key, and the return + value as the value saved. It passes all arguments on to the function, as + it should. + + The decorator itself takes a length argument, which is the number of + seconds the cache will keep the result around. + + It will put a temp value in the cache while the function is + processing. This should not matter in most cases, but if the app is using + threads, you won't be able to get the previous value, and will need to + wait until the function finishes. If this is not desired behavior, you can + remove the first two lines after the ``else``. + """ + def decorator(func): + def inner_func(*args, **kwargs): + if not cache_enabled(): + value = func(*args, **kwargs) + + else: + try: + value = cache_get('func', func.__name__, func.__module__, args, kwargs) + + except NotCachedError, e: + # This will set a temporary value while ``func`` is being + # processed. When using threads, this is vital, as otherwise + # the function can be called several times before it finishes + # and is put into the cache. + funcwrapper = CacheWrapper(".".join([func.__module__, func.__name__]), inprocess=True) + cache_set(e.key, value=funcwrapper, length=length, skiplog=True) + value = func(*args, **kwargs) + cache_set(e.key, value=value, length=length) + + except MethodNotFinishedError, e: + value = func(*args, **kwargs) + + return value + return inner_func + return decorator + + +def cache_get(*keys, **kwargs): + if kwargs.has_key('default'): + default_value = kwargs.pop('default') + use_default = True + else: + use_default = False + + key = cache_key(keys, **kwargs) + + if not cache_enabled(): + raise NotCachedError(key) + else: + global CACHE_CALLS, CACHE_HITS, REQUEST_CACHE + CACHE_CALLS += 1 + if CACHE_CALLS == 1: + cache_require() + + obj = None + tid = -1 + if REQUEST_CACHE['enabled']: + tid = cache_get_request_uid() + if tid > -1: + try: + obj = REQUEST_CACHE[tid][key] + log.debug('Got from request cache: %s', key) + except KeyError: + pass + + if obj == None: + obj = cache.get(key) + + if obj and isinstance(obj, CacheWrapper): + CACHE_HITS += 1 + CACHED_KEYS[key] = True + log.debug('got cached [%i/%i]: %s', CACHE_CALLS, CACHE_HITS, key) + if obj.inprocess: + raise MethodNotFinishedError(obj.val) + + cache_set_request(key, obj, uid=tid) + + return obj.val + else: + try: + del CACHED_KEYS[key] + except KeyError: + pass + + if use_default: + return default_value + + raise NotCachedError(key) + + +def cache_set(*keys, **kwargs): + """Set an object into the cache.""" + if cache_enabled(): + global CACHED_KEYS, REQUEST_CACHE + obj = kwargs.pop('value') + length = kwargs.pop('length', CACHE_TIMEOUT) + skiplog = kwargs.pop('skiplog', False) + + key = cache_key(keys, **kwargs) + val = CacheWrapper.wrap(obj) + if not skiplog: + log.debug('setting cache: %s', key) + cache.set(key, val, length) + CACHED_KEYS[key] = True + if REQUEST_CACHE['enabled']: + cache_set_request(key, val) + +def _hash_or_string(key): + if is_string_like(key) or isinstance(key, (types.IntType, types.LongType, types.FloatType)): + return smart_str(key) + else: + try: + #if it has a PK, use it. + return str(key._get_pk_val()) + except AttributeError: + return md5_hash(key) + +def cache_contains(*keys, **kwargs): + key = cache_key(keys, **kwargs) + return CACHED_KEYS.has_key(key) + +def cache_key(*keys, **pairs): + """Smart key maker, returns the object itself if a key, else a list + delimited by ':', automatically hashing any non-scalar objects.""" + + if is_string_like(keys): + keys = [keys] + + if is_list_or_tuple(keys): + if len(keys) == 1 and is_list_or_tuple(keys[0]): + keys = keys[0] + else: + keys = [md5_hash(keys)] + + if pairs: + keys = list(keys) + klist = pairs.keys() + klist.sort() + for k in klist: + keys.append(k) + keys.append(pairs[k]) + + key = KEY_DELIM.join([_hash_or_string(x) for x in keys]) + prefix = CACHE_PREFIX + KEY_DELIM + if not key.startswith(prefix): + key = prefix+key + return key.replace(" ", ".") + +def md5_hash(obj): + pickled = pickle.dumps(obj, protocol=pickle.HIGHEST_PROTOCOL) + return md5_constructor(pickled).hexdigest() + + +def is_memcached_backend(): + try: + return cache._cache.__module__.endswith('memcache') + except AttributeError: + return False + +def cache_require(): + """Error if keyedcache isn't running.""" + if cache_enabled(): + key = cache_key('require_cache') + cache_set(key,value='1') + v = cache_get(key, default = '0') + if v != '1': + raise CacheNotRespondingError() + else: + log.debug("Cache responding OK") + return True + +def cache_clear_request(uid): + """Clears all locally cached elements with that uid""" + global REQUEST_CACHE + try: + del REQUEST_CACHE[uid] + log.debug('cleared request cache: %s', uid) + except KeyError: + pass + +def cache_use_request_caching(): + global REQUEST_CACHE + REQUEST_CACHE['enabled'] = True + +def cache_get_request_uid(): + from threaded_multihost import threadlocals + return threadlocals.get_thread_variable('request_uid', -1) + +def cache_set_request(key, val, uid=None): + if uid == None: + uid = cache_get_request_uid() + + if uid>-1: + global REQUEST_CACHE + if not uid in REQUEST_CACHE: + REQUEST_CACHE[uid] = {key:val} + else: + REQUEST_CACHE[uid][key] = val diff --git a/keyedcache/locale/de/LC_MESSAGES/django.mo b/keyedcache/locale/de/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 00000000..c623451a --- /dev/null +++ b/keyedcache/locale/de/LC_MESSAGES/django.mo diff --git a/keyedcache/locale/de/LC_MESSAGES/django.po b/keyedcache/locale/de/LC_MESSAGES/django.po new file mode 100644 index 00000000..fc94969b --- /dev/null +++ b/keyedcache/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,40 @@ +# Satchmo Translation Package +# Copyright (C) 2008 Satchmo Project +# This file is distributed under the same license as the Satchmo package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-03-22 15:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: templates/keyedcache/delete.html:6 +#: templates/keyedcache/stats.html:6 +#: templates/keyedcache/view.html:6 +msgid "Home" +msgstr "Start" + +#: templates/keyedcache/delete.html:7 +#: templates/keyedcache/view.html:7 +msgid "Cache" +msgstr "" + +#: templates/keyedcache/delete.html:8 +msgid "Cache Delete" +msgstr "" + +#: templates/keyedcache/stats.html:7 +msgid "Cache Stats" +msgstr "Cachestatistik" + +#: templates/keyedcache/view.html:8 +msgid "Cache View" +msgstr "" + diff --git a/keyedcache/locale/en/LC_MESSAGES/django.mo b/keyedcache/locale/en/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 00000000..c2bc0b94 --- /dev/null +++ b/keyedcache/locale/en/LC_MESSAGES/django.mo diff --git a/keyedcache/locale/en/LC_MESSAGES/django.po b/keyedcache/locale/en/LC_MESSAGES/django.po new file mode 100644 index 00000000..7c6fdf87 --- /dev/null +++ b/keyedcache/locale/en/LC_MESSAGES/django.po @@ -0,0 +1,40 @@ +# Satchmo Translation Package +# Copyright (C) 2008 Satchmo Project +# This file is distributed under the same license as the Satchmo package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-12-31 00:49-0600\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: templates/keyedcache/delete.html:6 +#: templates/keyedcache/stats.html:6 +#: templates/keyedcache/view.html:6 +msgid "Home" +msgstr "" + +#: templates/keyedcache/delete.html:7 +#: templates/keyedcache/view.html:7 +msgid "Cache" +msgstr "" + +#: templates/keyedcache/delete.html:8 +msgid "Cache Delete" +msgstr "" + +#: templates/keyedcache/stats.html:7 +msgid "Cache Stats" +msgstr "" + +#: templates/keyedcache/view.html:8 +msgid "Cache View" +msgstr "" + diff --git a/keyedcache/locale/es/LC_MESSAGES/django.po b/keyedcache/locale/es/LC_MESSAGES/django.po new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/keyedcache/locale/es/LC_MESSAGES/django.po diff --git a/keyedcache/locale/fr/LC_MESSAGES/django.mo b/keyedcache/locale/fr/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 00000000..43ae9859 --- /dev/null +++ b/keyedcache/locale/fr/LC_MESSAGES/django.mo diff --git a/keyedcache/locale/fr/LC_MESSAGES/django.po b/keyedcache/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 00000000..811a3def --- /dev/null +++ b/keyedcache/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,69 @@ +# Satchmo Translation Package +# Copyright (C) 2008 Satchmo Project +# Jacques Moulin <jacques@tpi.be>, 2008. +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-11-02 16:11+0100\n" +"PO-Revision-Date: 2008-11-02 17:51+0100\n" +"Last-Translator: Jacques Moulin <jacques@tpi.be>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Poedit-Language: French\n" +"X-Poedit-SourceCharset: utf-8\n" + +#: views.py:16 +msgid "Yes" +msgstr "Oui" + +#: views.py:17 +msgid "No" +msgstr "Non" + +#: views.py:21 +msgid "Key to delete" +msgstr "Clé à effacer" + +#: views.py:22 +msgid "Include Children?" +msgstr "Inclure les enfants?" + +#: views.py:23 +msgid "Delete all keys?" +msgstr "Effacer toutes les clés?" + +#: templates/keyedcache/delete.html.py:6 +#: templates/keyedcache/stats.html.py:6 +#: templates/keyedcache/view.html.py:6 +#: templates/keyedcache/delete.html.py:6 +#: templates/keyedcache/stats.html.py:6 +#: templates/keyedcache/view.html.py:6 +msgid "Home" +msgstr "Accueil" + +#: templates/keyedcache/delete.html.py:7 +#: templates/keyedcache/view.html.py:7 +#: templates/keyedcache/delete.html.py:7 +#: templates/keyedcache/view.html.py:7 +msgid "Cache" +msgstr "Cache" + +#: templates/keyedcache/delete.html.py:8 +#: templates/keyedcache/delete.html.py:8 +msgid "Cache Delete" +msgstr "Vider le cache" + +#: templates/keyedcache/stats.html.py:7 +#: templates/keyedcache/stats.html.py:7 +msgid "Cache Stats" +msgstr "Statut du cache" + +#: templates/keyedcache/view.html.py:8 +#: templates/keyedcache/view.html.py:8 +msgid "Cache View" +msgstr "Voir le cache" + diff --git a/keyedcache/locale/he/LC_MESSAGES/django.mo b/keyedcache/locale/he/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 00000000..8c043f31 --- /dev/null +++ b/keyedcache/locale/he/LC_MESSAGES/django.mo diff --git a/keyedcache/locale/he/LC_MESSAGES/django.po b/keyedcache/locale/he/LC_MESSAGES/django.po new file mode 100644 index 00000000..7354e16a --- /dev/null +++ b/keyedcache/locale/he/LC_MESSAGES/django.po @@ -0,0 +1,60 @@ +# translation of Satchmo +# Copyright (C) 2008 The Satchmo Project +# This file is distributed under the same license as the Satchmo package. +# +# Aviv Greenberg <avivgr@gmail.com>, 2008. +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2009-03-13 23:01+0200\n" +"PO-Revision-Date: 2009-03-13 16:04\n" +"Last-Translator: Aviv Greenberg <avivgr@gmail.com>\n" +"Language-Team: <en@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: KBabel 1.11.4\n" +"X-Translated-Using: django-rosetta 0.4.0\n" + +#: views.py:16 +msgid "Yes" +msgstr "כן" + +#: views.py:17 +msgid "No" +msgstr "לא" + +#: views.py:21 +msgid "Key to delete" +msgstr "מפתח שיש למחוק" + +#: views.py:22 +msgid "Include Children?" +msgstr "כלול ילדים?" + +#: views.py:23 +msgid "Delete all keys?" +msgstr "מחק את כל המפתחות?" + +#: templates/keyedcache/delete.html:6 templates/keyedcache/stats.html:6 +#: templates/keyedcache/view.html:6 +msgid "Home" +msgstr "בית" + +#: templates/keyedcache/delete.html:7 templates/keyedcache/view.html:7 +msgid "Cache" +msgstr "זכרון מטמון" + +#: templates/keyedcache/delete.html:8 +msgid "Cache Delete" +msgstr "מחק זכרון מטמון" + +#: templates/keyedcache/stats.html:7 +msgid "Cache Stats" +msgstr "סטטיסטיקת זכרון מטמון" + +#: templates/keyedcache/view.html:8 +msgid "Cache View" +msgstr "הצג זכרון מטמון" diff --git a/keyedcache/locale/it/LC_MESSAGES/django.mo b/keyedcache/locale/it/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 00000000..5e614a26 --- /dev/null +++ b/keyedcache/locale/it/LC_MESSAGES/django.mo diff --git a/keyedcache/locale/it/LC_MESSAGES/django.po b/keyedcache/locale/it/LC_MESSAGES/django.po new file mode 100644 index 00000000..271ef7be --- /dev/null +++ b/keyedcache/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,68 @@ +# translation of django.po to Italiano +# Copyright (C) 2008 Satchmo Project +# This file is distributed under the same license as the PACKAGE package. +# +# costantino giuliodori <costantino.giuliodori@gmail.com>, 2007. +# Alessandro Ronchi <alessandro.ronchi@soasi.com>, 2008. +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-09-27 09:16-0700\n" +"PO-Revision-Date: 2008-09-30 13:13+0200\n" +"Last-Translator: Alessandro Ronchi <alessandro.ronchi@soasi.com>\n" +"Language-Team: Italiano <it@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: KBabel 1.11.4\n" +"Plural-Forms: nplurals=2; plural=n > 1\n" + +#: views.py:16 +msgid "Yes" +msgstr "Si" + +#: views.py:17 +msgid "No" +msgstr "No" + +#: views.py:21 +msgid "Key to delete" +msgstr "Chiave da eliminare" + +#: views.py:22 +# translated = "Slug" +msgid "Include Children?" +msgstr "Includi i figli?" + +#: views.py:23 +msgid "Delete all keys?" +msgstr "Elimina tutte le chiavi?" + +#: templates/keyedcache/delete.html:6 +#: templates/keyedcache/stats.html:6 +#: templates/keyedcache/view.html:6 +msgid "Home" +msgstr "Pagina iniziale" + +#: templates/keyedcache/delete.html:7 +#: templates/keyedcache/view.html:7 +# translated = "Prodotto sottotipi" +msgid "Cache" +msgstr "Cache" + +#: templates/keyedcache/delete.html:8 +# translated = "Cache" +msgid "Cache Delete" +msgstr "Elimina Cache" + +#: templates/keyedcache/stats.html:7 +# translated = "Elimina cache" +msgid "Cache Stats" +msgstr "Statistiche Cache" + +#: templates/keyedcache/view.html:8 +# translated = "Statistiche di cache" +msgid "Cache View" +msgstr "Viste Cache" + diff --git a/keyedcache/locale/ko/LC_MESSAGES/django.mo b/keyedcache/locale/ko/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 00000000..18d97529 --- /dev/null +++ b/keyedcache/locale/ko/LC_MESSAGES/django.mo diff --git a/keyedcache/locale/ko/LC_MESSAGES/django.po b/keyedcache/locale/ko/LC_MESSAGES/django.po new file mode 100644 index 00000000..0969979a --- /dev/null +++ b/keyedcache/locale/ko/LC_MESSAGES/django.po @@ -0,0 +1,40 @@ +# Satchmo Translation Package +# Copyright (C) 2008 Satchmo Project +# This file is distributed under the same license as the Satchmo package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-12-31 00:49-0600\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: templates/keyedcache/delete.html:6 +#: templates/keyedcache/stats.html:6 +#: templates/keyedcache/view.html:6 +msgid "Home" +msgstr "홈" + +#: templates/keyedcache/delete.html:7 +#: templates/keyedcache/view.html:7 +msgid "Cache" +msgstr "캐쉬" + +#: templates/keyedcache/delete.html:8 +msgid "Cache Delete" +msgstr "캐쉬 삭제" + +#: templates/keyedcache/stats.html:7 +msgid "Cache Stats" +msgstr "캐쉬 상태" + +#: templates/keyedcache/view.html:8 +msgid "Cache View" +msgstr "캐쉬 보기" + diff --git a/keyedcache/locale/pl/LC_MESSAGES/django.mo b/keyedcache/locale/pl/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 00000000..14c6acd3 --- /dev/null +++ b/keyedcache/locale/pl/LC_MESSAGES/django.mo diff --git a/keyedcache/locale/pl/LC_MESSAGES/django.po b/keyedcache/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 00000000..77974341 --- /dev/null +++ b/keyedcache/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,60 @@ +# Satchmo Translation Package +# Copyright (C) 2008 Satchmo Project +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-09-03 18:10+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: <jerzyk@jerzyk.com>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: views.py:16 +msgid "Yes" +msgstr "Tak" + +#: views.py:17 +msgid "No" +msgstr "Nie" + +#: views.py:21 +msgid "Key to delete" +msgstr "Identyfikator do usunięcia" + +#: views.py:22 +msgid "Include Children?" +msgstr "Czy razem z dziećmi?" + +#: views.py:23 +msgid "Delete all keys?" +msgstr "Usunąć wszystkie identyfikatory?" + +#: templates/keyedcache/delete.html:6 +#: templates/keyedcache/stats.html:6 +#: templates/keyedcache/view.html:6 +msgid "Home" +msgstr "Strona startowa" + +#: templates/keyedcache/delete.html:7 +#: templates/keyedcache/view.html:7 +msgid "Cache" +msgstr "Cache" + +#: templates/keyedcache/delete.html:8 +msgid "Cache Delete" +msgstr "Wyczyść pamięć podręczną" + +#: templates/keyedcache/stats.html:7 +msgid "Cache Stats" +msgstr "Statystyka pamięci podręcznej" + +#: templates/keyedcache/view.html:8 +msgid "Cache View" +msgstr "Podgląd pamięci podręcznej" + diff --git a/keyedcache/locale/pt_BR/LC_MESSAGES/django.mo b/keyedcache/locale/pt_BR/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 00000000..bb1225bd --- /dev/null +++ b/keyedcache/locale/pt_BR/LC_MESSAGES/django.mo diff --git a/keyedcache/locale/pt_BR/LC_MESSAGES/django.po b/keyedcache/locale/pt_BR/LC_MESSAGES/django.po new file mode 100644 index 00000000..4a9a1842 --- /dev/null +++ b/keyedcache/locale/pt_BR/LC_MESSAGES/django.po @@ -0,0 +1,61 @@ +# Satchmo Translation Package +# Copyright (C) 2008 Satchmo Project +# This file is distributed under the same license as the PACKAGE package. +# Terry Laundos Aguiar <terry@s1solucoes.com.br>, 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-09-05 23:50-0300\n" +"PO-Revision-Date: 2008-09-05 23:51-0300\n" +"Last-Translator: Terry Laundos Aguiar <terry@s1solucoes.com.br>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: views.py:16 +msgid "Yes" +msgstr "Sim" + +#: views.py:17 +msgid "No" +msgstr "Não" + +#: views.py:21 +msgid "Key to delete" +msgstr "Chave para apagar" + +#: views.py:22 +#, fuzzy +msgid "Include Children?" +msgstr "Incluir imagens?" + +#: views.py:23 +msgid "Delete all keys?" +msgstr "Apagar todas as chaves?" + +#: templates/keyedcache/delete.html:6 +#: templates/keyedcache/stats.html:6 +#: templates/keyedcache/view.html:6 +msgid "Home" +msgstr "Inicial" + +#: templates/keyedcache/delete.html:7 +#: templates/keyedcache/view.html:7 +msgid "Cache" +msgstr "Cache" + +#: templates/keyedcache/delete.html:8 +msgid "Cache Delete" +msgstr "Apagar cache" + +#: templates/keyedcache/stats.html:7 +msgid "Cache Stats" +msgstr "Estatísticas de cache" + +#: templates/keyedcache/view.html:8 +msgid "Cache View" +msgstr "Exibição de cache" + diff --git a/keyedcache/locale/ru/LC_MESSAGES/django.mo b/keyedcache/locale/ru/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 00000000..1ff29657 --- /dev/null +++ b/keyedcache/locale/ru/LC_MESSAGES/django.mo diff --git a/keyedcache/locale/ru/LC_MESSAGES/django.po b/keyedcache/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 00000000..fbf7be23 --- /dev/null +++ b/keyedcache/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,40 @@ +# Satchmo Translation Package +# Copyright (C) 2008 Satchmo Project +# This file is distributed under the same license as the Satchmo package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: Satchmo\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-12-31 00:49-0600\n" +"PO-Revision-Date: 2009-03-02 15:50+0300\n" +"Last-Translator: Данил Семеленов <danil.mail@gmail.com>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language-Team: \n" + +#: templates/keyedcache/delete.html:6 +#: templates/keyedcache/stats.html:6 +#: templates/keyedcache/view.html:6 +msgid "Home" +msgstr "" + +#: templates/keyedcache/delete.html:7 +#: templates/keyedcache/view.html:7 +msgid "Cache" +msgstr "Кеш" + +#: templates/keyedcache/delete.html:8 +msgid "Cache Delete" +msgstr "Очистка кеша" + +#: templates/keyedcache/stats.html:7 +msgid "Cache Stats" +msgstr "Статистика кеша" + +#: templates/keyedcache/view.html:8 +msgid "Cache View" +msgstr "Просмотр кеша" + diff --git a/keyedcache/locale/sv/LC_MESSAGES/django.mo b/keyedcache/locale/sv/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 00000000..c9f2fd84 --- /dev/null +++ b/keyedcache/locale/sv/LC_MESSAGES/django.mo diff --git a/keyedcache/locale/sv/LC_MESSAGES/django.po b/keyedcache/locale/sv/LC_MESSAGES/django.po new file mode 100644 index 00000000..9aa00572 --- /dev/null +++ b/keyedcache/locale/sv/LC_MESSAGES/django.po @@ -0,0 +1,44 @@ +# Satchmo Translation Package +# Copyright (C) 2008 Satchmo Project +# This file is distributed under the same license as the PACKAGE package. +# N.L. <kotorinl@yahoo.co.uk>, 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: Satchmo svn\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-04-30 23:40+0200\n" +"PO-Revision-Date: 2008-04-30 23:35+0100\n" +"Last-Translator: N.L. <kotorinl@yahoo.co.uk>\n" +"Language-Team: Group\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Swedish\n" +"X-Poedit-Basepath: ../../../\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Poedit-Country: SWEDEN\n" + +#: templates/keyedcache/delete.html:6 +#: templates/keyedcache/stats.html:6 +#: templates/keyedcache/view.html:6 +msgid "Home" +msgstr "Hem" + +#: templates/keyedcache/delete.html:7 +#: templates/keyedcache/view.html:7 +msgid "Cache" +msgstr "Cache" + +#: templates/keyedcache/delete.html:8 +msgid "Cache Delete" +msgstr "Radera Cache" + +#: templates/keyedcache/stats.html:7 +msgid "Cache Stats" +msgstr "Cache-statistik" + +#: templates/keyedcache/view.html:8 +msgid "Cache View" +msgstr "Visa Cache" + diff --git a/keyedcache/locale/tr/LC_MESSAGES/django.mo b/keyedcache/locale/tr/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 00000000..60e5be3f --- /dev/null +++ b/keyedcache/locale/tr/LC_MESSAGES/django.mo diff --git a/keyedcache/locale/tr/LC_MESSAGES/django.po b/keyedcache/locale/tr/LC_MESSAGES/django.po new file mode 100644 index 00000000..4dd6869c --- /dev/null +++ b/keyedcache/locale/tr/LC_MESSAGES/django.po @@ -0,0 +1,42 @@ +# Satchmo Translation Package +# Copyright (C) 2008 Satchmo Project +# This file is distributed under the same license as the Satchmo package. +# Selin Çuhadar <selincuhadar@gmail.com>, 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: Satchmo\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-12-31 00:49-0600\n" +"PO-Revision-Date: 2008-06-09 18:18+0200\n" +"Last-Translator: Selin Çuhadar <selincuhadar@gmail.com>\n" +"Language-Team: Turkish <selincuhadar@gmail.com>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Country: TURKEY\n" +"X-Poedit-SourceCharset: utf-8\n" + +#: templates/keyedcache/delete.html:6 +#: templates/keyedcache/stats.html:6 +#: templates/keyedcache/view.html:6 +msgid "Home" +msgstr "Ev" + +#: templates/keyedcache/delete.html:7 +#: templates/keyedcache/view.html:7 +msgid "Cache" +msgstr "Cache" + +#: templates/keyedcache/delete.html:8 +msgid "Cache Delete" +msgstr "Cache'yi Sil" + +#: templates/keyedcache/stats.html:7 +msgid "Cache Stats" +msgstr "Cache İstatistikleri" + +#: templates/keyedcache/view.html:8 +msgid "Cache View" +msgstr "Cache'yi Göster" + diff --git a/keyedcache/models.py b/keyedcache/models.py new file mode 100644 index 00000000..dec68463 --- /dev/null +++ b/keyedcache/models.py @@ -0,0 +1,86 @@ +import keyedcache +import logging + +log = logging.getLogger('keyedcache') + +class CachedObjectMixin(object): + """Provides basic object keyedcache for any objects using this as a mixin.""" + + def cache_delete(self, *args, **kwargs): + key = self.cache_key(*args, **kwargs) + log.debug("clearing cache for %s", key) + keyedcache.cache_delete(key, children=True) + + def cache_get(self, *args, **kwargs): + key = self.cache_key(*args, **kwargs) + return keyedcache.cache_get(key) + + def cache_key(self, *args, **kwargs): + keys = [self.__class__.__name__, self] + keys.extend(args) + return keyedcache.cache_key(keys, **kwargs) + + def cache_reset(self): + self.cache_delete() + self.cache_set() + + def cache_set(self, *args, **kwargs): + val = kwargs.pop('value', self) + key = self.cache_key(*args, **kwargs) + keyedcache.cache_set(key, value=val) + + def is_cached(self, *args, **kwargs): + return keyedcache.is_cached(self.cache_key(*args, **kwargs)) + +def find_by_id(cls, groupkey, objectid, raises=False): + """A helper function to look up an object by id""" + ob = None + try: + ob = keyedcache.cache_get(groupkey, objectid) + except keyedcache.NotCachedError, e: + try: + ob = cls.objects.get(pk=objectid) + keyedcache.cache_set(e.key, value=ob) + + except cls.DoesNotExist: + log.debug("No such %s: %s", groupkey, objectid) + if raises: + raise cls.DoesNotExist + + return ob + + +def find_by_key(cls, groupkey, key, raises=False): + """A helper function to look up an object by key""" + ob = None + try: + ob = keyedcache.cache_get(groupkey, key) + except keyedcache.NotCachedError, e: + try: + ob = cls.objects.get(key__exact=key) + keyedcache.cache_set(e.key, value=ob) + + except cls.DoesNotExist: + log.debug("No such %s: %s", groupkey, key) + if raises: + raise + + return ob + +def find_by_slug(cls, groupkey, slug, raises=False): + """A helper function to look up an object by slug""" + ob = None + try: + ob = keyedcache.cache_get(groupkey, slug) + except keyedcache.NotCachedError, e: + try: + ob = cls.objects.get(slug__exact=slug) + keyedcache.cache_set(e.key, value=ob) + + except cls.DoesNotExist: + log.debug("No such %s: %s", groupkey, slug) + if raises: + raise + + return ob + diff --git a/keyedcache/templates/keyedcache/delete.html b/keyedcache/templates/keyedcache/delete.html new file mode 100644 index 00000000..9449a6d3 --- /dev/null +++ b/keyedcache/templates/keyedcache/delete.html @@ -0,0 +1,21 @@ +{% extends "admin/base_site.html" %} +{% load messaging_tags i18n %} + +{% block breadcrumbs %}{% if not is_popup %} +<div class="breadcrumbs"> + <a href="/admin/">{% trans "Home" %}</a> › + <a href="{% url keyedcache_view %}">{% trans "Cache" %}</a> › + {% trans "Cache Delete" %} +</div> +{% endif %}{% endblock %} + +{% block content %} +{% show_messages %} +<p>[<a href="{% url keyedcache_stats %}">Cache Stats</a>] [<a href="{% url keyedcache_view %}">View Cache</a>]</p> +<h1>Delete From Cache</h1> +<form method="POST" action="{% url keyedcache_delete %}"> +{{ form.as_p }} +<input type="submit"/> +</form> + +{% endblock %} diff --git a/keyedcache/templates/keyedcache/stats.html b/keyedcache/templates/keyedcache/stats.html new file mode 100644 index 00000000..0a3f17d6 --- /dev/null +++ b/keyedcache/templates/keyedcache/stats.html @@ -0,0 +1,21 @@ +{% extends "admin/base_site.html" %} +{% load messaging_tags i18n %} + +{% block breadcrumbs %}{% if not is_popup %} +<div class="breadcrumbs"> + <a href="/admin/">{% trans "Home" %}</a> › + {% trans "Cache Stats" %} +</div> +{% endif %}{% endblock %} + +{% block content %} +{% show_messages %} +<p>[<a href="{% url keyedcache_view %}">View Cache</a>] [<a href="{% url keyedcache_delete %}">Delete from Cache</a>] +<h1>Cache Stats</h1> +<p>Backend: {{ cache_backend }} ({% if cache_running %}running{% else %}down{% endif %})</p> +<p>Timeout: {{ cache_time }}</p> +<p>Keys in cache: {{ cache_count }}</p> +<p>Cache Calls: {{ cache_calls }}</p> +<p>Cache Hits: {{ cache_hits }}</p> +<p>Cache Hit Rate: {{ hit_rate }}%</p> +{% endblock %} diff --git a/keyedcache/templates/keyedcache/view.html b/keyedcache/templates/keyedcache/view.html new file mode 100644 index 00000000..e28c5a0e --- /dev/null +++ b/keyedcache/templates/keyedcache/view.html @@ -0,0 +1,18 @@ +{% extends "admin/base_site.html" %} +{% load messaging_tags i18n %} + +{% block breadcrumbs %}{% if not is_popup %} +<div class="breadcrumbs"> + <a href="/admin/">{% trans "Home" %}</a> › + <a href="{% url keyedcache_view %}">{% trans "Cache" %}</a> › + {% trans "Cache View" %} +</div> +{% endif %}{% endblock %} + +{% block content %} +{% show_messages %} +<p>[<a href="{% url keyedcache_stats %}">Cache Stats</a>] [<a href="{% url keyedcache_delete %}">Delete from Cache</a>] +<h1>Cache Keys</h1> +<p style="font-size:82%;">{% for key in cached_keys %}{{ key }}, {% endfor %} +</p> +{% endblock %} diff --git a/keyedcache/tests.py b/keyedcache/tests.py new file mode 100644 index 00000000..8abb8dd3 --- /dev/null +++ b/keyedcache/tests.py @@ -0,0 +1,150 @@ +import keyedcache +import random +from django.test import TestCase +import time + +CACHE_HIT=0 + +def cachetest(a,b,c): + global CACHE_HIT + CACHE_HIT += 1 + r = [random.randrange(0,1000) for x in range(0,3)] + ret = [r, a + r[0], b + r[1], c + r[2]] + return ret + +cachetest = keyedcache.cache_function(2)(cachetest) + +class DecoratorTest(TestCase): + + def testCachePut(self): + d = cachetest(1,2,3) + self.assertEqual(CACHE_HIT,1) + + d2 = cachetest(1,2,3) + self.assertEqual(CACHE_HIT,1) + self.assertEqual(d, d2) + + seeds = d[0] + self.assertEqual(seeds[0] + 1, d[1]) + self.assertEqual(seeds[1] + 2, d[2]) + self.assertEqual(seeds[2] + 3, d[3]) + + time.sleep(3) + d3 = cachetest(1,2,3) + self.assertEqual(CACHE_HIT,2) + self.assertNotEqual(d, d3) + + def testDeleteCachedFunction(self): + orig = cachetest(10,20,30) + keyedcache.cache_delete_function(cachetest) + after = cachetest(10,20,30) + self.assertNotEqual(orig,keyedcache) + +class CachingTest(TestCase): + + def testCacheGetFail(self): + try: + keyedcache.cache_get('x') + self.fail('should have raised NotCachedError') + except keyedcache.NotCachedError: + pass + + def testCacheGetOK(self): + one = [1,2,3,4] + keyedcache.cache_set('ok', value=one, length=2) + two = keyedcache.cache_get('ok') + self.assertEqual(one, two) + + time.sleep(5) + try: + three = keyedcache.cache_get('ok') + self.fail('should have raised NotCachedError, got %s' % three) + except keyedcache.NotCachedError: + pass + + def testCacheGetDefault(self): + chk = keyedcache.cache_get('default',default='-') + self.assertEqual(chk, '-') + + + def testDelete(self): + keyedcache.cache_set('del', value=True) + + for x in range(0,10): + keyedcache.cache_set('del', 'x', x, value=True) + for y in range(0,5): + keyedcache.cache_set('del', 'x', x, 'y', y, value=True) + + # check to make sure all the values are in the cache + self.assert_(keyedcache.cache_get('del', default=False)) + for x in range(0,10): + self.assert_(keyedcache.cache_get('del', 'x', x, default=False)) + for y in range(0,5): + self.assert_(keyedcache.cache_get('del', 'x', x, 'y', y, default=False)) + + # try to delete just one + killed = keyedcache.cache_delete('del','x',1) + self.assertEqual([keyedcache.CACHE_PREFIX + "::del::x::1"], killed) + self.assertFalse(keyedcache.cache_get('del', 'x', 1, default=False)) + + # but the others are still there + self.assert_(keyedcache.cache_get('del', 'x', 2, default=False)) + + # now kill all of del::x::1 + killed = keyedcache.cache_delete('del','x', 1, children=True) + for y in range(0,5): + self.assertFalse(keyedcache.cache_get('del', 'x', 1, 'y', y, default=False)) + + # but del::x::2 and children are there + self.assert_(keyedcache.cache_get('del','x',2,'y',1, default=False)) + + # kill the rest + killed = keyedcache.cache_delete('del', children=True) + self.assertFalse(keyedcache.cache_get('del',default=False)) + for x in range(0,10): + self.assertFalse(keyedcache.cache_get('del', 'x', x, default=False)) + for y in range(0,5): + self.assertFalse(keyedcache.cache_get('del', 'x', x, 'y', y, default=False)) + + +class TestCacheDisable(TestCase): + + def testDisable(self): + keyedcache.cache_set('disabled', value=False) + v = keyedcache.cache_get('disabled') + self.assertEqual(v, False) + + keyedcache.cache_enable(False) + keyedcache.cache_set('disabled', value=True) + try: + keyedcache.cache_get('disabled') + self.fail('should have raised NotCachedError') + except keyedcache.NotCachedError, nce: + key = keyedcache.cache_key('disabled') + self.assertEqual(nce.key, key) + + keyedcache.cache_enable() + v2 = keyedcache.cache_get('disabled') + # should still be False, since the cache was disabled + self.assertEqual(v2, False) + +class TestKeyMaker(TestCase): + + def testSimpleKey(self): + v = keyedcache.cache_key('test') + self.assertEqual(v, keyedcache.CACHE_PREFIX + '::test') + + def testDualKey(self): + v = keyedcache.cache_key('test', 2) + self.assertEqual(v, keyedcache.CACHE_PREFIX + '::test::2') + + def testPairedKey(self): + v = keyedcache.cache_key('test', more='yes') + self.assertEqual(v, keyedcache.CACHE_PREFIX + '::test::more::yes') + + def testPairedDualKey(self): + v = keyedcache.cache_key('test', 3, more='yes') + self.assertEqual(v, keyedcache.CACHE_PREFIX + '::test::3::more::yes') + + + diff --git a/keyedcache/threaded.py b/keyedcache/threaded.py new file mode 100644 index 00000000..997fddbc --- /dev/null +++ b/keyedcache/threaded.py @@ -0,0 +1,32 @@ +"""Causes the keyedcache to also use a first-level cache in memory - this can cut 30-40% of memcached calls. + +To enable, add this to some models.py file in an app:: + + from keyedcache import threaded + threaded.start_listening() + +""" +from threaded_multihost import threadlocals +from django.core.signals import request_started, request_finished +from keyedcache import cache_clear_request, cache_use_request_caching +import random +import logging +log = logging.getLogger('keyedcache.threaded') + +def set_request_uid(sender, *args, **kwargs): + """Puts a unique id into the thread""" + tid = random.randrange(1,10000000) + threadlocals.set_thread_variable('request_uid', tid) + #log.debug('request UID: %s', tid) + +def clear_request_uid(sender, *args, **kwargs): + """Removes the thread cache for this request""" + tid = threadlocals.get_thread_variable('request_uid', -1) + if tid > -1: + cache_clear_request(tid) + +def start_listening(): + log.debug('setting up threaded keyedcache') + cache_use_request_caching() + request_started.connect(set_request_uid) + request_finished.connect(clear_request_uid) diff --git a/keyedcache/urls.py b/keyedcache/urls.py new file mode 100644 index 00000000..1a944043 --- /dev/null +++ b/keyedcache/urls.py @@ -0,0 +1,10 @@ +""" +URLConf for Caching app +""" + +from django.conf.urls.defaults import patterns +urlpatterns = patterns('keyedcache.views', + (r'^$', 'stats_page', {}, 'keyedcache_stats'), + (r'^view/$', 'view_page', {}, 'keyedcache_view'), + (r'^delete/$', 'delete_page', {}, 'keyedcache_delete'), +) diff --git a/keyedcache/utils.py b/keyedcache/utils.py new file mode 100644 index 00000000..29b8fd71 --- /dev/null +++ b/keyedcache/utils.py @@ -0,0 +1,14 @@ +import types + +def is_string_like(maybe): + """Test value to see if it acts like a string""" + try: + maybe+"" + except TypeError: + return 0 + else: + return 1 + + +def is_list_or_tuple(maybe): + return isinstance(maybe, (types.TupleType, types.ListType)) diff --git a/keyedcache/views.py b/keyedcache/views.py new file mode 100644 index 00000000..9a3c1219 --- /dev/null +++ b/keyedcache/views.py @@ -0,0 +1,103 @@ +from django import forms +from django.conf import settings +from django.contrib.auth.decorators import user_passes_test +from django.http import HttpResponseRedirect +from django.shortcuts import render_to_response +from django.template import RequestContext +from django.utils.translation import ugettext_lazy as _ +import keyedcache +import logging + +log = logging.getLogger('keyedcache.views') + +YN = ( + ('Y', _('Yes')), + ('N', _('No')), + ) + +class CacheDeleteForm(forms.Form): + tag = forms.CharField(label=_('Key to delete'), required=False) + children = forms.ChoiceField(label=_('Include Children?'), choices=YN, initial="Y") + kill_all = forms.ChoiceField(label=_('Delete all keys?'), choices=YN, initial="Y") + + def delete_cache(self): + + data = self.cleaned_data + if data['kill_all'] == "Y": + keyedcache.cache_delete() + result = "Deleted all keys" + elif data['tag']: + keyedcache.cache_delete(data['tag'], children=data['children']) + if data['children'] == "Y": + result = "Deleted %s and children" % data['tag'] + else: + result = "Deleted %s" % data['tag'] + else: + result = "Nothing selected to delete" + + log.debug(result) + return result + +def stats_page(request): + calls = keyedcache.CACHE_CALLS + hits = keyedcache.CACHE_HITS + + if (calls and hits): + rate = float(keyedcache.CACHE_HITS)/keyedcache.CACHE_CALLS*100 + else: + rate = 0 + + try: + running = keyedcache.cache_require() + + except keyedcache.CacheNotRespondingError: + running = False + + ctx = RequestContext(request, { + 'cache_count' : len(keyedcache.CACHED_KEYS), + 'cache_running' : running, + 'cache_time' : settings.CACHE_TIMEOUT, + 'cache_backend' : settings.CACHE_BACKEND, + 'cache_calls' : keyedcache.CACHE_CALLS, + 'cache_hits' : keyedcache.CACHE_HITS, + 'hit_rate' : "%02.1f" % rate + }) + + return render_to_response('keyedcache/stats.html', context_instance=ctx) + +stats_page = user_passes_test(lambda u: u.is_authenticated() and u.is_staff, login_url='/accounts/login/')(stats_page) + +def view_page(request): + keys = keyedcache.CACHED_KEYS.keys() + + keys.sort() + + ctx = RequestContext(request, { + 'cached_keys' : keys, + }) + + return render_to_response('keyedcache/view.html', context_instance=ctx) + +view_page = user_passes_test(lambda u: u.is_authenticated() and u.is_staff, login_url='/accounts/login/')(view_page) + +def delete_page(request): + log.debug("delete_page") + if request.method == "POST": + form = CacheDeleteForm(request.POST) + if form.is_valid(): + log.debug('delete form valid') + results = form.delete_cache() + return HttpResponseRedirect('../') + else: + log.debug("Errors in form: %s", form.errors) + else: + log.debug("new form") + form = CacheDeleteForm() + + ctx = RequestContext(request, { + 'form' : form, + }) + + return render_to_response('keyedcache/delete.html', context_instance=ctx) + +delete_page = user_passes_test(lambda u: u.is_authenticated() and u.is_staff, login_url='/accounts/login/')(delete_page) |