Skip to content

Log Guide [PT BR]

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

O sistema de log é usado para coletar informações importantes sobre o perfil dos usuários do Amadeus, possibilitando conhecer quais partes são mais acessadas e quanto tempo são gasto nelas, entre outras coisas. Com essas informações nós somos capazes de melhorar o aprendizado dos estudantes dando à ele direções (dicas) para aumentar suas notas.

Dependências

Para ser capaz de usar o sistema de log você precisará importar no arquivo views.py do seu app os seguintes pacotes:

  • LogMixin de core.models: usado para ativar o sistema de log em Class-Based Views;
  • log_decorator de core.decorators: usado para ativar o sistema de log em Function-Based Views;
  • log_decorator_ajax de core.decorators: usado para ativar o sistema de log em Function-Based Views que são ativados por componentes ajax como accordion ou collapse no caso de você precisar armazenar quanto tempo o usuário passou com aquele componente aberto;
  • Log from core.models: usado caso você necessite armazenar o tempo gasto pelo usuário na página;

Como usar

  • Class-Based Views:

    • Primeiro você precisa fazer sua classe extender LogMixin:

         class MyView(..., LogMixin,...):
    • Fazendo isso os seguintes atributos ficaram disponíveis para a sua classe:

      • log_action - Uma string que indica qual foi a ação do usuário (Padrão em inglês) (Ex.: viewed, create, update...)
      • log_resource - Uma string que indica qual foi o recurso (i.e. setor do app) ao qual o usuário realizou a ação (Ex.: forum, course, topic...)
      • log_component - Uma string que indica qual foi o app ao qual o usuário realizou a ação (O nome da app)
      • log_context - Um dicionário que armazena toda informação relevante sobre o contexto do recurso, como todo o caminho para chegar até ele, e informações específicas do recurso.

      Em código:

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

      Obs.: O atributo log_context deve ser explicitamente declarado na sessão de atributos da classe para ser acessado nos métodos da classe para populá-lo. Para seu preenchimento você pode usar qualquer método da Class-Based View (form_valid, dispatch, get_success_url) desde que você tenha toda a informação necessária nele. Em código:

         class MyView(..., LogMixin,...):
              #definicoes de atributos e outros metodos
              def get_success_url(self):
                    #alguma rotina/definicoes
                    self.log_context['key'] = value
                    #possibly more log_context info
                    #outras rotinas/definicoes do metodo

      Obs².: Toda informação colocada no log_context deve serializável para json (É recomendável usar apenas strings)

    • Com esses atributos preenchidos você precisará chamar em um dos seus métodos (o mesmo que você usou para preencher o log_context) a seguinte função:

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

      Essa função recebe como parâmetro todos os atributos de log que estão disponíveis através da adição do LogMixin e o usuário que fez a requisição. É importante garantir que o usuário não seja um usuário anônimo o que pode ser feito pela adição do LoginRequiredMixin (Veja a documentação do Django para mais informações).

    • Para armazenar o tempo gasto pelo usuário naquele recurso você precisa:

      • Adicionar isso em seu log_context:
         self.log_context['timestamp_start'] = str(int(time.time()))
      • Adicionar essa linha depois de chamar a função createLog:
         self.request.session['log_id'] = Log.objects.latest('id').id
    • Aqui está um exemplo completo de como ficaria o código:

         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, ))
                   
               #Nesse caso 'Forum' tem uma relacao com 'topic' (Voce precisa acessar um topic para alcancar o forum)
               #Entao informacao sobre o topic do forum é fornecida ao 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'))
                   
               #Nesse caso 'Forum' tem uma relacao com 'topic' (Voce precisa acessar um topic para alcancar o forum)
               #Entao informacao sobre o topic do forum é fornecida ao 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:

    • Para ter o sistema de log funcionando com function-based views tudo que você precisa fazer é adicionar o seguinte decorator:

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

      Nele, o primeiro parâmetro se refere ao log_component; o segundo ao log_action; e o terceiro ao log_resource;

      Obs.: Para preencher o log_context tudo que você precisa é, no corpo da sua função, adicioná-lo ao parâmetro "log_context" do request. Em código:

         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

      O código completo seria algo como isso:

         #imports e outras funcoes
         @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!")})
    • Se você quiser armazenar o tempo gasto pelo usuário você precisará mudar o decorator para:
      Obs.: Isso é recomendado para requisições ajax, se esse não for o caso você deveria considerar usar Class-Based Views.

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

      Obs.: Os parâmetros são os mesmos de log_decorator.

      Somado a isso você precisará adicionar em seu log_context:

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

      E pegar o último id de log generado para passar para o response:

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

      O código completo seria algo similiar a isso:

         @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