Commit dbff607509849b49b4ce6478d7332eca1ed3f9bb

Authored by Erik Zambom
1 parent e2dcefc6

Changing log system and adding viewed topic log [Issue: #231]

core/decorators.py
1 1 from django.conf import settings
2 2 import json
  3 +import time
3 4 from functools import wraps
  5 +from django.shortcuts import get_object_or_404
4 6 from .models import Action, Resource, Action_Resource, Log, Notification
5 7  
6 8 def log_decorator(log_component = '', log_action = '', log_resource = ''):
... ... @@ -38,7 +40,6 @@ def log_decorator(log_component = '', log_action = '', log_resource = ''):
38 40 log = Log()
39 41 log.user = request.user
40 42 log.component = log_component
41   - #log.context = json.dumps(request.log_context)
42 43 log.context = request.log_context
43 44 log.action_resource = action_resource
44 45  
... ... @@ -51,6 +52,75 @@ def log_decorator(log_component = '', log_action = '', log_resource = ''):
51 52 return _log_decorator
52 53  
53 54  
  55 +def log_decorator_ajax(log_component = '', log_action = '', log_resource = ''):
  56 +
  57 + def _log_decorator_ajax(view_function):
  58 +
  59 + def _decorator(request, *args, **kwargs):
  60 + view_action = request.GET.get("action")
  61 +
  62 + if view_action == 'open':
  63 + if request.user.is_authenticated:
  64 + action = Action.objects.filter(name = log_action)
  65 + resource = Resource.objects.filter(name = log_resource)
  66 +
  67 + if not action:
  68 + action = Action(name = log_action)
  69 + action.save()
  70 + else:
  71 + action = action[0]
  72 +
  73 + if not resource:
  74 + resource = Resource(name = log_resource)
  75 + resource.save()
  76 + else:
  77 + resource = resource[0]
  78 +
  79 + action_resource = Action_Resource.objects.filter(action = action, resource = resource)
  80 +
  81 + if not action_resource:
  82 + action_resource = Action_Resource(action = action, resource = resource)
  83 + action_resource.save()
  84 + else:
  85 + action_resource = action_resource[0]
  86 +
  87 + log = Log()
  88 + log.user = request.user
  89 + log.component = log_component
  90 + log.context = ""
  91 + log.action_resource = action_resource
  92 +
  93 + log.save()
  94 +
  95 + response = view_function(request, *args, **kwargs)
  96 +
  97 + log = Log.objects.latest('id')
  98 + log.context = request.log_context
  99 + log.save()
  100 + elif view_action == 'close':
  101 + if request.user.is_authenticated:
  102 + log = get_object_or_404(Log, id = request.GET.get('log_id'))
  103 +
  104 + if type(log.context) == dict:
  105 + log_context = log.context
  106 + else:
  107 + log_context = json.loads(log.context)
  108 +
  109 + log_context['timestamp_end'] = str(int(time.time()))
  110 +
  111 + log.context = log_context
  112 +
  113 + log.save()
  114 +
  115 + response = view_function(request, *args, **kwargs)
  116 +
  117 + return response
  118 +
  119 + return wraps(view_function)(_decorator)
  120 +
  121 + return _log_decorator_ajax
  122 +
  123 +
54 124 def notification_decorator(read = False, message = '', actor = None, users = [], not_action='', not_resource='', resource_link=''):
55 125  
56 126 def _notification_decorator(view_function):
... ...
core/middleware.py
... ... @@ -32,3 +32,17 @@ class TimeSpentMiddleware(object):
32 32 log.save()
33 33  
34 34 request.session['log_id'] = None
  35 +
  36 + oppened_logs = Log.objects.filter(user = request.user, context__contains={'timestamp_end': '-1'})
  37 +
  38 + for op_log in oppened_logs:
  39 + if type(op_log.context) == dict:
  40 + log_context = op_log.context
  41 + else:
  42 + log_context = json.loads(op_log.context)
  43 +
  44 + log_context['timestamp_end'] = str(int(time.time()))
  45 +
  46 + op_log.context = log_context
  47 +
  48 + op_log.save()
... ...
courses/static/js/topic.js 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +function openTopic(url, topic, btn) {
  2 + var icon = btn.find('i');
  3 + var action = '', log_id;
  4 +
  5 + if (icon.hasClass('fa-caret-square-o-down')) {
  6 + icon.removeClass('fa-caret-square-o-down');
  7 + icon.addClass('fa-caret-square-o-up');
  8 + action = 'open';
  9 + log_id = -1;
  10 + } else {
  11 + icon.addClass('fa-caret-square-o-down');
  12 + icon.removeClass('fa-caret-square-o-up');
  13 + action = 'close';
  14 + log_id = $(".topic_" + topic).find(".log_id").val();
  15 + }
  16 +
  17 + $.ajax({
  18 + url: url,
  19 + data: {"action": action, "log_id": log_id},
  20 + dataType: 'json',
  21 + success: function (data) {
  22 + if (action == 'open') {
  23 + $(".topic_" + topic).find(".log_id").val(data.log_id);
  24 + }
  25 + },
  26 + error: function(data) {
  27 + console.log('Error');
  28 + }
  29 + });
  30 +}
0 31 \ No newline at end of file
... ...
courses/templates/subject/form_view_student.html
... ... @@ -6,6 +6,7 @@
6 6 <script src="{% static 'js/file.js' %}"></script>
7 7 <script type="text/javascript" src="{% static 'js/material.js' %}"></script>
8 8 <script type = "text/javascript" src="{% static 'links.js' %}"></script>
  9 + <script type="text/javascript" src="{% static 'js/topic.js' %}"></script>
9 10 {% endblock %}
10 11 <div class="cards-detail">
11 12 <div class="panel-group accordion ui-accordion ui-widget ui-helper-reset ui-sortable" role="tablist" aria-multiselectable="false">
... ... @@ -13,7 +14,7 @@
13 14 <div class="panel-heading topic ui-sortable-handle" role="tab">
14 15 <div class="row">
15 16 <div class="col-md-1 moreAccordion" data-toggle="collapse" data-parent="#accordion-{{topic.slug}}" href=".collapseTopic-{{topic.slug}}" aria-expanded="false" aria-controls="collapseTopic-{{topic.slug}}">
16   - <button class="btn btn-default btn-sm caret-square"><i class="fa fa-caret-square-o-down fa-2x" aria-hidden="true"></i></button>
  17 + <button class="btn btn-default btn-sm caret-square" onclick="openTopic('{% url 'course:topic_log' topic.id %}', '{{topic.id}}', $(this));"><i class="fa fa-caret-square-o-down fa-2x" aria-hidden="true"></i></button>
17 18 </div>
18 19 <div class="col-xs-9 col-md-9 titleTopic">
19 20 <a href="{% url 'course:view_topic' topic.slug %}" role="button">
... ... @@ -22,7 +23,7 @@
22 23 </div>
23 24 </div>
24 25 </div>
25   - <div class="panel-collapse collapseTopic-{{topic.slug}} collapse in" role="tabpanel" aria-labelledby="heading_{{topic.id}}" aria-expanded="true" aria-hidden="false">
  26 + <div class="panel-collapse collapseTopic-{{topic.slug}} collapse" role="tabpanel" aria-labelledby="heading_{{topic.id}}" aria-expanded="false" aria-hidden="true">
26 27 <div class="panel-body">
27 28 <div class="presentation">
28 29 <p>
... ... @@ -32,8 +33,7 @@
32 33 </p>
33 34  
34 35 </div>
35   - </div>
36   - </div>
  36 +
37 37  
38 38 {% if not professor_links %}
39 39 <div class="row">
... ... @@ -82,4 +82,6 @@
82 82 {% include "links/update_link.html" %}
83 83 {% endif %}
84 84 </div>
  85 + </div>
  86 + </div>
85 87 </div>
86 88 \ No newline at end of file
... ...
courses/templates/subject/form_view_teacher.html
1 1 {% load static i18n list_topic_foruns permission_tags widget_tweaks professor_access list_topic_exercises %}
2 2  
  3 +<script type="text/javascript" src="{% static 'js/topic.js' %}"></script>
  4 +
3 5 <div class="panel panel-default cards-detail">
4 6 <div class="panel-heading topic">
5 7 <div class="row">
6 8 <div class="col-md-1 moreAccordion" data-toggle="collapse" data-parent="#accordion-{{topic.slug}}" href=".collapseTopic-{{topic.slug}}" aria-expanded="false" aria-controls="collapseTopic-{{topic.slug}}">
7   - <button class="btn btn-default btn-sm caret-square"><i class="fa fa-caret-square-o-down fa-2x" aria-hidden="true"></i></button>
  9 + <button class="btn btn-default btn-sm caret-square" onclick="openTopic('{% url 'course:topic_log' topic.id %}', '{{topic.id}}', $(this));"><i class="fa fa-caret-square-o-down fa-2x" aria-hidden="true"></i></button>
8 10 </div>
9 11 <div class="col-xs-9 col-md-9 titleTopic">
10 12 <a href="{% url 'course:view_topic' topic.slug %}" role="button">
... ... @@ -32,7 +34,8 @@
32 34 </div>
33 35 </div>
34 36 </div>
35   - <div class="panel-collapse collapseTopic-{{topic.slug}} topic_{{ topic.id }} collapse in" role="tabpanel" aria-labelledby="heading_{{topic.id}}" aria-expanded="true" aria-hidden="false">
  37 + <div class="panel-collapse collapseTopic-{{topic.slug}} topic_{{ topic.id }} collapse" role="tabpanel" aria-labelledby="heading_{{topic.id}}" aria-expanded="false" aria-hidden="true">
  38 + <input type="hidden" class="log_id" />
36 39 <div class="panel-body">
37 40  
38 41 {# dados do tópico no modo de visualização #}
... ...
courses/urls.py
... ... @@ -34,6 +34,7 @@ urlpatterns = [
34 34 url(r'^subjects/file-material-view/(?P<slug>[\w_-]+)/$', views.FileMaterialView.as_view(), name='file_material_view'),
35 35 url(r'^links/',include('links.urls',namespace = 'links')),
36 36 url(r'^exercise/', include('exercise.urls', namespace='exercise')),
  37 + url(r'^topic/(?P<topic>[\w_-]+)/$', views.topic_log, name='topic_log'),
37 38 url(r'^(?P<slug>[\w_-]+)/', include([
38 39 url(r'^$', views.CourseView.as_view(), name='view'),
39 40 url(r'^(?P<category>[\w_-]+)/$', views.CourseView.as_view(), name='view_filter')
... ...
courses/views.py
1 1 from .forms import CourseForm, UpdateCourseForm, CategoryCourseForm, SubjectForm,TopicForm,ActivityForm
2 2 from .models import Course, Subject, CourseCategory, Topic, SubjectCategory, Activity, CategorySubject
3   -from core.decorators import log_decorator
  3 +from core.decorators import log_decorator, log_decorator_ajax
4 4 from core.mixins import LogMixin, NotificationMixin
5 5 from core.models import Log
6 6 from courses.models import Material
... ... @@ -482,6 +482,7 @@ class UpdateCatView(LoginRequiredMixin, HasRoleMixin, generic.UpdateView):
482 482 messages.success(self.request, _('Category "%s" updated successfully!')%(objeto))
483 483 #return reverse_lazy('course:update_cat', kwargs={'slug' : self.object.slug})
484 484 return reverse_lazy('course:manage_cat')
  485 +
485 486 class DeleteCatView(LoginRequiredMixin, HasRoleMixin, generic.DeleteView):
486 487  
487 488 allowed_roles = ['professor', 'system_admin']
... ... @@ -970,6 +971,35 @@ class FileMaterialView(LoginRequiredMixin, LogMixin, generic.DetailView):
970 971  
971 972 return super(FileMaterialView, self).dispatch(*args, **kwargs)
972 973  
  974 +@login_required
  975 +@log_decorator_ajax("courses", "viewed", "topic")
  976 +def topic_log(request, topic):
  977 + action = request.GET.get('action')
  978 +
  979 + if action == 'open':
  980 + topic = get_object_or_404(Topic, id = topic)
  981 + log_context = {}
  982 + log_context['topic_id'] = topic.id
  983 + log_context['topic_name'] = topic.name
  984 + log_context['topic_slug'] = topic.slug
  985 + log_context['subject_id'] = topic.subject.id
  986 + log_context['subject_name'] = topic.subject.name
  987 + log_context['subject_slug'] = topic.subject.slug
  988 + log_context['course_id'] = topic.subject.course.id
  989 + log_context['course_name'] = topic.subject.course.name
  990 + log_context['course_slug'] = topic.subject.course.slug
  991 + log_context['course_category_id'] = topic.subject.course.category.id
  992 + log_context['course_category_name'] = topic.subject.course.category.name
  993 + log_context['timestamp_start'] = str(int(time.time()))
  994 + log_context['timestamp_end'] = "-1"
  995 + request.log_context = log_context
  996 + log_id = Log.objects.latest('id').id
  997 +
  998 + response = JsonResponse({"message": "ok", "log_id": log_id})
  999 + else:
  1000 + response = JsonResponse({"message": "ok"})
  1001 +
  1002 + return response
973 1003  
974 1004 #API VIEWS
975 1005 class CourseViewSet(viewsets.ModelViewSet):
... ...