ga_trackable 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +1 -0
- data/Rakefile +11 -0
- data/lib/ga_trackable/base_fetcher.rb +91 -0
- data/lib/ga_trackable/configuration.rb +61 -0
- data/lib/ga_trackable/engine.rb +12 -0
- data/lib/ga_trackable/page_views_fetcher.rb +102 -0
- data/lib/ga_trackable/tasks/ga_trackable.rake +100 -0
- data/lib/ga_trackable/trackable.rb +45 -0
- data/lib/ga_trackable/version.rb +4 -0
- data/lib/ga_trackable/video_plays_fetcher.rb +126 -0
- data/lib/ga_trackable.rb +57 -0
- metadata +114 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dc165dc8d86aa90d10356dfe90e2bc982974443c
|
4
|
+
data.tar.gz: f89011a9ea3777d70aa4ca2f1b2f3e6ae2d6c106
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e1bcc38ff487a2823cbdd53447080b9a86a56fbe5eb8ffd4e56ef0362594fcfe0849da05d4b093b21e22b7f3eafb8bc9bf421c2c78d1e1caf4b0d6c540dbebc9
|
7
|
+
data.tar.gz: 0c7b4ce17aa26ed516668c263ba7b880309bf513aadf2618810bb4534dac3fab52dfae221deaa0db11ece98d293c40e35bad48948b4de400eaef711035df11e9
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016 HttpLab
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# ga_trackable
|
data/Rakefile
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class GaTrackable::BaseFetcher
|
3
|
+
|
4
|
+
GA_DATE_FORMAT = '%Y-%m-%d'
|
5
|
+
|
6
|
+
attr_reader :start_date
|
7
|
+
attr_reader :end_date
|
8
|
+
attr_accessor :sometime_processed_counters
|
9
|
+
|
10
|
+
def initialize(
|
11
|
+
client: GaTrackable.client,
|
12
|
+
analytics: GaTrackable.analytics,
|
13
|
+
config: GaTrackable.config
|
14
|
+
)
|
15
|
+
@client = client
|
16
|
+
@analytics = analytics
|
17
|
+
@config = config
|
18
|
+
@sometime_processed_counters = []
|
19
|
+
end
|
20
|
+
|
21
|
+
# Вытащить просмотры за текущий день. Метод может вызываться много раз,
|
22
|
+
# количество просмотров за текущий день будет обновляться.
|
23
|
+
def fetch_for_current_day!
|
24
|
+
@start_date = DateTime.current.beginning_of_day
|
25
|
+
@end_date = DateTime.current
|
26
|
+
|
27
|
+
self.sometime_processed_counters = []
|
28
|
+
|
29
|
+
start_index = 1
|
30
|
+
max_results = 1000
|
31
|
+
|
32
|
+
begin
|
33
|
+
rows = get_data(start_date, end_date, start_index, max_results).rows
|
34
|
+
rows.each do |row|
|
35
|
+
begin
|
36
|
+
process_row(row, create_in_past: true)
|
37
|
+
rescue => ex
|
38
|
+
handle_exception(ex)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
start_index += max_results
|
42
|
+
end while rows.size == max_results
|
43
|
+
|
44
|
+
self.sometime_processed_counters = []
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Инициализировать кеши просмотров. Метод приватный, потому что его вызов
|
51
|
+
# приведет к тому, что все каунтеры будут удалены и перестроены.
|
52
|
+
# Стартовой датой считаем 1 год назад.
|
53
|
+
# Вытаскиваем просмотры за все время, исключая сегодняшний день.
|
54
|
+
# Просмотры за сегодня и далее должны вытаскиваться с помощью fetch_for_current_day! и
|
55
|
+
# запоминаться в виде отдельного счетчика.
|
56
|
+
def initial_fetch!(start_date: 1.year.ago)
|
57
|
+
@start_date = start_date
|
58
|
+
@end_date = 1.day.ago.end_of_day
|
59
|
+
|
60
|
+
self.sometime_processed_counters = []
|
61
|
+
counter_class.delete_all
|
62
|
+
|
63
|
+
start_index = 1
|
64
|
+
max_results = 1000
|
65
|
+
|
66
|
+
begin
|
67
|
+
rows = get_data(start_date, end_date, start_index, max_results).rows
|
68
|
+
rows.each do |row|
|
69
|
+
begin
|
70
|
+
process_row(row, create_in_past: true)
|
71
|
+
rescue => ex
|
72
|
+
handle_exception(ex)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
start_index += max_results
|
76
|
+
end while rows.size == max_results
|
77
|
+
|
78
|
+
self.sometime_processed_counters = []
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
def handle_exception(ex)
|
83
|
+
@config.out << "Exception occured:\n"
|
84
|
+
@config.out << ex.message
|
85
|
+
@config.out << "\n"
|
86
|
+
@config.out << ex.backtrace.join("\n")
|
87
|
+
|
88
|
+
@config.exceptions_handler.error(ex)
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_model'
|
3
|
+
|
4
|
+
module GaTrackable
|
5
|
+
class Configuration
|
6
|
+
|
7
|
+
include ActiveModel::Validations
|
8
|
+
|
9
|
+
def self.attribute_names
|
10
|
+
%i(app_name app_version secret_path secret_key scope issuer_email view_id page_views_black_filter page_views_white_filter page_views_entity_fetcher video_plays_entity_fetcher out exceptions_handler video_url_base rails_env)
|
11
|
+
end
|
12
|
+
|
13
|
+
def attribute_names
|
14
|
+
self.class.attribute_names
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_accessor *attribute_names
|
18
|
+
|
19
|
+
validates(
|
20
|
+
:app_name,
|
21
|
+
:app_version,
|
22
|
+
:secret_path,
|
23
|
+
:secret_key,
|
24
|
+
:scope,
|
25
|
+
:issuer_email,
|
26
|
+
:view_id,
|
27
|
+
:page_views_white_filter,
|
28
|
+
:out,
|
29
|
+
presence: true
|
30
|
+
)
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@view_id = ENV['GA_TRACKABLE_VIEW_ID']
|
34
|
+
@app_name = ENV['GA_TRACKABLE_APP_NAME']
|
35
|
+
@app_version = ENV['GA_TRACKABLE_APP_VERSION']
|
36
|
+
@secret_path = ENV['GA_TRACKABLE_SECRET_PATH']
|
37
|
+
@secret_key = ENV['GA_TRACKABLE_KEY_SECRET']
|
38
|
+
@scope = ENV['GA_TRACKABLE_SCOPE']
|
39
|
+
@issuer_email = ENV['GA_TRACKABLE_ISSUER_EMAIL']
|
40
|
+
@page_views_white_filter = ENV['GA_TRACKABLE_PAGEVIEWS_WHITE_FILTER']
|
41
|
+
@page_views_black_filter = ENV['GA_TRACKABLE_PAGEVIEWS_BLACK_FILTER']
|
42
|
+
@out = STDOUT
|
43
|
+
@video_url_base = []
|
44
|
+
|
45
|
+
yield(self) if block_given?
|
46
|
+
|
47
|
+
[
|
48
|
+
@view_id,
|
49
|
+
@app_name,
|
50
|
+
@app_version,
|
51
|
+
@secret_path,
|
52
|
+
@secret_key,
|
53
|
+
@scope,
|
54
|
+
@issuer_email,
|
55
|
+
@page_views_white_filter,
|
56
|
+
@page_views_black_filter
|
57
|
+
].each(&:freeze)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class GaTrackable::PageViewsFetcher < GaTrackable::BaseFetcher
|
3
|
+
|
4
|
+
def counter_class
|
5
|
+
GaTrackable::PageViewsCounter
|
6
|
+
end
|
7
|
+
|
8
|
+
# Поулчить данные по просмотрам для конкретного пути. Используется в rake-таске.
|
9
|
+
def get_details_for(start_date: 1.year.ago, path:)
|
10
|
+
ga_start_date = start_date.strftime(GA_DATE_FORMAT)
|
11
|
+
ga_end_date = DateTime.current.strftime(GA_DATE_FORMAT)
|
12
|
+
|
13
|
+
data = @client.execute(api_method: @analytics.data.ga.get, parameters: {
|
14
|
+
'ids' => "ga:#{@config.view_id}",
|
15
|
+
'start-date' => ga_start_date,
|
16
|
+
'end-date' => ga_end_date,
|
17
|
+
'dimensions' => 'ga:pagePath,ga:pageTitle',
|
18
|
+
'metrics' => 'ga:uniquePageviews,ga:pageviews',
|
19
|
+
'sort' => 'ga:pagePath,ga:pageTitle',
|
20
|
+
'filters' => "ga:pagePath=@#{path}"
|
21
|
+
}).data
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Обработать запись из коллекции, которую отдает гуглоаналитика.
|
27
|
+
# create_in_past нужен для создания счетчика "вчера". Это нужно в случае стартовой инициализации счетчиков.
|
28
|
+
# Стартовые данные за весь период записываем вчерашним днем, за сегодня и далее -- записываем стандартным образом.
|
29
|
+
def process_row(row, create_in_past: false)
|
30
|
+
# Пропускаем события с demo
|
31
|
+
return if @config.page_views_black_filter && row[0] =~ @config.page_views_black_filter
|
32
|
+
page_path = URI(row[0].split('?').first).path.chomp('/')
|
33
|
+
unique_page_views = row[1].to_i
|
34
|
+
page_views = row[2].to_i
|
35
|
+
entity = get_entity(page_path)
|
36
|
+
|
37
|
+
# Пытаемся получить каунтер на сегодняшний день.
|
38
|
+
today_counters = entity.page_views_counters.where('created_at >= ?', start_date)
|
39
|
+
fail "more than one today counter for #{entity.class.name}##{entity.id}" if today_counters.size > 1
|
40
|
+
|
41
|
+
if today_counters.any?
|
42
|
+
counter = today_counters.first
|
43
|
+
else
|
44
|
+
counter = entity.page_views_counters.build(page_path: page_path)
|
45
|
+
if create_in_past
|
46
|
+
counter.created_at = 1.day.ago
|
47
|
+
counter.updated_at = counter.created_at
|
48
|
+
counter.initial_data = true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Аналитика считает просмотры по каждому url-у и тайтлу.
|
53
|
+
# Это значит что если в процессе существования сущности менялся ее тайтл,
|
54
|
+
# но не менялся url, аналитика отдаст столько записей, сколько было разных тайтлов.
|
55
|
+
# Нам нужна агрегированная информация по тайтлам. Для этого введен массив sometime_processed_counters
|
56
|
+
# Если url в нем есть, значит количество просмотров не инициализируем, а инкрементируем.
|
57
|
+
if sometime_processed_counters.index(page_path)
|
58
|
+
counter.unique_page_views += unique_page_views
|
59
|
+
counter.page_views += page_views
|
60
|
+
else
|
61
|
+
counter.unique_page_views = unique_page_views
|
62
|
+
sometime_processed_counters << page_path
|
63
|
+
counter.page_views = page_views
|
64
|
+
end
|
65
|
+
|
66
|
+
counter.save!
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_data(tstart, tend, start_index, max_results)
|
70
|
+
ga_start_date = tstart.strftime(GA_DATE_FORMAT)
|
71
|
+
ga_end_date = tend.strftime(GA_DATE_FORMAT)
|
72
|
+
|
73
|
+
params = {
|
74
|
+
'ids' => "ga:#{@config.view_id}",
|
75
|
+
'start-date' => ga_start_date,
|
76
|
+
'end-date' => ga_end_date,
|
77
|
+
'start-index' => start_index,
|
78
|
+
'max-results' => max_results,
|
79
|
+
'dimensions' => 'ga:pagePath',
|
80
|
+
'metrics' => 'ga:uniquePageviews,ga:pageviews',
|
81
|
+
'sort' => 'ga:pagePath',
|
82
|
+
'filters' => "ga:pagePath=~#{@config.page_views_white_filter}"
|
83
|
+
}
|
84
|
+
|
85
|
+
data = @client.execute(api_method: @analytics.data.ga.get, parameters: params).data
|
86
|
+
|
87
|
+
GaTrackable.out << "Page views data:\n"
|
88
|
+
GaTrackable.out << "params:\n"
|
89
|
+
GaTrackable.out << params.to_s
|
90
|
+
GaTrackable.out << "\n"
|
91
|
+
GaTrackable.out << "response:\n"
|
92
|
+
GaTrackable.out << data.rows.to_s
|
93
|
+
GaTrackable.out << "\n"
|
94
|
+
|
95
|
+
data
|
96
|
+
end
|
97
|
+
|
98
|
+
def get_entity(page_path)
|
99
|
+
@config.page_views_entity_fetcher.call(page_path)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'colorize'
|
3
|
+
|
4
|
+
namespace :ga_trackable do
|
5
|
+
# Все таски ga должны зависеть от check_staging, для предотвращения запуска
|
6
|
+
# в стейджинг-окружении.
|
7
|
+
task check_staging: :environment do
|
8
|
+
if GaTrackable.config.rails_env && GaTrackable.config.rails_env.staging?
|
9
|
+
puts 'Запрещен запуск задач GA на стейджинг-сервере.'.red
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Обновить статистику просмотров за сегодняшний день'
|
15
|
+
task fetch_for_current_day: :check_staging do
|
16
|
+
if GaTrackable.config.page_views_entity_fetcher
|
17
|
+
GaTrackable::PageViewsFetcher.new.fetch_for_current_day!
|
18
|
+
end
|
19
|
+
if GaTrackable.config.video_plays_entity_fetcher
|
20
|
+
GaTrackable::VideoPlaysFetcher.new.fetch_for_current_day!
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Initial fetch'
|
25
|
+
task initial_fetch: :check_staging do
|
26
|
+
if GaTrackable.config.page_views_entity_fetcher
|
27
|
+
GaTrackable::PageViewsFetcher.new.send(:initial_fetch!, start_date: 1.year.ago)
|
28
|
+
end
|
29
|
+
if GaTrackable.config.video_plays_entity_fetcher
|
30
|
+
GaTrackable::VideoPlaysFetcher.new.send(:initial_fetch!, start_date: 1.year.ago)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'Отобразить детальную информацию по просмотрам страниц'
|
35
|
+
task get_details_for_page_views: :check_staging do
|
36
|
+
path = ENV['path']
|
37
|
+
unless path.present?
|
38
|
+
puts <<-HERE.strip_heredoc
|
39
|
+
Отобразить детальную информацию по просмотрам страниц:
|
40
|
+
rake uralok:ga:get_details_for_page_views path=/programs/home-concert1/stories/tomas-live start_date=2014-10-01
|
41
|
+
* Параметр start_date не обязателен (по-умолчанию: 1.year.ago).
|
42
|
+
HERE
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
|
46
|
+
f = GaTrackable::PageViewsFetcher.new
|
47
|
+
opts = { path: path }
|
48
|
+
opts[:start_date] = DateTime.parse(ENV['start_date']) if ENV['start_date'].present?
|
49
|
+
data = f.get_details_for opts
|
50
|
+
|
51
|
+
totalUniquePageViews = 0
|
52
|
+
totalPageViews = 0
|
53
|
+
data.rows.each do |row|
|
54
|
+
puts [data.columnHeaders[0].name.rjust(18), row[0]].join(': ')
|
55
|
+
puts [data.columnHeaders[1].name.rjust(18), row[1]].join(': ')
|
56
|
+
puts [data.columnHeaders[2].name, row[2]].join(': ')
|
57
|
+
puts [data.columnHeaders[3].name, row[3]].join(': ')
|
58
|
+
totalUniquePageViews += row[2].to_i
|
59
|
+
totalPageViews += row[3].to_i
|
60
|
+
puts '---'
|
61
|
+
end
|
62
|
+
|
63
|
+
puts "Всего уникальных просмотров за выбранный период: #{totalUniquePageViews}".green
|
64
|
+
puts "Всего просмотров за выбранный период: #{totalPageViews}".green
|
65
|
+
end
|
66
|
+
|
67
|
+
desc 'Отобразить детальную информацию по просмотрам видео'
|
68
|
+
task get_details_for_video_plays: :check_staging do
|
69
|
+
path = ENV['path']
|
70
|
+
unless path.present?
|
71
|
+
puts <<-HERE.strip_heredoc
|
72
|
+
Отобразить детальную информацию по просмотрам видео:
|
73
|
+
rake uralok:ga:get_details_for_video_plays path=/programs/home-concert1/stories/tomas-live start_date=2014-10-01
|
74
|
+
* Параметр start_date не обязателен (по-умолчанию: 1.year.ago).
|
75
|
+
HERE
|
76
|
+
exit
|
77
|
+
end
|
78
|
+
|
79
|
+
f = GaTrackable::VideoPlaysFetcher.new
|
80
|
+
opts = { path: path }
|
81
|
+
opts[:start_date] = DateTime.parse(ENV['start_date']) if ENV['start_date'].present?
|
82
|
+
data = f.get_details_for opts
|
83
|
+
|
84
|
+
totalUniqueEvents = 0
|
85
|
+
totalEvents = 0
|
86
|
+
data.rows.each do |row|
|
87
|
+
puts [data.columnHeaders[0].name.rjust(18), row[0]].join(': ')
|
88
|
+
puts [data.columnHeaders[1].name.rjust(18), row[1]].join(': ')
|
89
|
+
puts [data.columnHeaders[2].name.rjust(18), row[2]].join(': ')
|
90
|
+
puts [data.columnHeaders[3].name.rjust(18), row[3]].join(': ')
|
91
|
+
puts [data.columnHeaders[4].name.rjust(18), row[4]].join(': ')
|
92
|
+
totalUniqueEvents += row[3].to_i
|
93
|
+
totalEvents += row[4].to_i
|
94
|
+
puts '---'
|
95
|
+
end
|
96
|
+
|
97
|
+
puts "Всего уникальных просмотров за выбранный период: #{totalUniqueEvents}".green
|
98
|
+
puts "Всего просмотров за выбранный период: #{totalEvents}".green
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GaTrackable::Trackable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def ga_trackable(video_plays:)
|
7
|
+
setup_page_views_relations
|
8
|
+
setup_video_plays_relations if video_plays
|
9
|
+
end
|
10
|
+
|
11
|
+
def setup_page_views_relations
|
12
|
+
has_many :page_views_counters, class_name: GaTrackable::PageViewsCounter, as: :trackable, dependent: :destroy
|
13
|
+
include PageViewsMethods
|
14
|
+
end
|
15
|
+
|
16
|
+
def setup_video_plays_relations
|
17
|
+
has_many :video_plays_counters, class_name: GaTrackable::VideoPlaysCounter, as: :trackable, dependent: :destroy
|
18
|
+
include VideoPlaysMethods
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module PageViewsMethods
|
23
|
+
extend ActiveSupport::Concern
|
24
|
+
|
25
|
+
def total_unique_page_views
|
26
|
+
page_views_counters.sum(:unique_page_views)
|
27
|
+
end
|
28
|
+
|
29
|
+
def total_page_views
|
30
|
+
page_views_counters.sum(:page_views)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module VideoPlaysMethods
|
35
|
+
extend ActiveSupport::Concern
|
36
|
+
|
37
|
+
def total_unique_video_plays
|
38
|
+
video_plays_counters.sum(:unique_events)
|
39
|
+
end
|
40
|
+
|
41
|
+
def total_video_plays
|
42
|
+
video_plays_counters.sum(:total_events)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class GaTrackable::VideoPlaysFetcher < GaTrackable::BaseFetcher
|
3
|
+
|
4
|
+
GA_FILTER = 'Video Plays'
|
5
|
+
|
6
|
+
def counter_class
|
7
|
+
GaTrackable::VideoPlaysCounter
|
8
|
+
end
|
9
|
+
|
10
|
+
# Поулчить данные по просмотрам для конкретного пути. Используется в rake-таске.
|
11
|
+
def get_details_for(start_date: 1.year.ago, path:)
|
12
|
+
ga_start_date = start_date.in_time_zone.strftime(GA_DATE_FORMAT)
|
13
|
+
ga_end_date = DateTime.current.strftime(GA_DATE_FORMAT)
|
14
|
+
|
15
|
+
data = @client.execute(api_method: @analytics.data.ga.get, parameters: {
|
16
|
+
'ids' => "ga:#{@config.view_id}",
|
17
|
+
'start-date' => ga_start_date,
|
18
|
+
'end-date' => ga_end_date,
|
19
|
+
'dimensions' => 'ga:eventCategory,ga:eventAction',
|
20
|
+
'metrics' => 'ga:uniqueEvents,ga:totalEvents',
|
21
|
+
'sort' => 'ga:eventCategory,ga:eventAction',
|
22
|
+
'filters' => "ga:eventCategory==#{GA_FILTER};ga:eventAction=@#{path}"
|
23
|
+
}).data
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# create_in_past нужен для создания счетчика "вчера". Это нужно в случае стартовой инициализации счетчиков.
|
29
|
+
# Стартовые данные за весь период записываем вчерашним днем, за сегодня и далее -- записываем стандартным образом.
|
30
|
+
# По-просмотрам видео есть специфика:
|
31
|
+
# одной странице могут соответствовать разные видеофайлы(event_action) в разное время.
|
32
|
+
def process_row(row, hsh = {})
|
33
|
+
event_action = row[1]
|
34
|
+
|
35
|
+
entities = get_entities(fetch_video_url(event_action))
|
36
|
+
|
37
|
+
entities.each {|e| process_entity(e, row, hsh) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def process_entity(entity, row, create_in_past: false)
|
41
|
+
event_category = row[0]
|
42
|
+
event_action = row[1]
|
43
|
+
unique_events = row[2].to_i
|
44
|
+
total_events = row[3].to_i
|
45
|
+
|
46
|
+
# Пытаемся получить каунтер на сегодняшний день для полученного видеофайла (event_action).
|
47
|
+
today_counters = entity.video_plays_counters.where('created_at >= ? AND event_action = ?', start_date, event_action)
|
48
|
+
fail "more than one today counter for #{entity.class.name}##{entity.id} and <#{event_action}>" if today_counters.size > 1
|
49
|
+
|
50
|
+
if today_counters.any?
|
51
|
+
counter = today_counters.first
|
52
|
+
else
|
53
|
+
counter = entity.video_plays_counters.build(
|
54
|
+
event_category: event_category,
|
55
|
+
event_action: event_action
|
56
|
+
)
|
57
|
+
if create_in_past
|
58
|
+
counter.created_at = 1.day.ago
|
59
|
+
counter.updated_at = counter.created_at
|
60
|
+
counter.initial_data = true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Мы считаем просмотры по event_action плейлист.
|
65
|
+
# В выдаче аналитики комбинации страница + видеофайл могут встречаться много раз.
|
66
|
+
# По этому код ниже определяет надо инициализировать счетчики, или инкрементировать.
|
67
|
+
key = event_action
|
68
|
+
if sometime_processed_counters.index(key)
|
69
|
+
counter.unique_events += unique_events
|
70
|
+
counter.total_events += total_events
|
71
|
+
else
|
72
|
+
counter.unique_events = unique_events
|
73
|
+
counter.total_events = total_events
|
74
|
+
sometime_processed_counters << key
|
75
|
+
end
|
76
|
+
|
77
|
+
counter.save!
|
78
|
+
end
|
79
|
+
|
80
|
+
def get_data(tstart, tend, start_index, max_results)
|
81
|
+
ga_start_date = tstart.strftime(GA_DATE_FORMAT)
|
82
|
+
ga_end_date = tend.strftime(GA_DATE_FORMAT)
|
83
|
+
|
84
|
+
params = {
|
85
|
+
'ids' => "ga:#{@config.view_id}",
|
86
|
+
'start-date' => ga_start_date,
|
87
|
+
'end-date' => ga_end_date,
|
88
|
+
'start-index' => start_index,
|
89
|
+
'max-results' => max_results,
|
90
|
+
'dimensions' => 'ga:eventCategory,ga:eventAction',
|
91
|
+
'metrics' => 'ga:uniqueEvents,ga:totalEvents',
|
92
|
+
'sort' => 'ga:eventCategory,ga:eventAction',
|
93
|
+
'filters' => "ga:eventCategory==#{GA_FILTER}"
|
94
|
+
}
|
95
|
+
data = @client.execute(api_method: @analytics.data.ga.get, parameters: params).data
|
96
|
+
|
97
|
+
GaTrackable.out << "Video plays data:\n"
|
98
|
+
GaTrackable.out << "params:\n"
|
99
|
+
GaTrackable.out << params.to_s
|
100
|
+
GaTrackable.out << "\n"
|
101
|
+
GaTrackable.out << "response:\n"
|
102
|
+
GaTrackable.out << data.rows.to_s
|
103
|
+
GaTrackable.out << "\n"
|
104
|
+
|
105
|
+
data
|
106
|
+
end
|
107
|
+
|
108
|
+
def fetch_video_url(event_action)
|
109
|
+
index = nil
|
110
|
+
@config.video_url_base.each do |base|
|
111
|
+
index = event_action.index base
|
112
|
+
break if index.present?
|
113
|
+
end
|
114
|
+
if index.present?
|
115
|
+
trailing_index = event_action.index('/playlist.m3u8') || event_action.length
|
116
|
+
key = event_action.slice(index...trailing_index)
|
117
|
+
else
|
118
|
+
key = event_action
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_entity(video_url)
|
123
|
+
@config.video_plays_entity_fetcher.call(video_url)
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
data/lib/ga_trackable.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'rails'
|
3
|
+
require 'google/api_client'
|
4
|
+
|
5
|
+
module GaTrackable
|
6
|
+
require 'ga_trackable/configuration'
|
7
|
+
require 'ga_trackable/version'
|
8
|
+
require 'ga_trackable/trackable'
|
9
|
+
require 'ga_trackable/base_fetcher'
|
10
|
+
require 'ga_trackable/page_views_fetcher'
|
11
|
+
require 'ga_trackable/video_plays_fetcher'
|
12
|
+
require 'ga_trackable/engine'
|
13
|
+
|
14
|
+
InvalidConfigurationError = Class.new(StandardError)
|
15
|
+
|
16
|
+
class << self
|
17
|
+
|
18
|
+
def setup(&blk)
|
19
|
+
@config ||= GaTrackable::Configuration.new(&blk)
|
20
|
+
|
21
|
+
if @config.invalid?
|
22
|
+
msg = "GaTrackable configuration ERROR:\n"
|
23
|
+
raise InvalidConfigurationError, msg + @config.errors.full_messages.join("\n")
|
24
|
+
end
|
25
|
+
|
26
|
+
@config
|
27
|
+
end
|
28
|
+
|
29
|
+
def reset
|
30
|
+
@config = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def config
|
34
|
+
@config || raise(InvalidConfigurationError, 'GaTrackable is not configured!')
|
35
|
+
end
|
36
|
+
|
37
|
+
def client
|
38
|
+
@client ||= begin
|
39
|
+
client = Google::APIClient.new(
|
40
|
+
application_name: config.app_name,
|
41
|
+
application_version: config.app_version
|
42
|
+
)
|
43
|
+
key = Google::APIClient::PKCS12.load_key(config.secret_path, config.secret_key)
|
44
|
+
service_account = Google::APIClient::JWTAsserter.new(config.issuer_email, config.scope, key)
|
45
|
+
client.authorization = service_account.authorize
|
46
|
+
client
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def analytics
|
51
|
+
@analytics ||= client.discovered_api('analytics', 'v3')
|
52
|
+
end
|
53
|
+
|
54
|
+
delegate :out, to: :config
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ga_trackable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yury
|
8
|
+
- Kotov
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-11-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: google-api-client
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - '='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.8.6
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 0.8.6
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: activemodel
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: colorize
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
description: A Ruby wrapper for tracking via Google Analytics API
|
71
|
+
email:
|
72
|
+
- non-gi-suong@ya.ru
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- LICENSE
|
78
|
+
- README.md
|
79
|
+
- Rakefile
|
80
|
+
- lib/ga_trackable.rb
|
81
|
+
- lib/ga_trackable/base_fetcher.rb
|
82
|
+
- lib/ga_trackable/configuration.rb
|
83
|
+
- lib/ga_trackable/engine.rb
|
84
|
+
- lib/ga_trackable/page_views_fetcher.rb
|
85
|
+
- lib/ga_trackable/tasks/ga_trackable.rake
|
86
|
+
- lib/ga_trackable/trackable.rb
|
87
|
+
- lib/ga_trackable/version.rb
|
88
|
+
- lib/ga_trackable/video_plays_fetcher.rb
|
89
|
+
homepage: https://github.com/httplab/ga_trackable
|
90
|
+
licenses:
|
91
|
+
- MIT
|
92
|
+
metadata: {}
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 2.5.1
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: A Ruby wrapper for page views and video plays tracking via Google Analytics
|
113
|
+
API
|
114
|
+
test_files: []
|