Skip to content

Commit

Permalink
Merge branch 'master' into 2939-orgs
Browse files Browse the repository at this point in the history
Conflicts:
	ckan/logic/auth/get.py
	ckan/logic/auth/publisher/get.py
	ckan/templates/header.html
	ckan/tests/functional/api/test_activity.py

simple enough merge conflicts
  • Loading branch information
tobes committed Nov 19, 2012
2 parents 0635ad1 + 326a7c0 commit 3914033
Show file tree
Hide file tree
Showing 23 changed files with 983 additions and 452 deletions.
7 changes: 7 additions & 0 deletions ckan/controllers/user.py
Expand Up @@ -500,6 +500,13 @@ def dashboard(self, id=None):
'user': c.user or c.author, 'for_view': True}
data_dict = {'id': id, 'user_obj': c.userobj}
self._setup_template_variables(context, data_dict)

c.dashboard_activity_stream = h.dashboard_activity_stream(id)

# Mark the user's new activities as old whenever they view their
# dashboard page.
get_action('dashboard_mark_all_new_activities_as_old')(context, {})

return render('user/dashboard.html')

def follow(self, id):
Expand Down
5 changes: 4 additions & 1 deletion ckan/lib/activity_streams.py
@@ -1,4 +1,5 @@
import re
import datetime

from pylons.i18n import _
from webhelpers.html import literal
Expand Down Expand Up @@ -244,10 +245,12 @@ def activity_list_to_html(context, activity_stream):
for match in matches:
snippet = activity_snippet_functions[match](activity, detail)
data[str(match)] = snippet

activity_list.append({'msg': activity_msg,
'type': activity_type.replace(' ', '-').lower(),
'icon': activity_icon,
'data': data,
'timestamp': activity['timestamp']})
'timestamp': activity['timestamp'],
'is_new': activity.get('is_new', False)})
return literal(base.render('activity_streams/activity_stream_items.html',
extra_vars={'activities': activity_list}))
10 changes: 10 additions & 0 deletions ckan/lib/base.py
Expand Up @@ -213,6 +213,16 @@ def __before__(self, action, **params):
self._identify_user()
i18n.handle_request(request, c)

# If the user is logged in add their number of new activities to the
# template context.
if c.userobj:
from ckan.logic import get_action
new_activities_count = get_action(
'dashboard_new_activities_count')
context = {'model': model, 'session': model.Session,
'user': c.user or c.author}
c.new_activities = new_activities_count(context, {})

def _identify_user(self):
'''
Identifies the user using two methods:
Expand Down
115 changes: 75 additions & 40 deletions ckan/logic/action/get.py
@@ -1,6 +1,7 @@
import uuid
import logging
import json
import datetime

from pylons import config
from pylons.i18n import _
Expand Down Expand Up @@ -1857,11 +1858,7 @@ def user_activity_list(context, data_dict):
_check_access('user_show', context, data_dict)
model = context['model']
user_id = _get_or_bust(data_dict, 'id')
query = model.Session.query(model.Activity)
query = query.filter_by(user_id=user_id)
query = query.order_by(_desc(model.Activity.timestamp))
query = query.limit(15)
activity_objects = query.all()
activity_objects = model.activity.user_activity_list(user_id)
return model_dictize.activity_list_dictize(activity_objects, context)

def package_activity_list(context, data_dict):
Expand All @@ -1880,11 +1877,7 @@ def package_activity_list(context, data_dict):
_check_access('package_show', context, data_dict)
model = context['model']
package_id = _get_or_bust(data_dict, 'id')
query = model.Session.query(model.Activity)
query = query.filter_by(object_id=package_id)
query = query.order_by(_desc(model.Activity.timestamp))
query = query.limit(15)
activity_objects = query.all()
activity_objects = model.activity.package_activity_list(package_id)
return model_dictize.activity_list_dictize(activity_objects, context)

def group_activity_list(context, data_dict):
Expand All @@ -1909,6 +1902,14 @@ def group_activity_list(context, data_dict):
group_show = logic.get_action('group_show')
group_id = group_show(context, {'id': group_id})['id']

# FIXME: The SQLAlchemy below should be moved into ckan/model/activity.py
# (to encapsulate SQLALchemy in the model and avoid using it from the
# logic) but it can't be because it requires the list of dataset_ids which
# comes from logic.group_package_show() (and I don't want to access the
# logic from the model). Need to change it to get the dataset_ids from the
# model instead. There seems to be multiple methods for getting a group's
# datasets, some in the logic and some in the model.

# Get a list of the IDs of the group's datasets.
group_package_show = logic.get_action('group_package_show')
datasets = group_package_show(context, {'id': group_id})
Expand Down Expand Up @@ -1957,11 +1958,7 @@ def recently_changed_packages_activity_list(context, data_dict):
# FIXME: Filter out activities whose subject or object the user is not
# authorized to read.
model = context['model']
query = model.Session.query(model.Activity)
query = query.filter(model.Activity.activity_type.endswith('package'))
query = query.order_by(_desc(model.Activity.timestamp))
query = query.limit(15)
activity_objects = query.all()
activity_objects = model.activity.recently_changed_packages_activity_list()
return model_dictize.activity_list_dictize(activity_objects, context)

def activity_detail_list(context, data_dict):
Expand Down Expand Up @@ -2362,52 +2359,90 @@ def group_followee_list(context, data_dict):


def dashboard_activity_list(context, data_dict):
'''Return the dashboard activity stream of the given user.
'''Return the authorized user's dashboard activity stream.
:param id: the id or name of the user
:type id: string
Unlike the activity dictionaries returned by other *_activity_list actions,
these activity dictionaries have an extra boolean value with key 'is_new'
that tells you whether the activity happened since the user last viewed her
dashboard ('is_new': True) or not ('is_new': False).
:rtype: list of dictionaries
The user's own activities are always marked 'is_new': False.
:rtype: list of activity dictionaries
'''
# FIXME: Filter out activities whose subject or object the user is not
# authorized to read.
model = context['model']
user_id = _get_or_bust(data_dict, 'id')
_check_access('dashboard_activity_list', context, data_dict)

activity_query = model.Session.query(model.Activity)
user_followees_query = activity_query.join(model.UserFollowingUser, model.UserFollowingUser.object_id == model.Activity.user_id)
dataset_followees_query = activity_query.join(model.UserFollowingDataset, model.UserFollowingDataset.object_id == model.Activity.object_id)
model = context['model']
user_id = model.User.get(context['user']).id

from_user_query = activity_query.filter(model.Activity.user_id==user_id)
about_user_query = activity_query.filter(model.Activity.object_id==user_id)
user_followees_query = user_followees_query.filter(model.UserFollowingUser.follower_id==user_id)
dataset_followees_query = dataset_followees_query.filter(model.UserFollowingDataset.follower_id==user_id)
# FIXME: Filter out activities whose subject or object the user is not
# authorized to read.
activity_objects = model.activity.dashboard_activity_list(user_id)

activity_dicts = model_dictize.activity_list_dictize(
activity_objects, context)

# Mark the new (not yet seen by user) activities.
strptime = datetime.datetime.strptime
fmt = '%Y-%m-%dT%H:%M:%S.%f'
last_viewed = model.Dashboard.get_activity_stream_last_viewed(user_id)
for activity in activity_dicts:
if activity['user_id'] == user_id:
# Never mark the user's own activities as new.
activity['is_new'] = False
else:
activity['is_new'] = (strptime(activity['timestamp'], fmt)
> last_viewed)

query = from_user_query.union(about_user_query).union(
user_followees_query).union(dataset_followees_query)
query = query.order_by(_desc(model.Activity.timestamp))
query = query.limit(15)
activity_objects = query.all()
return activity_dicts

return model_dictize.activity_list_dictize(activity_objects, context)

def dashboard_activity_list_html(context, data_dict):
'''Return the dashboard activity stream of the given user as HTML.
'''Return the authorized user's dashboard activity stream as HTML.
The activity stream is rendered as a snippet of HTML meant to be included
in an HTML page, i.e. it doesn't have any HTML header or footer.
:param id: The id or name of the user.
:type id: string
:rtype: string
'''
activity_stream = dashboard_activity_list(context, data_dict)
return activity_streams.activity_list_to_html(context, activity_stream)


def dashboard_new_activities_count(context, data_dict):
'''Return the number of new activities in the user's dashboard.
Return the number of new activities in the authorized user's dashboard
activity stream.
Activities from the user herself are not counted by this function even
though they appear in the dashboard (users don't want to be notified about
things they did themselves).
:rtype: int
'''
_check_access('dashboard_new_activities_count', context, data_dict)
activities = logic.get_action('dashboard_activity_list')(
context, data_dict)
return len([activity for activity in activities if activity['is_new']])


def dashboard_mark_all_new_activities_as_old(context, data_dict):
'''Mark all the authorized user's new dashboard activities as old.
This will reset dashboard_new_activities_count to 0.
'''
_check_access('dashboard_mark_all_new_activities_as_old', context,
data_dict)
model = context['model']
user_id = model.User.get(context['user']).id
model.Dashboard.update_activity_stream_last_viewed(user_id)


def _unpick_search(sort, allowed_fields=None, total=None):
''' This is a helper function that takes a sort string
eg 'name asc, last_modified desc' and returns a list of
Expand Down
19 changes: 19 additions & 0 deletions ckan/logic/auth/get.py
Expand Up @@ -196,5 +196,24 @@ def get_site_user(context, data_dict):
else:
return {'success': True}


def member_roles_list(context, data_dict):
return {'success': True}


def dashboard_activity_list(context, data_dict):
if 'user' in context:
return {'success': True}
else:
return {'success': False,
'msg': _("You must be logged in to access your dashboard.")}


def dashboard_new_activities_count(context, data_dict):
return new_authz.is_authorized('dashboard_activity_list',
context, data_dict)


def dashboard_mark_all_new_activities_as_old(context, data_dict):
return new_authz.is_authorized('dashboard_activity_list',
context, data_dict)
16 changes: 16 additions & 0 deletions ckan/migration/versions/062_add_dashboard_table.py
@@ -0,0 +1,16 @@
from sqlalchemy import *
from migrate import *

def upgrade(migrate_engine):
metadata = MetaData()
metadata.bind = migrate_engine
migrate_engine.execute('''
CREATE TABLE dashboard (
user_id text NOT NULL,
activity_stream_last_viewed timestamp without time zone NOT NULL
);
ALTER TABLE dashboard
ADD CONSTRAINT dashboard_pkey PRIMARY KEY (user_id);
ALTER TABLE dashboard
ADD CONSTRAINT dashboard_user_id_fkey FOREIGN KEY (user_id) REFERENCES "user"(id) ON UPDATE CASCADE ON DELETE CASCADE;
''')
File renamed without changes.
3 changes: 3 additions & 0 deletions ckan/model/__init__.py
Expand Up @@ -149,6 +149,9 @@
DomainObjectOperation,
DomainObject,
)
from dashboard import (
Dashboard,
)

import ckan.migration

Expand Down

0 comments on commit 3914033

Please sign in to comment.