Skip to content

Commit

Permalink
Merge branch 'master' of github.com:okfn/ckan into 1635-feature-email…
Browse files Browse the repository at this point in the history
…-notifications-for-activity-streams
  • Loading branch information
Sean Hammond committed Nov 19, 2012
2 parents 7a5df8e + 326a7c0 commit 6388d8d
Show file tree
Hide file tree
Showing 10 changed files with 385 additions and 139 deletions.
4 changes: 2 additions & 2 deletions ckan/controllers/user.py
Expand Up @@ -503,9 +503,9 @@ def dashboard(self, id=None):

c.dashboard_activity_stream = h.dashboard_activity_stream(id)

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

return render('user/dashboard.html')

Expand Down
13 changes: 2 additions & 11 deletions ckan/lib/activity_streams.py
Expand Up @@ -187,13 +187,9 @@ def activity_stream_string_new_related_item():
# A list of activity types that may have details
activity_stream_actions_with_detail = ['changed package']

def activity_list_to_html(context, activity_stream, is_dashboard=False):
def activity_list_to_html(context, activity_stream):
'''Return the given activity stream as a snippet of HTML.'''

# get the last time they read the dashboard
if (is_dashboard):
last_viewed = logic.get_action('dashboard_get_last_viewed')(context, {})

activity_list = [] # These are the activity stream messages.
for activity in activity_stream:
detail = None
Expand Down Expand Up @@ -233,17 +229,12 @@ def activity_list_to_html(context, activity_stream, is_dashboard=False):
for match in matches:
snippet = activity_snippet_functions[match](activity, detail)
data[str(match)] = snippet
timestamp = datetime.datetime.strptime(activity['timestamp'], '%Y-%m-%dT%H:%M:%S.%f').timetuple()
if (is_dashboard):
is_new = ( timestamp > last_viewed )
else:
is_new = False;

activity_list.append({'msg': activity_msg,
'type': activity_type.replace(' ', '-').lower(),
'icon': activity_icon,
'data': data,
'timestamp': activity['timestamp'],
'is_new': is_new})
'is_new': activity.get('is_new', False)})
return literal(base.render('activity_streams/activity_stream_items.html',
extra_vars={'activities': activity_list}))
123 changes: 52 additions & 71 deletions ckan/logic/action/get.py
Expand Up @@ -1734,11 +1734,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 @@ -1757,11 +1753,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 @@ -1786,6 +1778,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 All @@ -1810,11 +1810,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 @@ -2201,39 +2197,41 @@ def group_followee_list(context, data_dict):
def dashboard_activity_list(context, data_dict):
'''Return the authorized user's dashboard activity stream.
:rtype: list of dictionaries
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).
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.
if 'user' not in context:
raise logic.NotAuthorized(
_("You must be logged in to access your dashboard."))
_check_access('dashboard_activity_list', context, data_dict)

model = context['model']
user_id = model.User.get(context['user']).id

userobj = model.User.get(context['user'])
if not userobj:
raise logic.NotAuthorized(
_("You must be logged in to access your dashboard."))
user_id = userobj.id

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)
# FIXME: Filter out activities whose subject or object the user is not
# authorized to read.
activity_objects = model.activity.dashboard_activity_list(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)
activity_dicts = model_dictize.activity_list_dictize(
activity_objects, context)

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()
# 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)

return model_dictize.activity_list_dictize(activity_objects, context)
return activity_dicts


def dashboard_activity_list_html(context, data_dict):
Expand All @@ -2246,55 +2244,38 @@ def dashboard_activity_list_html(context, data_dict):
'''
activity_stream = dashboard_activity_list(context, data_dict)
return activity_streams.activity_list_to_html(context, activity_stream, is_dashboard=True)
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 activity stream.
'''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
'''
# We don't bother to do our own auth check in this function, because we
# assume dashboard_activity_list will do it.
activities = dashboard_activity_list(context, data_dict)

model = context['model']
user = model.User.get(context['user']) # The authorized user.
last_viewed = model.Dashboard.get_activity_stream_last_viewed(user.id)

strptime = datetime.datetime.strptime
fmt = '%Y-%m-%dT%H:%M:%S.%f'
new_activities = [activity for activity in activities if
strptime(activity['timestamp'], fmt) > last_viewed]
return len(new_activities)
_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_get_last_viewed(context, data_dict):
model = context['model']
user = model.User.get(context['user']) # The authorized user.
last_viewed = model.Dashboard.get_activity_stream_last_viewed(user.id)
return last_viewed.timetuple()

def dashboard_mark_activities_as_read(context, data_dict):
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.
'''
if 'user' not in context:
raise logic.NotAuthorized(
_("You must be logged in to access your dashboard."))

_check_access('dashboard_mark_all_new_activities_as_old', context,
data_dict)
model = context['model']

userobj = model.User.get(context['user'])
if not userobj:
raise logic.NotAuthorized(
_("You must be logged in to access your dashboard."))
user_id = userobj.id
user_id = model.User.get(context['user']).id
model.Dashboard.update_activity_stream_last_viewed(user_id)


Expand Down
18 changes: 18 additions & 0 deletions ckan/logic/auth/get.py
Expand Up @@ -189,3 +189,21 @@ def get_site_user(context, data_dict):
return {'success': False, 'msg': 'Only internal services allowed to use this action'}
else:
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 ckan.new_authz.is_authorized('dashboard_activity_list',
context, data_dict)


def dashboard_mark_all_new_activities_as_old(context, data_dict):
return ckan.new_authz.is_authorized('dashboard_activity_list',
context, data_dict)
5 changes: 5 additions & 0 deletions ckan/logic/auth/publisher/get.py
Expand Up @@ -5,6 +5,11 @@
from ckan.logic.auth.publisher import _groups_intersect
from ckan.authz import Authorizer
from ckan.logic.auth import get_package_object, get_group_object, get_resource_object
from ckan.logic.auth.get import (
dashboard_new_activities_count,
dashboard_activity_list,
dashboard_mark_all_new_activities_as_old,
)

def site_read(context, data_dict):
"""\
Expand Down

0 comments on commit 6388d8d

Please sign in to comment.