Skip to content

Commit

Permalink
[#1625] Add 'new user 'activity stream event.
Browse files Browse the repository at this point in the history
Add activity_dict_save() function to ckan/lib/dictization/model_save.py.
Initialises an Activity object from a dict and adds the object to the
sqlalchemy session so that it gets saved to the database. Also added a
simple test case to ckan/tests/lib/test_dictization.py.

Add activity_create() function to ckan/logic/action/create.py. This is
the logic function for creating an activity stream event, to be called
by other logic functions. It pretty much just wraps
activity_dict_save(), but creates a new revision for the new activity
and commits the change to the db.

The two functions above will work for any kind of activity stream event.

Extended the user_create() logic function in ckan/logic/action/create.py
to construct an activity dict and call activity_create(), so that a 'new
user' activity stream event is emitted whenever a new user is created.

Added HTML renderer for 'new user' activity to ckan/logic/action/get.py,
and added new_user.html template to ckan/templates/activity_streams.

Added Activity and ActivityDetail classes to ckan/model/__init__.py.
  • Loading branch information
Sean Hammond committed Jan 10, 2012
1 parent ae3be84 commit eb62fb7
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 2 deletions.
17 changes: 17 additions & 0 deletions ckan/lib/dictization/model_save.py
Expand Up @@ -438,3 +438,20 @@ def task_status_dict_save(task_status_dict, context):

task_status = table_dict_save(task_status_dict, model.TaskStatus, context)
return task_status

def activity_dict_save(activity_dict, context):

model = context['model']
session = context['session']

user_id = activity_dict['user_id']
object_id = activity_dict['object_id']
revision_id = activity_dict['revision_id']
activity_type = activity_dict['activity_type']
activity_obj = model.Activity(user_id, object_id, revision_id,
activity_type)
session.add(activity_obj)

# TODO: Handle activity details.

return activity_obj
49 changes: 47 additions & 2 deletions ckan/logic/action/create.py
Expand Up @@ -16,11 +16,13 @@
group_dict_save,
package_api_to_dict,
package_dict_save,
user_dict_save)
user_dict_save,
activity_dict_save)

from ckan.lib.dictization.model_dictize import (group_dictize,
package_dictize,
user_dictize)
user_dictize,
activity_dictize)


from ckan.logic.schema import default_create_package_schema, default_resource_schema
Expand Down Expand Up @@ -231,6 +233,14 @@ def user_create(context, data_dict):

if not context.get('defer_commit'):
model.repo.commit()

activity_dict = {
'user_id': user.id,
'object_id': user.id,
'activity_type': 'new user',
}
activity_create(context, activity_dict)

context['user'] = user
context['id'] = user.id
log.debug('Created user %s' % str(user.name))
Expand Down Expand Up @@ -278,3 +288,38 @@ def group_create_rest(context, data_dict):

return group_dict

def activity_create(context, activity_dict):
'''Create an activity stream event and add it to the model. Return a
dictionary representation of the new event.
This function doesn't do any authorisation or validation yet, it's only
meant to be used internally and not yet ready to be exposed e.g. via the
API.
Arguments:
context -- Pylons context dict with at least the 'model' key.
activity_dict -- A dict containing the values for the activity stream event
to be created. Should have keys 'user_id', 'object_id', 'activity_type' and
(optionally) 'data'.
'''
model = context['model']

revision = model.repo.new_revision()
revision.author = activity_dict['user']
if 'message' in context:
revision.message = context['message']
else:
revision.message = _(u'REST API: Create activity')
assert not activity_dict.has_key('revision_id')
activity_dict['revision_id'] = revision.id

activity = activity_dict_save(activity_dict, context)

if not context.get('defer_commit'):
model.repo.commit()

log.debug("Created '%s' activity" % activity.activity_type)
return activity_dictize(activity, context)
8 changes: 8 additions & 0 deletions ckan/logic/action/get.py
Expand Up @@ -924,11 +924,16 @@ def render_changed_package_activity(context, activity):
return render('activity_streams/changed_package.html',
extra_vars = {'activity': activity})

def render_new_user_activity(context, activity):
return render('activity_streams/new_user.html',
extra_vars = {'activity': activity})

# Global dictionary mapping activity types to functions that render activity
# dicts to HTML snippets for including in HTML pages.
activity_renderers = {
'new package' : render_new_package_activity,
'changed package' : render_changed_package_activity,
'new user' : render_new_user_activity,
}

def user_activity_list_html(context, data_dict):
Expand All @@ -942,5 +947,8 @@ def user_activity_list_html(context, data_dict):
html = []
for activity in reversed(activity_stream):
activity_type = activity['activity_type']
if not activity_renderers.has_key(activity_type):
raise NotImplementedError, ("No activity renderer for activity "
"type '%s'" % str(activity_type))
html.append(activity_renderers[activity_type](context, activity))
return literal('\n'.join(html))
1 change: 1 addition & 0 deletions ckan/model/__init__.py
Expand Up @@ -23,6 +23,7 @@
from rating import *
from package_relationship import *
from task_status import *
from activity import *
import ckan.migration
from ckan.lib.helpers import OrderedDict, datetime_to_date_str
from vdm.sqlalchemy.base import SQLAlchemySession
Expand Down
16 changes: 16 additions & 0 deletions ckan/templates/activity_streams/new_user.html
@@ -0,0 +1,16 @@
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:i18n="http://genshi.edgewall.org/i18n"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip=""
>
<xi:include href="../_util.html" />
<div>
<div style="font-weight:bold;">
${h.linked_user(activity.user_id)}
<span style="background:yellow;">signed up</span>
${h.render_datetime(activity.timestamp, '%B %d %Y')}
</div>
</div>
</html>
33 changes: 33 additions & 0 deletions ckan/tests/lib/test_dictization.py
Expand Up @@ -10,12 +10,14 @@
from ckan.lib.dictization.model_dictize import (package_dictize,
resource_dictize,
group_dictize,
activity_dictize,
package_to_api1,
package_to_api2,
)
from ckan.lib.dictization.model_save import (package_dict_save,
resource_dict_save,
group_dict_save,
activity_dict_save,
package_api_to_dict,
group_api_to_dict,
package_tag_list_save,
Expand Down Expand Up @@ -963,3 +965,34 @@ def test_19_package_tag_list_save_duplicates(self):

pkg = model.Package.by_name(name)
assert_equal(set([tag.name for tag in pkg.tags]), set(('tag1',)))

def test_20_activity_save(self):

# Add a new Activity object to the database by passing a dict to
# activity_dict_save()
context = {"model": model, "session": model.Session}
user = model.User.by_name(u'tester')
revision = model.repo.new_revision()
activity_dict = {
'user_id': user.id,
'object_id': user.id,
'revision_id': revision.id,
'activity_type': 'changed user'
}
activity_dict_save(activity_dict, context)
model.Session.commit()

# Retrieve the newest Activity object from the database, check that its
# attributes match those of the dict we saved.
activity_obj = model.Session.query(model.Activity).all()[-1]
assert activity_obj.user_id == activity_dict['user_id']
assert activity_obj.object_id == activity_dict['object_id']
assert activity_obj.revision_id == activity_dict['revision_id']
assert activity_obj.activity_type == activity_dict['activity_type']

# The activity object should also have an ID and timestamp.
assert activity_obj.id
assert activity_obj.timestamp

# We didn't pass in any data so this should be empty.
assert not activity_obj.data

0 comments on commit eb62fb7

Please sign in to comment.