Source code for sodarcache.api
"""Sodarcache API for adding and updating cache items"""
import logging
from django.contrib.auth import get_user_model
# Projectroles dependency
from projectroles.plugins import get_active_plugins
from projectroles.utils import get_app_names
from sodarcache.models import JSONCacheItem
# Local variables
APP_NAMES = get_app_names()
LABEL_MAX_WIDTH = 32
CACHE_TYPES = ['json']
# Access Django user model
User = get_user_model()
logger = logging.getLogger(__name__)
[docs]class SodarCacheAPI:
"""SodarCache backend API to be used by Django apps."""
# TODO: Make model selection dynamic once we introduce types other than JSON
# Internal functions -------------------------------------------------------
@classmethod
def _check_app_name(cls, app_name):
"""Check if app_name is valid, raise ValueError if not"""
if app_name not in APP_NAMES:
raise ValueError(
'Unknown app name "{}" (installed apps: {})'.format(
app_name, ', '.join(x for x in APP_NAMES)
)
)
@classmethod
def _check_data_type(cls, data_type):
"""Check if data_type is valid, raise ValueError if not"""
if data_type not in CACHE_TYPES:
raise ValueError(
'Unknown data type "{}" for a cache item '
'(allowed types: {})'.format(
data_type, ', '.join(x for x in CACHE_TYPES)
)
)
# API functions ------------------------------------------------------------
[docs] @classmethod
def get_project_cache(cls, project, data_type='json'):
"""
Return all cached data for a project.
:param project: Project object
:param data_type: String stating the data type of the cache items
:return: QuerySet
:raise: ValueError if data_type is invalid
"""
cls._check_data_type(data_type)
return JSONCacheItem.objects.filter(project=project)
[docs] @classmethod
def update_cache(cls, name=None, project=None, user=None):
"""
Update items by certain name within a project by calling implemented
functions in project app plugins.
:param name: Item name to limit update to (string, optional)
:param project: Project object to limit update to (optional)
:param user: User object to denote user triggering the update (optional)
"""
plugins = get_active_plugins(plugin_type='project_app')
for plugin in plugins:
plugin.update_cache(name, project, user)
[docs] @classmethod
def delete_cache(cls, app_name=None, project=None):
"""
Delete cache items. Optionallly limit to project and/or user.
:param app_name: Name of the app which sets the item (string)
:param project: Project object (optional)
:return: Integer (deleted item count)
:raise: ValueError if app_name is given but invalid
"""
if app_name:
cls._check_app_name(app_name)
if not app_name and not project:
items = JSONCacheItem.objects.all()
else:
query_params = {}
if app_name:
query_params['app_name'] = app_name
if project:
query_params['project'] = project
items = JSONCacheItem.objects.filter(**query_params)
if items:
item_count = items.count()
items.delete()
logger.info(
'Deleted {} item{} from cache(app={}, project={})'.format(
item_count,
's' if item_count != 1 else '',
app_name,
project.sodar_uuid if project else None,
)
)
return item_count
logger.info(
'No items found for deletion (app={}, project={})'.format(
app_name, project.sodar_uuid if project else None
)
)
return 0
[docs] @classmethod
def get_cache_item(cls, app_name, name, project=None):
"""
Return cached data by app_name, name (identifier) and optional project.
Returns None if not found.
:param name: Item name (string)
:param app_name: Name of the app which sets the item (string)
:param project: Project object (optional)
:return: JSONCacheItem object
:raise: ValueError if app_name is invalid
"""
cls._check_app_name(app_name)
query_string = {'app_name': app_name, 'name': name}
if project:
query_string['project'] = project
return JSONCacheItem.objects.filter(**query_string).first()
[docs] @classmethod
def set_cache_item(
cls, app_name, name, data, data_type='json', project=None, user=None
):
"""
Create or update and save a cache item.
:param app_name: Name of the app which sets the item (string)
:param name: Item name (string)
:param data: Item data (dict)
:param data_type: String stating the data type of the cache items
:param project: Project object (optional)
:param user: User object to denote user triggering the update (optional)
:return: JSONCacheItem object
:raise: ValueError if app_name is invalid
:raise: ValueError if data_type is invalid
"""
cls._check_app_name(app_name)
cls._check_data_type(data_type)
item = cls.get_cache_item(app_name, name, project)
log_msg = 'Updated item "{}:{}"'.format(app_name, name)
if not item:
if data_type == 'json':
item = JSONCacheItem()
item.name = name
item.app_name = app_name
item.data = data
if project:
item.project = project
log_msg += ' in project "{}" ({})'.format(
project.title, project.sodar_uuid
)
if user:
item.user = user
log_msg += ' by user "{}"'.format(user.username)
item.save()
logger.info(log_msg)
return item
[docs] @classmethod
def get_update_time(cls, app_name, name, project=None):
"""
Return the time of the last update of a cache object as seconds since
epoch.
:param name: Item name (string)
:param app_name: Name of the app which sets the item (string)
:param project: Project object (optional)
:return: Float
"""
item = cls.get_cache_item(app_name, name, project)
return item.date_modified.timestamp() if item else None