Skip to content

Log Guide [EN US]

Erik Zambom edited this page Dec 3, 2016 · 7 revisions

The log system is used to collect important information about Amadeus's users profile, giving the possibility to know which parts are more accessed and how much time are spent on it, among other things. With this bunch of information we are able to improve the students learning by telling him directions to increase his grades.

Dependencies

To be able to use the log system you'll need import in the views.py file of your app the following packages:

  • LogMixin from core.models: used to activate the log system in Class-Based Views;
  • log_decorator from core.decorators: used to activate the log system in Function-Based Views;
  • log_decorator_ajax from core.decorators: used to activate the log system in Function-Based Views that are activated by ajax components like accordion or collapse in case you need to store the time spent by the user with the component open;
  • Log from core.models: used in case you need store the time spent by the user in the page;

How to use

  • Class-Based Views:

    • First you need to make your class extends the LogMixin:

         class MyView(..., LogMixin,...):
    • Doing this the following attributes will be available to your class:

      • log_action - A string that indicates what was the user's action (Ex.: viewed, create, update...)
      • log_resource - A string that indicates what was the resource (i.e. part of the app) whose user has taken action (Ex.: forum, course, topic...)
      • log_component - A string that indicates what was the app whose user has taken action (The app's name)
      • log_context - A dict that stores all relevant information about the context of the resource, like all the path to get to it, and info about the resource specifically.

      In code:

         class MyView(..., LogMixin,...):
              log_action = ""
              log_resource = ""
              log_component = ""
              log_context = {}
              #other attributes

      Obs.: The log_context attribute should be explicit declared in the class attribute's section in order to access it in the class methods to populate it. For it's population you can use any of the class-based views methods (form_valid, dispatch, get_success_url) since you have all the required information in it. In code:

         class MyView(..., LogMixin,...):
              #attribute definitions and other methods
              def get_success_url(self):
                    #some routine/definitions
                    self.log_context['key'] = value
                    #possibly more log_context info
                    #other method routines/definitions

      Obs².: All information putted in log_context should be json serializable (It's recommended to use only strings)

    • With the fields fulfilled the attributes you'll need to call in one of your methods (the same as you used to populate the log_context) the following function:

         super(ClassViewName, self).createLog(self.request.user, self.log_component, self.log_action, self.log_resource, self.log_context)

      This function receives as parameter all the log attributes that are available through the addition of LogMixin and the user that made the request. It is important to ensure that the user is not an anonymous user which can be made by the addition of LoginRequiredMixin (See the Django documentation about it).

    • In order to store the time spent by the user in that resource you should:

      • Add this in your log_context:
         self.log_context['timestamp_start'] = str(int(time.time()))
      • Add this line after calling the createLog function:
         self.request.session['log_id'] = Log.objects.latest('id').id
    • Here is a full example on how it would be the code:

         import time
         from django.shortcuts import render, get_object_or_404
         from django.core.urlresolvers import reverse, reverse_lazy
         from core.models import Log
         from core.mixins import LogMixin
         from .forms import ForumForm
         from .models import Forum
      
         class CreateForumView(LoginRequiredMixin, generic.edit.CreateView, LogMixin):
           log_component = "forum"
           log_action = "create"
           log_resource = "forum"
           log_context = {}
      
           login_url = reverse_lazy("core:home")	
           redirect_field_name = 'next'
      
           template_name = 'forum/forum_form.html'
           form_class = ForumForm
      
           def get_success_url(self):
               self.success_url = reverse('course:forum:render_forum', args = (self.object.id, ))
                   
               #In this case Forum has a relation with topic (You need to access a topic to reach the forum)
               #So info about the forum's topic is provided to the log_context
               self.log_context['forum_id'] = self.object.id
               self.log_context['forum_name'] = self.object.name
               self.log_context['topic_id'] = self.object.topic.id
               self.log_context['topic_name'] = self.object.topic.name
               self.log_context['topic_slug'] = self.object.topic.slug
      
               super(CreateForumView, self).createLog(self.request.user, self.log_component, self.log_action, self.log_resource, self.log_context)
      
               return self.success_url
      
         class ForumDetailView(LoginRequiredMixin, LogMixin, generic.DetailView):
           log_component = "forum"
           log_action = "viewed"
           log_resource = "forum"
           log_context = {}
           
           login_url = reverse_lazy("core:home")	
           redirect_field_name = 'next'
      
           model = Forum
           template_name = 'forum/forum_view.html'
           context_object_name = 'forum'
      
           def dispatch(self, *args, **kwargs):
               forum = get_object_or_404(Forum, slug = self.kwargs.get('slug'))
                   
               #In this case Forum has a relation with topic (You need to access a topic to reach the forum)
               #So info about the forum's topic is provided to the log_context
               self.log_context['forum_id'] = forum.id
               self.log_context['forum_name'] = forum.name
               self.log_context['topic_id'] = forum.topic.id
               self.log_context['topic_name'] = forum.topic.name
               self.log_context['topic_slug'] = forum.topic.slug
               self.log_context['timestamp_start'] = str(int(time.time()))
      
               super(ForumDetailView, self).createLog(self.request.user, self.log_component, self.log_action, self.log_resource, self.log_context)
      
               self.request.session['log_id'] = Log.objects.latest('id').id
      
               return super(ForumDetailView, self).dispatch(*args, **kwargs)
      
           def get_context_data(self, **kwargs):
               context = super(ForumDetailView, self).get_context_data(**kwargs)
               forum = get_object_or_404(Forum, slug = self.kwargs.get('slug'))
      
               context['forum'] = forum
      
               return context
  • Function-Based Views:

    • To get log_system working with function-based views all you have to do is to add the following decorator:

         @log_decorator("", "", "")

      In it, the first parameter refers to log_component; the second to log_action; e the third to log_resource;

      Obs.: To populate the log_context all you need is, in the body of your function, add it in the request parameter "log_context". In code:

         log_context = {}
         log_context['course_id'] = course.id
         log_context['course_name'] = course.name
         log_context['course_slug'] = course.slug
         log_context['course_category_id'] = course.category.id
         log_context['course_category_name'] = course.category.name
      
         request.log_context = log_context

      The full code would be something like this:

         #imports and other functions
         @log_decorator("course", "subscribe", "course")
         def subscribe_course(request, slug):
            course = get_object_or_404(Course, slug = slug)
      
            course.students.add(request.user)
            log_context = {}
            log_context['course_id'] = course.id
            log_context['course_name'] = course.name
            log_context['course_slug'] = course.slug
            log_context['course_category_id'] = course.category.id
            log_context['course_category_name'] = course.category.name
      
            request.log_context = log_context
      
            return JsonResponse({"status": "ok", "message": _("Successfully subscribed to the course!")})
    • If you want to store the time spent by the user you'll need to change the decorator to:
      Obs.: This is recommended in ajax requests, if this is not the case you should consider using Class-Based Views

         @log_decorator_ajax("", "", "")

      Obs.: The parameters are the same as in log_decorator.

      In addition to it you'll need to add to your log_context:

         log_context['timestamp_start'] = str(int(time.time()))
         log_context['timestamp_end'] = "-1"

      And get the last generated log id to pass to response:

         log_id = Log.objects.latest('id').id
      
         response = JsonResponse({"message": "ok", "log_id": log_id})

      The full code would be similar to this:

         @log_decorator_ajax("courses", "viewed", "topic")
         def topic_log(request, topic):
            topic = get_object_or_404(Topic, id = topic)
            log_context = {}
            log_context['topic_id'] = topic.id
            log_context['topic_name'] = topic.name
            log_context['topic_slug'] = topic.slug
            log_context['subject_id'] = topic.subject.id
            log_context['subject_name'] = topic.subject.name
            log_context['subject_slug'] = topic.subject.slug
            log_context['timestamp_start'] = str(int(time.time()))
            log_context['timestamp_end'] = "-1"
            
            request.log_context = log_context
            
            log_id = Log.objects.latest('id').id
      
            response = JsonResponse({"message": "ok", "log_id": log_id})
      
            return response