From 492c3d63597701e331a22aad279e182c569f03a4 Mon Sep 17 00:00:00 2001 From: Braulio Bhavamitra Date: Fri, 21 Aug 2015 11:12:05 -0300 Subject: [PATCH] analytics: measure time on page --- plugins/analytics/controllers/myprofile/analytics_plugin/stats_controller.rb | 21 +++++++++++++++++++++ plugins/analytics/lib/analytics_plugin.rb | 3 ++- plugins/analytics/lib/analytics_plugin/base.rb | 11 +++++++++-- plugins/analytics/lib/ext/profile.rb | 2 +- plugins/analytics/locales/en.yml | 7 +++++++ plugins/analytics/locales/pt.yml | 7 +++++++ plugins/analytics/models/analytics_plugin/page_view.rb | 21 +++++++++++++++++---- plugins/analytics/models/analytics_plugin/visit.rb | 12 ++++++++++-- plugins/analytics/test/functional/content_viewer_controller_test.rb | 12 +++++++++--- plugins/analytics/views/analytics_plugin/_body_ending.html.slim | 2 +- plugins/analytics/views/analytics_plugin/stats/_table.html.slim | 38 ++++++++++++++++++++++++++++++++++++++ plugins/analytics/views/analytics_plugin/stats/index.html.slim | 5 +++++ 12 files changed, 127 insertions(+), 14 deletions(-) create mode 100644 plugins/analytics/controllers/myprofile/analytics_plugin/stats_controller.rb create mode 100644 plugins/analytics/views/analytics_plugin/stats/_table.html.slim create mode 100644 plugins/analytics/views/analytics_plugin/stats/index.html.slim diff --git a/plugins/analytics/controllers/myprofile/analytics_plugin/stats_controller.rb b/plugins/analytics/controllers/myprofile/analytics_plugin/stats_controller.rb new file mode 100644 index 0000000..8ebd23b --- /dev/null +++ b/plugins/analytics/controllers/myprofile/analytics_plugin/stats_controller.rb @@ -0,0 +1,21 @@ +class AnalyticsPlugin::StatsController < MyProfileController + + no_design_blocks + + before_filter :skip_page_view + + def index + end + + protected + + def default_url_options + # avoid rails' use_relative_controller! + {use_route: '/'} + end + + def skip_page_view + @analytics_skip_page_view = true + end + +end diff --git a/plugins/analytics/lib/analytics_plugin.rb b/plugins/analytics/lib/analytics_plugin.rb index 5f03782..5332918 100644 --- a/plugins/analytics/lib/analytics_plugin.rb +++ b/plugins/analytics/lib/analytics_plugin.rb @@ -1,6 +1,7 @@ module AnalyticsPlugin - TimeOnPageUpdateInterval = 2.minutes * 1000 + TimeOnPageUpdateInterval = 2.minutes + TimeOnPageUpdateIntervalMs = TimeOnPageUpdateInterval * 1000 extend Noosfero::Plugin::ParentMethods diff --git a/plugins/analytics/lib/analytics_plugin/base.rb b/plugins/analytics/lib/analytics_plugin/base.rb index 7e1e024..3a6ce50 100644 --- a/plugins/analytics/lib/analytics_plugin/base.rb +++ b/plugins/analytics/lib/analytics_plugin/base.rb @@ -28,8 +28,7 @@ class AnalyticsPlugin::Base < Noosfero::Plugin unless profile.analytics_anonymous? # FIXME: use session.id in Rails 4 - session_id = Marshal.load(Base64.decode64 request['_session_id'])['session_id'] rescue nil - #session_id = request.session_options[:id] + session_id = request.session_options[:id] page_view.user = user page_view.session_id = session_id end @@ -40,4 +39,12 @@ class AnalyticsPlugin::Base < Noosfero::Plugin }] end + def control_panel_buttons + { + title: I18n.t('analytics_plugin.lib.plugin.panel_button'), + icon: 'analytics-access', + url: {controller: 'analytics_plugin/stats', action: :index} + } + end + end diff --git a/plugins/analytics/lib/ext/profile.rb b/plugins/analytics/lib/ext/profile.rb index d026309..dd1da3b 100644 --- a/plugins/analytics/lib/ext/profile.rb +++ b/plugins/analytics/lib/ext/profile.rb @@ -13,7 +13,7 @@ end class Profile def analytics_settings attrs = {} - @analytics_settings ||= Noosfero::Plugin::Settings.new self, AnalyticsPlugin, attrs + @analytics_settings ||= Noosfero::Plugin::Settings.new self, ::AnalyticsPlugin, attrs attrs.each{ |a, v| @analytics_settings.send "#{a}=", v } @analytics_settings end diff --git a/plugins/analytics/locales/en.yml b/plugins/analytics/locales/en.yml index ebdecd8..18f7f00 100644 --- a/plugins/analytics/locales/en.yml +++ b/plugins/analytics/locales/en.yml @@ -5,6 +5,13 @@ en: &en plugin: name: 'Access tracking' description: 'Register the access of selected profiles' + panel_button: 'Access tracking' + + views: + stats: + user: 'User' + initial_time: 'Time' + pages: 'Pages' en-US: <<: *en diff --git a/plugins/analytics/locales/pt.yml b/plugins/analytics/locales/pt.yml index 86fd817..0208077 100644 --- a/plugins/analytics/locales/pt.yml +++ b/plugins/analytics/locales/pt.yml @@ -5,6 +5,13 @@ pt: &pt plugin: name: 'Rastreio de accesso' description: 'Registra o acesso de perfis selecionados' + panel_button: 'Rastreio de accesso' + + views: + stats: + user: 'Usuário' + initial_time: 'Horário' + pages: 'Páginas' pt-BR: <<: *pt diff --git a/plugins/analytics/models/analytics_plugin/page_view.rb b/plugins/analytics/models/analytics_plugin/page_view.rb index 1d0968a..ac9b91d 100644 --- a/plugins/analytics/models/analytics_plugin/page_view.rb +++ b/plugins/analytics/models/analytics_plugin/page_view.rb @@ -25,10 +25,24 @@ class AnalyticsPlugin::PageView < ActiveRecord::Base before_validation :fill_referer_page_view, on: :create before_validation :fill_visit, on: :create + scope :latest, -> { order 'request_started_at DESC' } + def request_duration self.request_finished_at - self.request_started_at end + def initial_time + self.page_loaded_at || self.request_finished_at + end + + def user_last_time_seen + self.initial_time + self.time_on_page + end + + def user_on_page? + Time.now < self.user_last_time_seen + AnalyticsPlugin::TimeOnPageUpdateInterval + end + def page_load! self.page_loaded_at = Time.now self.update_column :page_loaded_at, self.page_loaded_at @@ -36,10 +50,9 @@ class AnalyticsPlugin::PageView < ActiveRecord::Base def increase_time_on_page! now = Time.now - initial_time = self.page_loaded_at || self.request_finished_at - return unless now > initial_time + return unless now > self.initial_time - self.time_on_page = now - initial_time + self.time_on_page = now - self.initial_time self.update_column :time_on_page, self.time_on_page end @@ -59,7 +72,7 @@ class AnalyticsPlugin::PageView < ActiveRecord::Base end def fill_visit - self.visit = self.referer_page_view.visit if self.referer_page_view + self.visit = self.referer_page_view.visit if self.referer_page_view and self.referer_page_view.user_on_page? self.visit ||= AnalyticsPlugin::Visit.new profile: profile end diff --git a/plugins/analytics/models/analytics_plugin/visit.rb b/plugins/analytics/models/analytics_plugin/visit.rb index 59a12cb..d65f4f7 100644 --- a/plugins/analytics/models/analytics_plugin/visit.rb +++ b/plugins/analytics/models/analytics_plugin/visit.rb @@ -3,9 +3,17 @@ class AnalyticsPlugin::Visit < ActiveRecord::Base attr_accessible *self.column_names attr_accessible :profile - default_scope -> { includes :page_views } - belongs_to :profile has_many :page_views, class_name: 'AnalyticsPlugin::PageView', dependent: :destroy + default_scope -> { joins(:page_views).includes :page_views } + + scope :latest, -> { order 'analytics_plugin_page_views.request_started_at DESC' } + + def first_page_view + self.page_views.first + end + + delegate :user, :initial_time, to: :first_page_view + end diff --git a/plugins/analytics/test/functional/content_viewer_controller_test.rb b/plugins/analytics/test/functional/content_viewer_controller_test.rb index a19b316..f4a85cb 100644 --- a/plugins/analytics/test/functional/content_viewer_controller_test.rb +++ b/plugins/analytics/test/functional/content_viewer_controller_test.rb @@ -31,6 +31,8 @@ class ContentViewerControllerTest < ActionController::TestCase first_page_view = @community.page_views.order(:id).first assert_equal @request.referer, first_page_view.referer_url + assert_equal @user, first_page_view.user + assert first_page_view.request_duration > 0 and first_page_view.request_duration < 1 @request.env['HTTP_REFERER'] = first_url get :view_page, profile: @community.identifier, page: @community.articles.last.path.split('/') @@ -40,9 +42,13 @@ class ContentViewerControllerTest < ActionController::TestCase second_page_view = @community.page_views.order(:id).last assert_equal first_page_view, second_page_view.referer_page_view - assert_equal @user, second_page_view.user - - assert second_page_view.request_duration > 0 and second_page_view.request_duration < 1 + # another visit, the referer is set but should be ignored because + # the user didn't report to be on the page until now + @request.env['HTTP_REFERER'] = first_url + future = Time.now + 2*AnalyticsPlugin::TimeOnPageUpdateInterval + Time.stubs(:now).returns(future) + get :view_page, profile: @community.identifier, page: @community.articles.last.path.split('/') + assert_equal 2, @community.visits.count end end diff --git a/plugins/analytics/views/analytics_plugin/_body_ending.html.slim b/plugins/analytics/views/analytics_plugin/_body_ending.html.slim index 55029e1..f4bfea8 100644 --- a/plugins/analytics/views/analytics_plugin/_body_ending.html.slim +++ b/plugins/analytics/views/analytics_plugin/_body_ending.html.slim @@ -1,6 +1,6 @@ javascript: analytics.timeOnPage.baseUrl = #{url_for(controller: 'analytics_plugin/time_on_page').to_json} - analytics.timeOnPage.updateInterval = #{AnalyticsPlugin::TimeOnPageUpdateInterval.to_json} + analytics.timeOnPage.updateInterval = #{AnalyticsPlugin::TimeOnPageUpdateIntervalMs.to_json} analytics.requestId = #{request.env['action_dispatch.request_id'].to_json} analytics.init() diff --git a/plugins/analytics/views/analytics_plugin/stats/_table.html.slim b/plugins/analytics/views/analytics_plugin/stats/_table.html.slim new file mode 100644 index 0000000..5b6d7dc --- /dev/null +++ b/plugins/analytics/views/analytics_plugin/stats/_table.html.slim @@ -0,0 +1,38 @@ + +table#analytics-stats.table data-toggle='table' data-striped='true' data-sortable='true' data-icons-prefix='fa' + thead + - unless profile.analytics_anonymous? + th= t'analytics_plugin.views.stats.user' + th= t'analytics_plugin.views.stats.initial_time' + th= t'analytics_plugin.views.stats.pages' + + tbody + - profile.visits.each do |visit| + tr + td= link_to visit.user.name, visit.user.url + td + div data-toggle="tooltip" data-title='#{l visit.initial_time}' + = time_ago_in_words(visit.initial_time) + |  + = _'ago' + td + - visit.page_views.each do |page_view| + = link_to page_view.url, page_view.url + |  + = "(#{distance_of_time_in_words page_view.time_on_page})" + | ->  + +javascript: + $('#analytics-stats').bootstrapTable({ + striped: true, + columns: [ + {sortable: true}, + {sortable: true}, + {sortable: true}, + ], + }) + + $(document).ready(function() { + $('[data-toggle="tooltip"]').tooltip() + }) + diff --git a/plugins/analytics/views/analytics_plugin/stats/index.html.slim b/plugins/analytics/views/analytics_plugin/stats/index.html.slim new file mode 100644 index 0000000..d0cd6d1 --- /dev/null +++ b/plugins/analytics/views/analytics_plugin/stats/index.html.slim @@ -0,0 +1,5 @@ +- content_for :head + = javascript_include_tag 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.8.1/bootstrap-table-all.min.js' + = stylesheet_link_tag 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.8.1/bootstrap-table.css' + += render 'table' -- libgit2 0.21.2