Introducing notifications
This commit is contained in:
parent
20ba507095
commit
65f8bdc6c0
@ -15,6 +15,7 @@ from eve import Eve
|
|||||||
from eve.auth import TokenAuth
|
from eve.auth import TokenAuth
|
||||||
from eve.io.mongo import Validator
|
from eve.io.mongo import Validator
|
||||||
|
|
||||||
|
|
||||||
RFC1123_DATE_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
|
RFC1123_DATE_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
|
||||||
|
|
||||||
|
|
||||||
@ -118,6 +119,9 @@ from application.utils.authorization import check_permissions
|
|||||||
from application.utils.gcs import update_file_name
|
from application.utils.gcs import update_file_name
|
||||||
from application.utils.algolia import algolia_index_user_save
|
from application.utils.algolia import algolia_index_user_save
|
||||||
from application.utils.algolia import algolia_index_node_save
|
from application.utils.algolia import algolia_index_node_save
|
||||||
|
from application.utils.activities import activity_create
|
||||||
|
from application.utils.activities import activity_subscribe
|
||||||
|
# from application.utils.activities import notification_parse
|
||||||
from modules.file_storage import process_file
|
from modules.file_storage import process_file
|
||||||
from modules.file_storage import delete_file
|
from modules.file_storage import delete_file
|
||||||
from modules.file_storage import generate_link
|
from modules.file_storage import generate_link
|
||||||
@ -164,6 +168,22 @@ def before_inserting_nodes(items):
|
|||||||
if project:
|
if project:
|
||||||
item['project'] = project['_id']
|
item['project'] = project['_id']
|
||||||
|
|
||||||
|
def after_inserting_nodes(items):
|
||||||
|
for item in items:
|
||||||
|
activity_create(item['user'], 'node', item['_id'])
|
||||||
|
if item['node_type'] == 'comment':
|
||||||
|
verb = 'commented'
|
||||||
|
else:
|
||||||
|
verb = 'posted'
|
||||||
|
activity_subscribe(
|
||||||
|
item['user'],
|
||||||
|
verb,
|
||||||
|
'node',
|
||||||
|
item['_id'],
|
||||||
|
'node',
|
||||||
|
item['parent']
|
||||||
|
)
|
||||||
|
|
||||||
def item_parse_attachments(response):
|
def item_parse_attachments(response):
|
||||||
"""Before returning a response, check if the 'attachments' property is
|
"""Before returning a response, check if the 'attachments' property is
|
||||||
defined. If yes, load the file (for the moment only images) in the required
|
defined. If yes, load the file (for the moment only images) in the required
|
||||||
@ -229,16 +249,21 @@ def project_node_type_has_method(response):
|
|||||||
if not check_permissions(node_type, 'GET', append_allowed_methods=True):
|
if not check_permissions(node_type, 'GET', append_allowed_methods=True):
|
||||||
return abort(403)
|
return abort(403)
|
||||||
|
|
||||||
|
# def before_returning_notifications(response):
|
||||||
|
# for item in response['_items']:
|
||||||
|
# notification_parse(item)
|
||||||
|
|
||||||
app.on_fetched_item_nodes += before_returning_item_permissions
|
app.on_fetched_item_nodes += before_returning_item_permissions
|
||||||
app.on_fetched_item_nodes += item_parse_attachments
|
app.on_fetched_item_nodes += item_parse_attachments
|
||||||
app.on_fetched_resource_nodes += before_returning_resource_permissions
|
app.on_fetched_resource_nodes += before_returning_resource_permissions
|
||||||
app.on_fetched_resource_nodes += resource_parse_attachments
|
app.on_fetched_resource_nodes += resource_parse_attachments
|
||||||
app.on_fetched_item_node_types += before_returning_item_permissions
|
app.on_fetched_item_node_types += before_returning_item_permissions
|
||||||
|
# app.on_fetched_resource_notifications += before_returning_notifications
|
||||||
app.on_fetched_resource_node_types += before_returning_resource_permissions
|
app.on_fetched_resource_node_types += before_returning_resource_permissions
|
||||||
app.on_replace_nodes += before_replacing_node
|
app.on_replace_nodes += before_replacing_node
|
||||||
app.on_replaced_nodes += after_replacing_node
|
app.on_replaced_nodes += after_replacing_node
|
||||||
app.on_insert_nodes += before_inserting_nodes
|
app.on_insert_nodes += before_inserting_nodes
|
||||||
|
app.on_inserted_nodes += after_inserting_nodes
|
||||||
app.on_fetched_item_projects += before_returning_item_permissions
|
app.on_fetched_item_projects += before_returning_item_permissions
|
||||||
app.on_fetched_item_projects += project_node_type_has_method
|
app.on_fetched_item_projects += project_node_type_has_method
|
||||||
app.on_fetched_resource_projects += before_returning_resource_permissions
|
app.on_fetched_resource_projects += before_returning_resource_permissions
|
||||||
|
132
pillar/application/utils/activities.py
Normal file
132
pillar/application/utils/activities.py
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
from eve.methods.post import post_internal
|
||||||
|
from application import app
|
||||||
|
|
||||||
|
# def notification_parse(notification):
|
||||||
|
# # TODO: finish fixing this
|
||||||
|
# activities_collection = app.data.driver.db['activities']
|
||||||
|
# users_collection = app.data.driver.db['users']
|
||||||
|
# nodes_collection = app.data.driver.db['nodes']
|
||||||
|
# activity = activities_collection.find_one({'_id': notification['_id']})
|
||||||
|
# actor = users_collection.find_one({'_id': activity['actor_user']})
|
||||||
|
# # Context is optional
|
||||||
|
# context_object_type = None
|
||||||
|
# context_object_name = None
|
||||||
|
# context_object_url = None
|
||||||
|
|
||||||
|
# if activity['object_type'] == 'node':
|
||||||
|
# node = nodes_collection.find_one({'_id': activity['object']})
|
||||||
|
# # project = Project.find(node.project, {
|
||||||
|
# # 'projection': '{"name":1, "url":1}'}, api=api)
|
||||||
|
# # Initial support only for node_type comments
|
||||||
|
# if node['node_type'] == 'comment':
|
||||||
|
# # comment = Comment.query.get_or_404(notification_object.object_id)
|
||||||
|
# node['parent'] = nodes_collection.find_one({'_id': node['parent']})
|
||||||
|
# object_type = 'comment'
|
||||||
|
# object_name = ''
|
||||||
|
|
||||||
|
# object_url = url_for('nodes.view', node_id=node._id, redir=1)
|
||||||
|
# if node.parent.user == current_user.objectid:
|
||||||
|
# owner = "your {0}".format(node.parent.node_type)
|
||||||
|
# else:
|
||||||
|
# parent_comment_user = User.find(node.parent.user, api=api)
|
||||||
|
# owner = "{0}'s {1}".format(parent_comment_user.username,
|
||||||
|
# node.parent.node_type)
|
||||||
|
|
||||||
|
# context_object_type = node.parent.node_type
|
||||||
|
# context_object_name = owner
|
||||||
|
# context_object_url = url_for('nodes.view', node_id=node.parent._id, redir=1)
|
||||||
|
# if activity.verb == 'replied':
|
||||||
|
# action = 'replied to'
|
||||||
|
# elif activity.verb == 'commented':
|
||||||
|
# action = 'left a comment on'
|
||||||
|
# else:
|
||||||
|
# action = activity.verb
|
||||||
|
# else:
|
||||||
|
# return None
|
||||||
|
# else:
|
||||||
|
# return None
|
||||||
|
|
||||||
|
# return dict(
|
||||||
|
# _id=notification._id,
|
||||||
|
# username=actor.username,
|
||||||
|
# username_avatar=actor.gravatar(),
|
||||||
|
# action=action,
|
||||||
|
# object_type=object_type,
|
||||||
|
# object_name=object_name,
|
||||||
|
# object_url=object_url,
|
||||||
|
# context_object_type=context_object_type,
|
||||||
|
# context_object_name=context_object_name,
|
||||||
|
# context_object_url=context_object_url,
|
||||||
|
# date=pretty_date(activity._created),
|
||||||
|
# is_read=notification.is_read,
|
||||||
|
# # is_subscribed=notification.is_subscribed
|
||||||
|
# )
|
||||||
|
|
||||||
|
def notification_get_subscriptions(context_object_type, context_object_id, actor_user_id):
|
||||||
|
subscriptions_collection = app.data.driver.db['activities-subscriptions']
|
||||||
|
lookup = {
|
||||||
|
'user': {"$ne": actor_user_id},
|
||||||
|
'context_object_type': context_object_type,
|
||||||
|
'context_object': context_object_id,
|
||||||
|
'is_subscribed': True,
|
||||||
|
}
|
||||||
|
print lookup
|
||||||
|
return subscriptions_collection.find(lookup)
|
||||||
|
|
||||||
|
|
||||||
|
def activity_create(user_id, context_object_type, context_object_id):
|
||||||
|
"""Subscribe a user to changes for a specific context. We create a subscription
|
||||||
|
if none is found.
|
||||||
|
|
||||||
|
:param user_id: id of the user we are going to subscribe
|
||||||
|
:param context_object_type: hardcoded index, check the notifications/model.py
|
||||||
|
:param context_object_id: object id, to be traced with context_object_type_id
|
||||||
|
"""
|
||||||
|
subscriptions_collection = app.data.driver.db['activities-subscriptions']
|
||||||
|
lookup = {
|
||||||
|
'user': user_id,
|
||||||
|
'context_object_type': context_object_type,
|
||||||
|
'context_object': context_object_id
|
||||||
|
}
|
||||||
|
subscription = subscriptions_collection.find_one(lookup)
|
||||||
|
|
||||||
|
# If no subscription exists, we create one
|
||||||
|
if not subscription:
|
||||||
|
post_internal('activities-subscriptions', lookup)
|
||||||
|
|
||||||
|
|
||||||
|
def activity_subscribe(actor_user_id, verb, object_type, object_id,
|
||||||
|
context_object_type, context_object_id):
|
||||||
|
"""Add a notification object and creates a notification for each user that
|
||||||
|
- is not the original author of the post
|
||||||
|
- is actively subscribed to the object
|
||||||
|
|
||||||
|
This works using the following pattern:
|
||||||
|
|
||||||
|
ACTOR -> VERB -> OBJECT -> CONTEXT
|
||||||
|
|
||||||
|
:param actor_user_id: id of the user who is changing the object
|
||||||
|
:param verb: the action on the object ('commented', 'replied')
|
||||||
|
:param object_type: hardcoded name
|
||||||
|
:param object_id: object id, to be traced with object_type_id
|
||||||
|
"""
|
||||||
|
|
||||||
|
subscriptions = notification_get_subscriptions(
|
||||||
|
context_object_type, context_object_id, actor_user_id)
|
||||||
|
|
||||||
|
if subscriptions.count():
|
||||||
|
activity = dict(
|
||||||
|
actor_user=actor_user_id,
|
||||||
|
verb=verb,
|
||||||
|
object_type=object_type,
|
||||||
|
object=object_id,
|
||||||
|
context_object_type=context_object_type,
|
||||||
|
context_object=context_object_id
|
||||||
|
)
|
||||||
|
|
||||||
|
activity = post_internal('activities', activity)
|
||||||
|
for subscription in subscriptions:
|
||||||
|
notification = dict(
|
||||||
|
user=subscription['user'],
|
||||||
|
activity=activity[0]['_id'])
|
||||||
|
post_internal('notifications', notification)
|
@ -20,6 +20,16 @@ _file_embedded_schema = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_required_user_embedded_schema = {
|
||||||
|
'type': 'objectid',
|
||||||
|
'required': True,
|
||||||
|
'data_relation': {
|
||||||
|
'resource': 'users',
|
||||||
|
'field': '_id',
|
||||||
|
'embeddable': True
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
_activity_object_type = {
|
_activity_object_type = {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'required': True,
|
'required': True,
|
||||||
@ -251,7 +261,6 @@ nodes_schema = {
|
|||||||
},
|
},
|
||||||
'picture': {
|
'picture': {
|
||||||
'type': 'objectid',
|
'type': 'objectid',
|
||||||
'nullable': True,
|
|
||||||
'data_relation': {
|
'data_relation': {
|
||||||
'resource': 'files',
|
'resource': 'files',
|
||||||
'field': '_id',
|
'field': '_id',
|
||||||
@ -629,10 +638,7 @@ projects_schema = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
activities_subscriptions_schema = {
|
activities_subscriptions_schema = {
|
||||||
'user': {
|
'user': _required_user_embedded_schema,
|
||||||
'type': 'objectid',
|
|
||||||
'required': True
|
|
||||||
},
|
|
||||||
'context_object_type': _activity_object_type,
|
'context_object_type': _activity_object_type,
|
||||||
'context_object': {
|
'context_object': {
|
||||||
'type': 'objectid',
|
'type': 'objectid',
|
||||||
@ -646,16 +652,18 @@ activities_subscriptions_schema = {
|
|||||||
},
|
},
|
||||||
'web': {
|
'web': {
|
||||||
'type': 'boolean',
|
'type': 'boolean',
|
||||||
|
'default': True
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'is_subscribed': {
|
||||||
|
'type': 'boolean',
|
||||||
|
'default': True
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
activities_schema = {
|
activities_schema = {
|
||||||
'actor_user': {
|
'actor_user': _required_user_embedded_schema,
|
||||||
'type': 'objectid',
|
|
||||||
'required': True
|
|
||||||
},
|
|
||||||
'verb': {
|
'verb': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'required': True
|
'required': True
|
||||||
@ -673,13 +681,10 @@ activities_schema = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
notifications_schema = {
|
notifications_schema = {
|
||||||
'user': {
|
'user': _required_user_embedded_schema,
|
||||||
'type': 'objectid',
|
|
||||||
'required': True
|
|
||||||
},
|
|
||||||
'activity': {
|
'activity': {
|
||||||
'type': 'objectid',
|
'type': 'objectid',
|
||||||
'required': True
|
'required': True,
|
||||||
},
|
},
|
||||||
'is_read': {
|
'is_read': {
|
||||||
'type': 'boolean',
|
'type': 'boolean',
|
||||||
@ -752,20 +757,14 @@ projects = {
|
|||||||
|
|
||||||
activities = {
|
activities = {
|
||||||
'schema': activities_schema,
|
'schema': activities_schema,
|
||||||
'public_item_methods': None,
|
|
||||||
'public_methods': None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
activities_subscriptions = {
|
activities_subscriptions = {
|
||||||
'schema': activities_subscriptions_schema,
|
'schema': activities_subscriptions_schema,
|
||||||
'public_item_methods': None,
|
|
||||||
'public_methods': None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
notifications = {
|
notifications = {
|
||||||
'schema': activities_subscriptions_schema,
|
'schema': notifications_schema,
|
||||||
'public_item_methods': None,
|
|
||||||
'public_methods': None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user