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 | 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() | ... | ... |
... | ... | @@ -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): | ... | ... |