add tracking of renkan create, delete and update
authorymh <ymh.work@gmail.com>
Tue, 05 Jul 2016 13:19:38 +0200
changeset 77 06f627e804b6
parent 76 d3804d2eaf2d
child 78 8e1440b83f0c
add tracking of renkan create, delete and update
server/src/metaeducation/settings/__init__.py
server/src/metaeducation/static/metaeducation/css/style.css
server/src/metaeducation/templates/renkan_list.html
server/src/metaeducation/tracking/__init__.py
server/src/metaeducation/tracking/messages.py
server/src/metaeducation/tracking/middleware.py
server/src/metaeducation/urls.py
server/src/metaeducation/views/renkan.py
--- a/server/src/metaeducation/settings/__init__.py	Mon Jul 04 00:31:19 2016 +0200
+++ b/server/src/metaeducation/settings/__init__.py	Tue Jul 05 13:19:38 2016 +0200
@@ -59,7 +59,7 @@
     ),
     'DEFAULT_PARSER_CLASSES': (
         'rest_framework.parsers.JSONParser',
-    ),               
+    ),
     'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'
 }
 
@@ -72,7 +72,9 @@
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
     'django.middleware.security.SecurityMiddleware','guardian.backends.ObjectPermissionBackend',
-    'metaeducation.middleware.MtdcLoginRequiredWithContextMiddleware'
+    'metaeducation.middleware.MtdcLoginRequiredWithContextMiddleware',
+    'metaeducation.tracking.middleware.TrackingMiddleware'
+
 )
 
 ROOT_URLCONF = 'metaeducation.urls'
--- a/server/src/metaeducation/static/metaeducation/css/style.css	Mon Jul 04 00:31:19 2016 +0200
+++ b/server/src/metaeducation/static/metaeducation/css/style.css	Tue Jul 05 13:19:38 2016 +0200
@@ -4,17 +4,17 @@
 
 .errorModal {
     font-family: Arial, Helvetica, sans-serif;
-    display: none; 
-    position: fixed; 
-    z-index: 1000; 
-    padding-top: 100px; 
+    display: none;
+    position: fixed;
+    z-index: 1000;
+    padding-top: 100px;
     left: 0;
     top: 0;
-    width: 100%; 
-    height: 100%; 
-    overflow: auto; 
-    background-color: rgb(0,0,0); 
-    background-color: rgba(35,0,0,0.4); 
+    width: 100%;
+    height: 100%;
+    overflow: auto;
+    background-color: rgb(0,0,0);
+    background-color: rgba(35,0,0,0.4);
 }
 
 /* Modal Content */
@@ -34,7 +34,7 @@
 
 /* Add Animation */
 @-webkit-keyframes animatetop {
-    from {top:-300px; opacity:0} 
+    from {top:-300px; opacity:0}
     to {top:0; opacity:1}
 }
 
@@ -65,4 +65,23 @@
 
 .errorMsg{
 	display: none;
+}
+
+.renkanlist-action {
+    padding: 0 0.3em;
+}
+.renkan-del-form {
+    display: inline-block;
+}
+
+.renkan-del-button {
+    color: #337ab7;
+    text-decoration: none;
+    border: none;
+    background: none;
+}
+
+.renkan-del-button:hover, .renkan-del-button:focus {
+    color: #23527c;
+    text-decoration: underline;
 }
\ No newline at end of file
--- a/server/src/metaeducation/templates/renkan_list.html	Mon Jul 04 00:31:19 2016 +0200
+++ b/server/src/metaeducation/templates/renkan_list.html	Tue Jul 05 13:19:38 2016 +0200
@@ -29,9 +29,13 @@
       <td>{{renkan.title}}</td>
       <td>{{renkan.creation_date}}</td>
       <td>
-        <a href="{% url 'front_view_renkan' renkan_guid=renkan.renkan_guid %}" >View</a> 
-        <a href="{% url 'front_edit_renkan' renkan_guid=renkan.renkan_guid %}" >Edit</a> 
-        <a href="{% url 'front_delete_renkan' renkan_guid=renkan.renkan_guid %}" >Delete</a>
+        <a href="{% url 'front_view_renkan' renkan_guid=renkan.renkan_guid %}" class="renkanlist-action">View</a>
+        <a href="{% url 'front_edit_renkan' renkan_guid=renkan.renkan_guid %}"  class="renkanlist-action">Edit</a>
+        <form action="{% url 'front_delete_renkan' %}" method="post" class="renkan-del-form" >
+            {% csrf_token %}
+            <input type="hidden" name="renkan_guid" value="{{renkan.renkan_guid}}">
+            <button type="submit" class="renkan-del-button renkan-list-action">Delete</button>
+        </form>
       </td>
     </tr>
     {% endfor %}
@@ -43,4 +47,4 @@
     <br><br>
   </div>
 </footer>
-{% endblock main_content %}
\ No newline at end of file
+{% endblock main_content %}
--- a/server/src/metaeducation/tracking/__init__.py	Mon Jul 04 00:31:19 2016 +0200
+++ b/server/src/metaeducation/tracking/__init__.py	Tue Jul 05 13:19:38 2016 +0200
@@ -1,4 +1,8 @@
 from .tasks import send_tracking_data
-from .messages import send_close_renkan, send_open_edit_renkan, send_open_read_renkan
+from .messages import send_close_renkan, send_open_edit_renkan, send_open_read_renkan, \
+    send_delete_renkan, send_create_renkan, send_update_renkan
 
-__all__ = ['send_tracking_data', 'send_close_renkan', 'send_open_read_renkan', 'send_open_edit_renkan']
+__all__ = [
+    'send_tracking_data', 'send_close_renkan', 'send_open_read_renkan', 'send_open_edit_renkan',
+    'send_delete_renkan', 'send_update_renkan', 'send_create_renkan'
+]
--- a/server/src/metaeducation/tracking/messages.py	Mon Jul 04 00:31:19 2016 +0200
+++ b/server/src/metaeducation/tracking/messages.py	Tue Jul 05 13:19:38 2016 +0200
@@ -2,6 +2,7 @@
 import json
 import logging
 import pytz
+import functools
 
 from .tasks import send_tracking_data
 
@@ -118,7 +119,7 @@
     msg = get_base_message('open_edit', renkan.renkan_guid, current_user, registration)
 
     msg['object'] = {
-        **(msg['object']),
+        **(msg.get('object', {})),
         **{
             "definition": {
                 "name": {
@@ -133,3 +134,58 @@
     }
 
     send_tracking_data(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)
+    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,
+                }
+            }
+        }
+    }
+    send_tracking_data(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)
+    msg['object'] = {
+        **(msg.get('object', {})),
+        **{
+            "definition": {
+                "name": {
+                    'fr-FR': renkan.title
+                },
+                "type": "http://www.w3.org/ns/activitystreams#Renkan",
+                "extensions": {
+                    'http://www.w3.org/ns/activitystreams#Data': json.loads(renkan.content),
+                }
+            }
+        }
+    }
+    if renkan.source_revision:
+        msg['context']['extensions'] = {
+            **msg.get('context', {}).get('extensions', {}),
+            **{
+                'http://liris.renkantracking.org/fromCreate': {'renkan': renkan.source_revision.parent_renkan.renkan_guid, 'revision': renkan.source_revision.revision_guid}
+            }
+        }
+    send_tracking_data(msg)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/metaeducation/tracking/middleware.py	Tue Jul 05 13:19:38 2016 +0200
@@ -0,0 +1,59 @@
+import logging
+import uuid
+
+from django.db.models.signals import pre_delete, post_save
+from django.utils.functional import curry
+
+from renkanmanager.models import Renkan, Revision
+
+from . import send_delete_renkan, send_create_renkan, send_update_renkan
+
+logger = logging.getLogger(__name__)
+
+class TrackingMiddleware(object):
+    def process_request(self, request):
+        if not request.method in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
+            if hasattr(request, 'user') and request.user.is_authenticated():
+                user = request.user.external_id
+            else:
+                user = None
+
+            request.renkan_request_id = uuid.uuid4()
+
+            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,))
+        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'))
+        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)
--- a/server/src/metaeducation/urls.py	Mon Jul 04 00:31:19 2016 +0200
+++ b/server/src/metaeducation/urls.py	Tue Jul 05 13:19:38 2016 +0200
@@ -34,7 +34,7 @@
     url(r'^front/view/(?P<renkan_guid>[\w-]+)/$', ViewRenkanView.as_view(), name='front_view_renkan'),
     url(r'^tracking/$', UITrackingView.as_view(), name='tracking_view'),
     url(r'^tracking/close/$', UITrackingViewClose.as_view(), name='tracking_view_close'),
-    url(r'^front/delete/(?P<renkan_guid>[\w-]+)/$', staff_member_required(DeleteRenkanView.as_view()), name='front_delete_renkan')
+    url(r'^front/delete/$', staff_member_required(DeleteRenkanView.as_view()), name='front_delete_renkan')
 ]
 
 urlpatterns += staticfiles_urlpatterns()
--- a/server/src/metaeducation/views/renkan.py	Mon Jul 04 00:31:19 2016 +0200
+++ b/server/src/metaeducation/views/renkan.py	Tue Jul 05 13:19:38 2016 +0200
@@ -1,5 +1,6 @@
 import logging, json, uuid
 
+from django import forms
 from django.conf import settings
 from django.core.urlresolvers import reverse
 from django.http import HttpResponse, HttpResponseRedirect
@@ -72,10 +73,17 @@
         send_open_edit_renkan(renkan, str(request.user.external_id), str(context['registration']))
         return self.render_to_response(context)
 
+class DeleteForm(forms.Form):
+    renkan_guid = forms.UUIDField()
 
 class DeleteRenkanView(View):
 
-    def get(self, request, renkan_guid):
-        request.method = "DELETE"
-        delete_response = RenkanDetail.as_view()(request, renkan_guid)
-        return HttpResponseRedirect(request.META.get('HTTP_REFERER', settings.BASE_URL))
+    def post(self, request):
+        form = DeleteForm(request.POST)
+        if form.is_valid():
+            renkan_guid = form.cleaned_data['renkan_guid']
+            logger.debug("DELETE FORM VALID %r", form.cleaned_data)
+            request.method = "DELETE"
+            delete_response = RenkanDetail.as_view()(request, str(renkan_guid))
+            return HttpResponseRedirect(request.META.get('HTTP_REFERER', settings.BASE_URL))
+        return HttpResponse('field renkan_guid unknown or bad format', status=400)