# HG changeset patch # User ymh # Date 1469224607 -7200 # Node ID 709541e0524079a731c5765fc432917263da64dd # Parent 7732461bbbb2435b0651575944bf6960d5ef5fc9 Small refactoring for tracking handler to correct a but in unregister and also allow to switch user when the api is called with the act_as parameter diff -r 7732461bbbb2 -r 709541e05240 server/src/metaeducation/auth.py --- a/server/src/metaeducation/auth.py Tue Jul 12 17:40:27 2016 +0200 +++ b/server/src/metaeducation/auth.py Fri Jul 22 23:56:47 2016 +0200 @@ -8,6 +8,8 @@ from rest_framework.authentication import BaseAuthentication from rest_framework import exceptions +from metaeducation.tracking import change_handlers_user + logger = logging.getLogger(__name__) @@ -87,4 +89,11 @@ logger.debug("CLIENT CREDENTIAL AUTH: user %r is authenticated by token %r, auth success", external_id, token) + + + if hasattr(request, 'renkan_request_id') and user != getattr(request,'user', None): + # Re-register the tracking handlers with the new user + logger.debug("CLIENT CREDENTIAL AUTH: Change the registered tracking handlers to user %r", user.external_id) + change_handlers_user(user, request.renkan_request_id) + return (user, None) diff -r 7732461bbbb2 -r 709541e05240 server/src/metaeducation/tracking/__init__.py --- a/server/src/metaeducation/tracking/__init__.py Tue Jul 12 17:40:27 2016 +0200 +++ b/server/src/metaeducation/tracking/__init__.py Fri Jul 22 23:56:47 2016 +0200 @@ -1,8 +1,10 @@ from .tasks import send_tracking_data from .messages import send_close_renkan, send_open_edit_renkan, send_open_read_renkan, \ send_delete_renkan, send_create_renkan, send_update_renkan +from .signals_handler import register_handlers, register_pre_save_handlers, unregister_handlers, change_handlers_user __all__ = [ 'send_tracking_data', 'send_close_renkan', 'send_open_read_renkan', 'send_open_edit_renkan', - 'send_delete_renkan', 'send_update_renkan', 'send_create_renkan' + 'send_delete_renkan', 'send_update_renkan', 'send_create_renkan', 'register_handlers', + 'unregister_handlers', 'register_pre_save_handlers', 'change_handlers_user' ] diff -r 7732461bbbb2 -r 709541e05240 server/src/metaeducation/tracking/messages.py --- a/server/src/metaeducation/tracking/messages.py Tue Jul 12 17:40:27 2016 +0200 +++ b/server/src/metaeducation/tracking/messages.py Fri Jul 22 23:56:47 2016 +0200 @@ -2,7 +2,6 @@ import json import logging import pytz -import functools from .tasks import send_tracking_data @@ -47,10 +46,10 @@ msg = { 'actor': { 'objectType': 'Agent', - 'name': current_user, + 'name': current_user or 'n/a', 'account': { 'homePage': 'https://www.metaeducation.fr/Utilisateurs/', - 'name': current_user + 'name': current_user or 'n/a' } }, 'verb': verbNode, @@ -135,16 +134,12 @@ send_tracking_data.delay(msg) -def _send_operation_renkan(action, renkan, current_user, content=None): - if not content: - content = json.loads(renkan.content) - title = renkan.title - renkan_guid = renkan.renkan_guid - else: - content = json.loads(content) - title = content.get('title', '') - renkan_guid = content.get('id', content.get('_id', '')) - msg = get_base_message(action, renkan_guid, current_user) +def send_delete_renkan(renkan, current_user): + content = json.loads(renkan.content) + title = renkan.title + renkan_guid = renkan.renkan_guid + + msg = get_base_message('delete', renkan_guid, current_user) msg['object'] = { **(msg.get('object',{})), **{ @@ -161,9 +156,31 @@ } send_tracking_data.delay(msg) +# This must be called with a revision object containing the new content BEFORE the object save +def send_update_renkan(revision, current_user): + renkan = revision.parent_renkan + previousContent = json.loads(renkan.content) + content = json.loads(revision.content) + title = renkan.title + renkan_guid = renkan.renkan_guid + msg = get_base_message('update', renkan_guid, current_user) + msg['object'] = { + **(msg.get('object',{})), + **{ + "definition": { + "name": { + 'fr-FR': title + }, + "type": "http://www.w3.org/ns/activitystreams#Renkan", + "extensions": { + 'http://www.w3.org/ns/activitystreams#Data': content, + 'http://www.w3.org/ns/activitystreams#previousData': previousContent, + } + } + } + } + send_tracking_data.delay(msg) -send_delete_renkan = functools.partial(_send_operation_renkan, 'delete') -send_update_renkan = functools.partial(_send_operation_renkan, 'update') def send_create_renkan(renkan, current_user): msg = get_base_message('create', renkan.renkan_guid, current_user) diff -r 7732461bbbb2 -r 709541e05240 server/src/metaeducation/tracking/middleware.py --- a/server/src/metaeducation/tracking/middleware.py Tue Jul 12 17:40:27 2016 +0200 +++ b/server/src/metaeducation/tracking/middleware.py Fri Jul 22 23:56:47 2016 +0200 @@ -6,11 +6,10 @@ from renkanmanager.models import Renkan, Revision -from . import send_delete_renkan, send_create_renkan, send_update_renkan +from . import send_update_renkan, register_handlers, unregister_handlers, register_pre_save_handlers logger = logging.getLogger(__name__) -# Inspired by https://github.com/Atomidata/django-audit-log/blob/master/audit_log/middleware.py class TrackingMiddleware(object): def process_request(self, request): @@ -20,42 +19,23 @@ else: user = None - request.renkan_request_id = uuid.uuid4() + request.renkan_request_id = str(uuid.uuid4()) + register_handlers(user, request.renkan_request_id) - pre_delete.connect(curry(self.renkan_delete, user), sender=Renkan, dispatch_uid = (self.__class__, request.renkan_request_id,), weak=False) - post_save.connect(curry(self.revision_save, user), sender=Revision, dispatch_uid = (self.__class__, request.renkan_request_id,), weak=False) def process_response(self, request, response): if hasattr(request, 'renkan_request_id'): - pre_delete.disconnect(dispatch_uid = (self.__class__, request.renkan_request_id,)) - post_save.disconnect(dispatch_uid = (self.__class__, request.renkan_request_id,)) + unregister_handlers(request.renkan_request_id) return response - def process_exception(self, request, exception): - if hasattr(request, 'renkan_request_id'): - pre_delete.disconnect(dispatch_uid = (self.__class__, request.renkan_request_id,)) - post_save.disconnect(dispatch_uid = (self.__class__, request.renkan_request_id,)) - return None - def process_view(self, request, view_func, view_args, view_kwargs): if view_func.__name__ == 'RenkanDetail' and view_func.__module__ == 'renkanmanager.api.views' and request.method == 'PUT' and 'renkan_guid' in view_kwargs: if hasattr(request, 'user') and request.user.is_authenticated(): user = request.user.external_id else: user = None - send_update_renkan(None, user, request.body.decode('utf-8')) + # this test should never fail + if not hasattr(request, 'renkan_request_id'): + request.renkan_request_id = str(uuid.uuid4()) + register_pre_save_handlers(user, request.renkan_request_id) return None - - - def renkan_delete(self, user, sender, **kwargs): - renkan = kwargs.get('instance', None) - if not renkan: - return - send_delete_renkan(renkan, user) - - def revision_save(self, user, sender, **kwargs): - revision = kwargs.get('instance', None) - if not revision: - return - if kwargs.get('created', False) and revision.parent_renkan.revision_count <= 1: - send_create_renkan(revision.parent_renkan, user) diff -r 7732461bbbb2 -r 709541e05240 server/src/metaeducation/tracking/signals_handler.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/src/metaeducation/tracking/signals_handler.py Fri Jul 22 23:56:47 2016 +0200 @@ -0,0 +1,58 @@ +from functools import partial + +import logging + +from django.db.models.signals import pre_delete, post_save, pre_save + +from renkanmanager.models import Renkan, Revision +from . import send_delete_renkan, send_create_renkan, send_update_renkan + +logger = logging.getLogger(__name__) + +# Inspired by https://github.com/Atomidata/django-audit-log/blob/master/audit_log/middleware.py + +def register_handlers(user, uid): + pre_delete.connect(partial(renkan_delete, user), sender = Renkan, dispatch_uid = uid, weak = False) + post_save.connect(partial(revision_post_save, user), sender = Revision, dispatch_uid = uid, weak = False) + +def register_pre_save_handlers(user, uid): + pre_save.connect(partial(revision_pre_save, user), sender = Revision, dispatch_uid = uid, weak = False) + + +def unregister_handlers(uid): + pre_delete.disconnect(dispatch_uid = uid, sender = Renkan) + post_save.disconnect(dispatch_uid = uid, sender = Revision) + pre_save.disconnect(dispatch_uid = uid, sender = Revision) + +def change_handlers_user(user, uid): + if pre_delete.disconnect(dispatch_uid = uid, sender = Renkan): + pre_delete.connect(partial(renkan_delete, user), sender = Renkan, dispatch_uid = uid, weak = False) + if post_save.disconnect(dispatch_uid = uid, sender = Revision): + post_save.connect(partial(revision_post_save, user), sender = Revision, dispatch_uid = uid, weak = False) + if pre_save.disconnect(dispatch_uid = uid, sender = Revision): + pre_save.connect(partial(revision_pre_save, user), sender = Revision, dispatch_uid = uid, weak = False) + + +def renkan_delete(user, sender, **kwargs): + renkan = kwargs.get('instance', None) + if not renkan: + return + send_delete_renkan(renkan, user) + +def revision_post_save(user, sender, **kwargs): + revision = kwargs.get('instance', None) + + if not revision: + return + if kwargs.get('created', False) and revision.parent_renkan.revision_count <= 1: + send_create_renkan(revision.parent_renkan, user) + +#This handler is used and registered in a single case, i.e. PUT on renkanmanager.api.views.RenkanDetail +def revision_pre_save(user, sender, **kwargs): + revision = kwargs.get('instance', None) + + if not revision: + return + + # we look for renkan update : i.e. creation of a revision with a revision count > 0 or simple revision update + send_update_renkan(revision, user)