class SequereRegistry(dict):
    def __init__(self):
    def for_model(self, model):
    def get_identifier(self, instance):
    def identifiers(self):
    def unregister(self, name):
    def register(self, *args, **kwargs):
    def _register_model(self, model, sequere=None,
                        name=None, **kwargs):
    def _register(self, sequere):
def autodiscover():
def register(*args, **kwargs): def get_client(connection, connection_class=None):
    if connection_class:
        client = load_class(connection_class)(**connection)
    else:
        try:
            import redis
        except ImportError:
            raise ImproperlyConfigured(
                "The Redis backend requires redis-py to be installed.")
        if isinstance(connection, six.string_types):
            client = redis.from_url(connection, decode_responses=True)
        else:
            # see https://github.com/andymccurdy/redis-py/issues/463#issuecomment-41229918
            connection['decode_responses'] = True

            client = redis.StrictRedis(**connection)

    return client class Manager(object):
    def __init__(self, client, prefix=None):
        self.client = client
        self.prefix = prefix or ''

    def add_prefix(self, key):
        return get_key(self.prefix, key)

    def make_uid(self, data, client=None):
        client = client or self.client

        uid = str(uuid.uuid4()).replace('-', '')

        data['uid'] = uid

        key = self.add_prefix(get_key('uid', uid))

        client.hmset(key, data)

        return uid

    def get_data_from_uid(self, uid):
        key = self.add_prefix(get_key('uid', uid))

        return self.client.hgetall(key)

    def clear(self):
        self.client.flushdb() def get_key(*args, **kwargs):
    separator = kwargs.pop('separator', get_setting('KEY_SEPARATOR'))

    key = separator.join(['%s' % arg for arg in args if arg is not None])

    return key def get_backend(): def to_timestamp(dt):
    return int(time.mktime(dt.timetuple())) def from_timestamp(timestamp):
    if settings.USE_TZ:
        import pytz

        local_tz = timezone.get_default_timezone()

        utc_dt = datetime.utcfromtimestamp(timestamp).replace(tzinfo=pytz.utc)

        return local_tz.normalize(utc_dt.astimezone(local_tz))

    return datetime.fromtimestamp(timestamp) class Action(object):
    verb = None

    def __init__(self, actor, target=None, date=None, **kwargs):
        self.actor = actor
        self.target = target
        self.kwargs = kwargs.pop('kwargs', {})
        self.date = date or datetime.now()
        self.uid = None
        self.data = None

        for k, v in kwargs.items():
            setattr(self, k, v)

    def __str__(self):
        if not self.target:
            return '%s %s' % (self.actor, self.verb)

        return '%s %s %s' % (self.actor, self.verb, self.target)

    def __repr__(self):
        try:
            u = six.text_type(self)
        except (UnicodeEncodeError, UnicodeDecodeError):
            u = '[Bad Unicode data]'
        return force_str('<%s: %s>' % (self.__class__.__name__, u)) @lru_cache()
def get_actions():
    return dict((action.verb, action)
                for model_class in registry.values()
                for action in getattr(model_class, 'actions', [])) class ActionDoesNotExist(SequereException):
    pass class ActionInvalid(SequereException):
    def __init__(self, *args, **kwargs):
        self.data = kwargs.pop('data', None)

        super(ActionInvalid, self).__init__(*args, **kwargs) class RedisTimelineQuerySetTransformer(TimelineQuerySetTransformer):
    def _transform(self, scores):
        with self.qs.pipeline() as pipe:
            for uid, score in scores:
                pipe.hgetall(get_key(self.prefix, 'uid', uid))

            results = pipe.execute()

            actions = []

            for data in results:
                if not data:
                    continue

                try:
                    action = self.backend.get_action(data)
                except ActionInvalid as e:
                    logger.exception(e)
                else:
                    actions.append(action)

            return actions
import six
from django.db import models
from django.utils import timezone as datetime
from sequere.registry import registry
from sequere.utils import get_client
from sequere.backends.redis.managers import Manager
from sequere.backends.redis.utils import get_key
from sequere import app
from sequere.utils import to_timestamp, from_timestamp
from sequere.contrib.timeline.action import Action, get_actions
from sequere.contrib.timeline.exceptions import ActionDoesNotExist, ActionInvalid
from .query import RedisTimelineQuerySetTransformer





class RedisBackend(object):
    queryset_class = RedisTimelineQuerySetTransformer

    def __init__(self, *args, **kwargs):
        kwargs.setdefault('client_class', 'redis.StrictRedis')
        kwargs.setdefault('options', {'decode_responses': True})
        kwargs.setdefault('prefix', 'sequere:timeline:')

        self.client = get_client(kwargs['options'], connection_class=kwargs['client_class'])

        self.storage = Manager(self.client, prefix=kwargs['prefix'])

    def _make_key(self, instance, name, action=None, target=None):
        segments = [
            self.storage.add_prefix('uid'),
            app.backend.get_uid(instance),
            name,
        ]

        if target:
            if isinstance(target, six.string_types):
                segments += ['target', target]

            else:
                if isinstance(target, models.Model) or issubclass(target, models.Model):
                    segments += ['target', registry.get_identifier(target)]

        if action:
            if isinstance(action, six.string_types):
                segments += ['verb', action]
            elif issubclass(action, Action):
                segments += ['verb', action.verb]

        key = get_key(*segments)

        return key

    def _get_keys(self, instance, action):
        identifier = registry.get_identifier(instance)

        prefix = self.storage.add_prefix('uid')

        uid = app.backend.get_uid(instance)

        keys = [
            get_key(prefix, uid, 'private'),
            get_key(prefix, uid, 'private', 'target', identifier)
        ]

        if action.actor == instance:
            keys.append(get_key(prefix, uid, 'public'))
            keys.append(get_key(prefix, uid, 'public', 'target', identifier))

        if action.target is not None and action.target != action.actor:
            identifier = registry.get_identifier(action.target)

            keys.append(get_key(prefix, uid, 'private', 'target', identifier))

            if action.actor == instance:
                keys.append(get_key(prefix, uid, 'public', 'target', identifier))

        return keys

    def get_action(self, data):
        if isinstance(data, six.string_types + (int, )):
            return self.get_action(self.storage.get_data_from_uid(data))

        verb = data['verb']


        actions = get_actions()