Commit dbff607509849b49b4ce6478d7332eca1ed3f9bb
1 parent
e2dcefc6
Exists in
master
and in
5 other branches
Changing log system and adding viewed topic log [Issue: #231]
Showing
7 changed files
with
158 additions
and
8 deletions
Show diff stats
core/decorators.py
1 | from django.conf import settings | 1 | from django.conf import settings |
2 | import json | 2 | import json |
3 | +import time | ||
3 | from functools import wraps | 4 | from functools import wraps |
5 | +from django.shortcuts import get_object_or_404 | ||
4 | from .models import Action, Resource, Action_Resource, Log, Notification | 6 | from .models import Action, Resource, Action_Resource, Log, Notification |
5 | 7 | ||
6 | def log_decorator(log_component = '', log_action = '', log_resource = ''): | 8 | def log_decorator(log_component = '', log_action = '', log_resource = ''): |
@@ -38,7 +40,6 @@ def log_decorator(log_component = '', log_action = '', log_resource = ''): | @@ -38,7 +40,6 @@ def log_decorator(log_component = '', log_action = '', log_resource = ''): | ||
38 | log = Log() | 40 | log = Log() |
39 | log.user = request.user | 41 | log.user = request.user |
40 | log.component = log_component | 42 | log.component = log_component |
41 | - #log.context = json.dumps(request.log_context) | ||
42 | log.context = request.log_context | 43 | log.context = request.log_context |
43 | log.action_resource = action_resource | 44 | log.action_resource = action_resource |
44 | 45 | ||
@@ -51,6 +52,75 @@ def log_decorator(log_component = '', log_action = '', log_resource = ''): | @@ -51,6 +52,75 @@ def log_decorator(log_component = '', log_action = '', log_resource = ''): | ||
51 | return _log_decorator | 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 | def notification_decorator(read = False, message = '', actor = None, users = [], not_action='', not_resource='', resource_link=''): | 124 | def notification_decorator(read = False, message = '', actor = None, users = [], not_action='', not_resource='', resource_link=''): |
55 | 125 | ||
56 | def _notification_decorator(view_function): | 126 | def _notification_decorator(view_function): |
core/middleware.py
@@ -32,3 +32,17 @@ class TimeSpentMiddleware(object): | @@ -32,3 +32,17 @@ class TimeSpentMiddleware(object): | ||
32 | log.save() | 32 | log.save() |
33 | 33 | ||
34 | request.session['log_id'] = None | 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() |
@@ -0,0 +1,30 @@ | @@ -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 | \ No newline at end of file | 31 | \ No newline at end of file |
courses/templates/subject/form_view_student.html
@@ -6,6 +6,7 @@ | @@ -6,6 +6,7 @@ | ||
6 | <script src="{% static 'js/file.js' %}"></script> | 6 | <script src="{% static 'js/file.js' %}"></script> |
7 | <script type="text/javascript" src="{% static 'js/material.js' %}"></script> | 7 | <script type="text/javascript" src="{% static 'js/material.js' %}"></script> |
8 | <script type = "text/javascript" src="{% static 'links.js' %}"></script> | 8 | <script type = "text/javascript" src="{% static 'links.js' %}"></script> |
9 | + <script type="text/javascript" src="{% static 'js/topic.js' %}"></script> | ||
9 | {% endblock %} | 10 | {% endblock %} |
10 | <div class="cards-detail"> | 11 | <div class="cards-detail"> |
11 | <div class="panel-group accordion ui-accordion ui-widget ui-helper-reset ui-sortable" role="tablist" aria-multiselectable="false"> | 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,7 +14,7 @@ | ||
13 | <div class="panel-heading topic ui-sortable-handle" role="tab"> | 14 | <div class="panel-heading topic ui-sortable-handle" role="tab"> |
14 | <div class="row"> | 15 | <div class="row"> |
15 | <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 | <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 | </div> | 18 | </div> |
18 | <div class="col-xs-9 col-md-9 titleTopic"> | 19 | <div class="col-xs-9 col-md-9 titleTopic"> |
19 | <a href="{% url 'course:view_topic' topic.slug %}" role="button"> | 20 | <a href="{% url 'course:view_topic' topic.slug %}" role="button"> |
@@ -22,7 +23,7 @@ | @@ -22,7 +23,7 @@ | ||
22 | </div> | 23 | </div> |
23 | </div> | 24 | </div> |
24 | </div> | 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 | <div class="panel-body"> | 27 | <div class="panel-body"> |
27 | <div class="presentation"> | 28 | <div class="presentation"> |
28 | <p> | 29 | <p> |
@@ -32,8 +33,7 @@ | @@ -32,8 +33,7 @@ | ||
32 | </p> | 33 | </p> |
33 | 34 | ||
34 | </div> | 35 | </div> |
35 | - </div> | ||
36 | - </div> | 36 | + |
37 | 37 | ||
38 | {% if not professor_links %} | 38 | {% if not professor_links %} |
39 | <div class="row"> | 39 | <div class="row"> |
@@ -82,4 +82,6 @@ | @@ -82,4 +82,6 @@ | ||
82 | {% include "links/update_link.html" %} | 82 | {% include "links/update_link.html" %} |
83 | {% endif %} | 83 | {% endif %} |
84 | </div> | 84 | </div> |
85 | + </div> | ||
86 | + </div> | ||
85 | </div> | 87 | </div> |
86 | \ No newline at end of file | 88 | \ No newline at end of file |
courses/templates/subject/form_view_teacher.html
1 | {% load static i18n list_topic_foruns permission_tags widget_tweaks professor_access list_topic_exercises %} | 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 | <div class="panel panel-default cards-detail"> | 5 | <div class="panel panel-default cards-detail"> |
4 | <div class="panel-heading topic"> | 6 | <div class="panel-heading topic"> |
5 | <div class="row"> | 7 | <div class="row"> |
6 | <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}}"> | 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 | </div> | 10 | </div> |
9 | <div class="col-xs-9 col-md-9 titleTopic"> | 11 | <div class="col-xs-9 col-md-9 titleTopic"> |
10 | <a href="{% url 'course:view_topic' topic.slug %}" role="button"> | 12 | <a href="{% url 'course:view_topic' topic.slug %}" role="button"> |
@@ -32,7 +34,8 @@ | @@ -32,7 +34,8 @@ | ||
32 | </div> | 34 | </div> |
33 | </div> | 35 | </div> |
34 | </div> | 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 | <div class="panel-body"> | 39 | <div class="panel-body"> |
37 | 40 | ||
38 | {# dados do tópico no modo de visualização #} | 41 | {# dados do tópico no modo de visualização #} |
courses/urls.py
@@ -34,6 +34,7 @@ urlpatterns = [ | @@ -34,6 +34,7 @@ urlpatterns = [ | ||
34 | url(r'^subjects/file-material-view/(?P<slug>[\w_-]+)/$', views.FileMaterialView.as_view(), name='file_material_view'), | 34 | url(r'^subjects/file-material-view/(?P<slug>[\w_-]+)/$', views.FileMaterialView.as_view(), name='file_material_view'), |
35 | url(r'^links/',include('links.urls',namespace = 'links')), | 35 | url(r'^links/',include('links.urls',namespace = 'links')), |
36 | url(r'^exercise/', include('exercise.urls', namespace='exercise')), | 36 | url(r'^exercise/', include('exercise.urls', namespace='exercise')), |
37 | + url(r'^topic/(?P<topic>[\w_-]+)/$', views.topic_log, name='topic_log'), | ||
37 | url(r'^(?P<slug>[\w_-]+)/', include([ | 38 | url(r'^(?P<slug>[\w_-]+)/', include([ |
38 | url(r'^$', views.CourseView.as_view(), name='view'), | 39 | url(r'^$', views.CourseView.as_view(), name='view'), |
39 | url(r'^(?P<category>[\w_-]+)/$', views.CourseView.as_view(), name='view_filter') | 40 | url(r'^(?P<category>[\w_-]+)/$', views.CourseView.as_view(), name='view_filter') |
courses/views.py
1 | from .forms import CourseForm, UpdateCourseForm, CategoryCourseForm, SubjectForm,TopicForm,ActivityForm | 1 | from .forms import CourseForm, UpdateCourseForm, CategoryCourseForm, SubjectForm,TopicForm,ActivityForm |
2 | from .models import Course, Subject, CourseCategory, Topic, SubjectCategory, Activity, CategorySubject | 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 | from core.mixins import LogMixin, NotificationMixin | 4 | from core.mixins import LogMixin, NotificationMixin |
5 | from core.models import Log | 5 | from core.models import Log |
6 | from courses.models import Material | 6 | from courses.models import Material |
@@ -482,6 +482,7 @@ class UpdateCatView(LoginRequiredMixin, HasRoleMixin, generic.UpdateView): | @@ -482,6 +482,7 @@ class UpdateCatView(LoginRequiredMixin, HasRoleMixin, generic.UpdateView): | ||
482 | messages.success(self.request, _('Category "%s" updated successfully!')%(objeto)) | 482 | messages.success(self.request, _('Category "%s" updated successfully!')%(objeto)) |
483 | #return reverse_lazy('course:update_cat', kwargs={'slug' : self.object.slug}) | 483 | #return reverse_lazy('course:update_cat', kwargs={'slug' : self.object.slug}) |
484 | return reverse_lazy('course:manage_cat') | 484 | return reverse_lazy('course:manage_cat') |
485 | + | ||
485 | class DeleteCatView(LoginRequiredMixin, HasRoleMixin, generic.DeleteView): | 486 | class DeleteCatView(LoginRequiredMixin, HasRoleMixin, generic.DeleteView): |
486 | 487 | ||
487 | allowed_roles = ['professor', 'system_admin'] | 488 | allowed_roles = ['professor', 'system_admin'] |
@@ -970,6 +971,35 @@ class FileMaterialView(LoginRequiredMixin, LogMixin, generic.DetailView): | @@ -970,6 +971,35 @@ class FileMaterialView(LoginRequiredMixin, LogMixin, generic.DetailView): | ||
970 | 971 | ||
971 | return super(FileMaterialView, self).dispatch(*args, **kwargs) | 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 | #API VIEWS | 1004 | #API VIEWS |
975 | class CourseViewSet(viewsets.ModelViewSet): | 1005 | class CourseViewSet(viewsets.ModelViewSet): |