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
authorymh <ymh.work@gmail.com>
Fri, 22 Jul 2016 23:56:47 +0200
changeset 81 709541e05240
parent 80 7732461bbbb2
child 82 ba82858f9cd0
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
server/src/metaeducation/auth.py
server/src/metaeducation/tracking/__init__.py
server/src/metaeducation/tracking/messages.py
server/src/metaeducation/tracking/middleware.py
server/src/metaeducation/tracking/signals_handler.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)
--- 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'
 ]
--- 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)
--- 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)
--- /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)