зеркало из https://github.com/mozilla/kitsune.git
New method for storing and formatting actions.
* Drop the `Activity` class and table, replace with `Action`. * And associated renaming. * Define the concept of an `ActionFormatter` and a base class. * Add a README.
This commit is contained in:
Родитель
fc43a5ce3d
Коммит
6e4ff7d016
|
@ -0,0 +1,68 @@
|
|||
================
|
||||
Logging Activity
|
||||
================
|
||||
|
||||
The **activity** app provides a way to log arbitrary activity for interested
|
||||
users (c.f. your Github dashboard). This activity can appear on a user's
|
||||
profile, on their personal dashboard, or other places.
|
||||
|
||||
|
||||
Logging What Now?
|
||||
=================
|
||||
|
||||
Each bit of activity is represented in the database by an ``Action`` object.
|
||||
It's linked to relevant users by a ``ManyToManyField``. To add a new action to
|
||||
users' activity logs, create a new ``Action`` object and add the relevant users
|
||||
to the ``action.users`` manager.
|
||||
|
||||
|
||||
Formatting Actions
|
||||
==================
|
||||
|
||||
``Action`` objects require a **formatter** class that determines how to render
|
||||
the action in the log. For example, a formatter for a forum reply might decide
|
||||
to render the title of the action like this::
|
||||
|
||||
_('{user} replied to {thread}').format(user=, thread=)
|
||||
|
||||
Formatters have access to the entire action object, so they can look at any
|
||||
attached objects including, potentially, the creator (of the action), or the
|
||||
relevant content object (a ``GenericForeignKey``).
|
||||
|
||||
Formatters should probably subclass ``activity.ActionFormatter``, though that's
|
||||
not strictly required at the moment. They need to accept an ``Action`` object
|
||||
to their constructors and implement the following properties:
|
||||
|
||||
``title``:
|
||||
a title for the action
|
||||
``content``:
|
||||
text content, may be blank
|
||||
``__unicode__()``:
|
||||
probably the same as ``title``
|
||||
|
||||
An fuller example::
|
||||
|
||||
class ForumReplyFormatter(ActionFormatter):
|
||||
def __init__(self, action):
|
||||
self.action = action
|
||||
self.post = action.content_object
|
||||
title = _('{user} replied to {thread}')
|
||||
self.title = title.format(user=action.creator,
|
||||
thread=self.post.thread.title)
|
||||
self.content = self.post.content[0:225]
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
Saving the Formatter
|
||||
--------------------
|
||||
|
||||
When creating an ``Action``, you need to save a Python route to a formatter
|
||||
class. For example, assuming the formatter above was in ``forums.tasks``, you
|
||||
might store::
|
||||
|
||||
action = Action()
|
||||
action.formatter = 'forums.tasks.ForumReplyFormatter'
|
||||
|
||||
It should be a path you can import from the Django shell.
|
|
@ -0,0 +1,13 @@
|
|||
class ActionFormatter(object):
|
||||
"""A base class for action formatters.
|
||||
|
||||
Subclasses must implement all properties, optionally with @property."""
|
||||
|
||||
title = 'Something Happened!'
|
||||
content = ''
|
||||
|
||||
def __init__(self, action):
|
||||
self.action = action
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title
|
|
@ -8,29 +8,46 @@ from django.db import models
|
|||
from sumo.models import ModelBase
|
||||
|
||||
|
||||
class Activity(ModelBase):
|
||||
class Action(ModelBase):
|
||||
"""Represents a unit of activity in a user's 'inbox.'"""
|
||||
user = models.ForeignKey(User, related_name='activity_inbox')
|
||||
users = models.ManyToManyField(User, related_name='action_inbox')
|
||||
creator = models.ForeignKey(User, null=True, blank=True,
|
||||
related_name='activity')
|
||||
related_name='actions')
|
||||
created = models.DateTimeField(default=datetime.now, db_index=True)
|
||||
title = models.CharField(max_length=120)
|
||||
content = models.CharField(max_length=400, blank=True)
|
||||
data = models.CharField(max_length=400, blank=True)
|
||||
url = models.URLField(null=True, blank=True)
|
||||
content_type = models.ForeignKey(ContentType, null=True, blank=True)
|
||||
object_id = models.PositiveIntegerField(null=True, blank=True)
|
||||
content_object = generic.GenericForeignKey()
|
||||
formatter_cls = models.CharField(max_length=200,
|
||||
default='activity.ActionFormatter')
|
||||
|
||||
class Meta(object):
|
||||
ordering = ['-created']
|
||||
|
||||
@property
|
||||
def formatter(self):
|
||||
if not hasattr(self, 'fmt'):
|
||||
mod, _, cls = self.formatter_cls.rpartition('.')
|
||||
fmt_cls = getattr(__import__(mod, fromlist=[cls]), cls)
|
||||
self.fmt = fmt_cls(self)
|
||||
return self.fmt
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title
|
||||
return unicode(self.formatter)
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return self.formatter.title
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
return self.formatter.content
|
||||
|
||||
def get_absolute_url(self):
|
||||
return self.url
|
||||
|
||||
|
||||
class ActivityMixin(object):
|
||||
class ActionMixin(object):
|
||||
"""Add a GenericRelation to a model."""
|
||||
activity = generic.GenericRelation(Activity)
|
||||
actions = generic.GenericRelation(Action)
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
DROP TABLE `activity_activity`;
|
||||
|
||||
CREATE TABLE `activity_action_users` (
|
||||
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
|
||||
`action_id` integer NOT NULL,
|
||||
`user_id` integer NOT NULL,
|
||||
UNIQUE (`action_id`, `user_id`)
|
||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
|
||||
ALTER TABLE `activity_action_users` ADD CONSTRAINT `user_id_refs_id_514426b8` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`);
|
||||
|
||||
CREATE TABLE `activity_action` (
|
||||
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
|
||||
`creator_id` integer,
|
||||
`created` datetime NOT NULL,
|
||||
`data` varchar(400) NOT NULL,
|
||||
`url` varchar(200),
|
||||
`content_type_id` integer,
|
||||
`object_id` integer UNSIGNED,
|
||||
`formatter_cls` varchar(200) NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
|
||||
ALTER TABLE `activity_action` ADD CONSTRAINT `creator_id_refs_id_4475b305` FOREIGN KEY (`creator_id`) REFERENCES `auth_user` (`id`);
|
||||
ALTER TABLE `activity_action` ADD CONSTRAINT `content_type_id_refs_id_95f5c947` FOREIGN KEY (`content_type_id`) REFERENCES `django_content_type` (`id`);
|
||||
ALTER TABLE `activity_action_users` ADD CONSTRAINT `action_id_refs_id_52ad1f42` FOREIGN KEY (`action_id`) REFERENCES `activity_action` (`id`);
|
||||
CREATE INDEX `activity_action_f97a5119` ON `activity_action` (`creator_id`);
|
||||
CREATE INDEX `activity_action_3216ff68` ON `activity_action` (`created`);
|
||||
CREATE INDEX `activity_action_e4470c6e` ON `activity_action` (`content_type_id`);
|
||||
|
Загрузка…
Ссылка в новой задаче