Skip to main content
Build Status

A Django application to implement a follow system and a timeline using multiple backends (db, redis, etc.).

The timeline engine can be found in sequere.contrib.timeline.

Installation

  1. Either check out the package from GitHub or it pull from a release via PyPI

    pip install django-sequere
  2. Add sequere to your INSTALLED_APPS

INSTALLED_APPS = (
    'sequere',
)

Usage

In Sequere any resources can follow any resources and vice versa.

Let’s say you have two models:

# models.py

from django.db import models

class User(models.Model):
    username = models.CharField(max_length=150)


class Project(models.Model):
    name = models.CharField(max_length=150)

Now you to register them in sequere to identify them when a resource is following another one.

# sequere_registry.py

from .models import Project, User

import sequere

sequere.register(User)
sequere.register(Project)

Sequere uses the same concepts as Django Admin, so if you have already used it, you will not be lost.

You can now use Sequere like any other application, let’s play with it:

>>> from sequere.models import (follow, unfollow, get_followings_count, is_following,
                                    get_followers_count, get_followers, get_followings)

>>> from myapp.models import User, Project

>>> user = User.objects.create(username='thoas')

>>> project = Project.objects.create(name='La classe americaine')

>>> follow(user, project)  # thoas will now follow "La classe americaine"

>>> is_following(user, project)
True

>>> get_followers_count(project)
1

>>> get_followings_count(user)
1

>>> get_followers(user)
[]

>>> get_followers(project)
[(<User: thoas>, datetime.datetime(2013, 10, 25, 4, 41, 31, 612067))]

>>> get_followings(user)
[(<Project: La classe americaine, datetime.datetime(2013, 10, 25, 4, 41, 31, 612067))]

If you are as lazy as me to provide the original instance in each sequere calls, use SequereMixin

# models.py

from django.db import models

from sequere.mixin import SequereMixin

class User(SequereMixin, models.Model):
    username = models.Charfield(max_length=150)

class Project(SequereMixin, models.Model):
    name = models.Charfield(max_length=150)

Now you can use calls directly from the instance:

>>> from myapp.models import User, Project

>>> user = User.objects.create(username='thoas')

>>> project = Project.objects.create(name'La classe americaine')

>>> user.follow(project)  # thoas will now follow "La classe americaine"

>>> user.is_following(project)
True

>>> project.get_followers_count()
1

>>> user.get_followings_count()
1

>>> user.get_followers()
[]

>>> project.get_followers()
[(<User: thoas>, datetime.datetime(2013, 10, 25, 4, 41, 31, 612067))]

>>> user.get_followings()
[(<Project: La classe americaine, datetime.datetime(2013, 10, 25, 4, 41, 31, 612067))]

So much fun!

Backends

sequere.backends.database.DatabaseBackend

A database backend to store your follows in you favorite database using the Django’s ORM.

To use this backend you will have to add sequere.backends.database to your INSTALLED_APPS

INSTALLED_APPS = (
    'sequere',
    'sequere.backends.database',
)

The follower will be identified by the couple (from_identifier, from_object_id) and the following by (to_identifier, to_object_id).

Each identifiers are taken from the registry. For example, if you want to create a custom identifier key from a model you can customized it like so:

# sequere_registry.py

from myapp.models import Project

from sequere.base import ModelBase

import sequere


class ProjectSequere(ModelBase):
    identifier = 'projet' # the french way ;)

sequere.registry(Project, ProjectSequere)

sequere.backends.redis.RedisBackend

We are using exclusively Sorted Sets in this Redis implementation.

Create a uid for a new resource

INCR sequere:global:uid    =>  1
SET sequere:uid:{identifier}:{id} 1
HMSET sequere:uid::{id} identifier {identifier} object_id {id}

Store followers count

INCR sequere:uid:{to_uid}:followers:count => 1
INCR sequere:uid:{to_uid}:followers:{from_identifier}:count => 1

Store followings count

INCR sequere:uid:{from_uid}:followings:count => 1
INCR sequere:uid:{from_uid}:followings:{to_identifier}:count => 1

Add a new follower

ZADD sequere:uid:{to_uid}:followers {from_uid} {timestamp}
ZADD sequere:uid:{to_uid}:followers:{from_identifier} {from_uid} {timestamp}

Add a new following

ZADD sequere:uid:{from_uid}:followings {to_uid} {timestamp}
ZADD sequere:uid:{from_uid}:followings{to_identifier} {to_uid} {timestamp}

Retrieve the followers uids

ZRANGEBYSCORE sequere:uid:{uid}:followers -inf +inf

Retrieve the followings uids

ZRANGEBYSCORE sequere:uid:{uid}:followings =inf +inf

With this implementation you can retrieve your followers ordered

ZREVRANGEBYSCORE sequere:uid:{uid}:followers +inf -inf

Timeline

The timeline engine is directly based on sequere resources system.

Concept

A Timeline is basically a list of Action.

An Action is represented by:

  • actor which is the actor of the action

  • verb which is the action name

  • target which is the target of the action (not required)

  • date which is the date when the action has been done

Installation

You have to follow installation instructions of sequere first before installing sequere.contrib.timeline.

Add sequere.contrib.timeline to your INSTALLED_APPS

INSTALLED_APPS = (
    'sequere.contrib.timeline',
)

sequere.contrib.timeline requires celery to work properly, so you will have to install it.

Usage

You have to register your actions based on your resources, for example

# sequere_registry.py

from .models import Project, User

from sequere.contrib.timeline import Action
from sequere import register
from sequere.base import ModelBase


# actions
class JoinAction(Action):
    verb = 'join'


class LikeAction(Action):
    verb = 'like'

# resources
class ProjectSequere(ModelBase):
    identifier = 'project'

class UserSequere(ModelBase):
    identifier = 'user'

    actions = (JoinAction, LikeAction, )

# register resources
register(User, UserSequere)
register(Project, ProjectSequere)

Now we have registered our actions we can play with the timeline API

>>> from sequere.models import (follow, unfollow)

>>> from sequere.contrib.timeline import Timeline

>>> from myapp.models import User, Project

>>> from myapp.sequere_registry import JoinAction, LikeAction

>>> thoas = User.objects.create(username='thoas')

>>> project = Project.objects.create(name='La classe americaine')

>>> timeline = Timeline(thoas) # create a timeline

>>> timeline.save(JoinAction(actor=thoas)) # save the action in the timeline

>>> timeline.get_private()
[<JoinAction: thoas join>]

>>>: timeline.get_public()
[<JoinAction: thoas join>]

When the resource is the actor of its own action then we push the action both in private and public timelines.

Now we have to test the system with the follow process

>>> newbie = User.objects.create(username='newbie')

>>> follow(newbie, thoas) # newbie is now following thoas

>>> Timeline(newbie).get_private() # thoas actions now appear in the private timeline of newbie
[<JoinAction: thoas join>]

>>> Timeline(newbie).get_public()
[]

When A is following B we copy actions of B in the private timeline of A, celery is needed to handle these asynchronous tasks.

>>> unfollow(newbie, thoas)

>>> Timeline(newbie).get_private()
[]

When A is unfollowing B we delete the actions of B in the private timeline of A.

As you may have noticed the JoinAction is an action which does not need a target, some actions will need target, sequere.contrib.timeline provides a quick way to query actions for a specific target.

>>> timeline = Timeline(thoas)

>>> timeline.save(LikeAction(actor=thoas, target=project))

>>> timeline.get_private()
[<JoinAction: thoas join>, <LikeAction: thoas like La classe americaine>]

>>> timeline.get_private(target=Project) # only retrieve actions with Project resource as target
[<LikeAction: thoas like La classe americaine>]

>>> timeline.get_private(target='project') # only retrieve actions with 'project' identifier as target
[<LikeAction: thoas like La classe americaine>]

Configuration

SEQUERE_BACKEND

The backend used to store follows

Defaults to sequere.backends.database.DatabaseBackend.

SEQUERE_BACKEND_OPTIONS

A dictionary of parameters to pass to the backend, for example with redis:

SEQUERE_BACKEND = 'sequere.backends.redis.RedisBackend'
SEQUERE_BACKEND_OPTIONS = {
    'client_class': 'myproject.myapp.mockup.Connection',
    'options': {
        'host': 'localhost',
        'port': 6379,
        'db': 0,
    },
    'prefix': 'prefix-used:'
}

The (optional) prefix to be used for the key when storing in the Redis database.

Defaults to sequere:.

SEQUERE_TIMELINE_BACKEND

The backend used to store follows

Defaults to sequere.contrib.timeline.redis.RedisBackend.

SEQUERE_TIMELINE_BACKEND_OPTIONS

A dictionary of parameters to pass to the backend, for example with redis:

SEQUERE_TIMELINE_BACKEND = 'sequere.contrib.timeline.redis.RedisBackend'
SEQUERE_TIMELINE_BACKEND_OPTIONS = {
    'client_class': 'myproject.myapp.mockup.Connection',
    'options': {
        'host': 'localhost',
        'port': 6379,
        'db': 0,
    },
    'prefix': 'prefix-used:'
}

The (optional) prefix to be used for the key when storing in the Redis database.

Defaults to sequere:timeline:.

Resources

Metadata

Release files for django-sequere 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-sequere 0.4.1
File Size Uploaded
django-sequere-0.4.1.tar.gz 20.5 kB Details

Release files / django-sequere-0.4.1.tar.gz

Download URL django-sequere-0.4.1.tar.gz
Size 20.5 kB
Tags Source
SHA-256 checksum
How to use checksums
5308c1401974d792af25e0bdff4c858892186d5a9356fe8353c554930072aca3
BLAKE2b-256 checksum
How to use checksums
f9b7076498ed80d4f198066cbc020d3bd920c6b21f9232d4e3974f8a495da77c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No

Release history Release notifications | RSS feed

This release

0.4.1 This release

1 release file

0.4.0

0.3.0

1 release file

0.2.8

1 release file

0.2.7

1 release file

0.2.6

1 release file

0.2.5

1 release file

0.2.4

1 release file

0.2.3

1 release file

0.2.2

1 release file

0.2.1

0.2.0

1 release file

0.1.6

1 release file

0.1.5

1 release file

0.1.4

1 release file

0.1.3

1 release file

0.1.2

1 release file

0.1.1

1 release file

0.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page