trueandco_analytics 0.3.0
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/MIT-LICENSE +20 -0
- data/README.md +152 -0
- data/Rakefile +36 -0
- data/app/assets/javascripts/trueandco_analytics.js +170 -0
- data/app/commands/trueandco_analytics/arr_jsons_to_arr_hash.rb +20 -0
- data/app/commands/trueandco_analytics/metric_c/add_list.rb +65 -0
- data/app/commands/trueandco_analytics/report_c/generate.rb +31 -0
- data/app/commands/trueandco_analytics/session_c/create.rb +65 -0
- data/app/commands/trueandco_analytics/session_c/put_data.rb +46 -0
- data/app/commands/trueandco_analytics/user_c/create_or_update_user_if_exist.rb +41 -0
- data/app/controllers/trueandco_analytics/application_controller.rb +5 -0
- data/app/controllers/trueandco_analytics/reciver_controller.rb +19 -0
- data/app/models/trueandco_analytics/application_record.rb +5 -0
- data/app/models/trueandco_analytics/metric_user.rb +8 -0
- data/app/models/trueandco_analytics/metric_user_session.rb +8 -0
- data/app/models/trueandco_analytics/metric_user_visit.rb +8 -0
- data/app/views/trueandco_analytics/receiver/pull_user_statistic.erb +0 -0
- data/app/workers/trueandco_analytics/user_session_metric_worker.rb +21 -0
- data/config/routes.rb +3 -0
- data/config/secrets.yml +2 -0
- data/db/migrate/20170802133950_create_metric_users.rb +19 -0
- data/db/migrate/20170802134044_create_metric_user_visits.rb +20 -0
- data/db/migrate/20170802134059_create_metric_user_sessions.rb +27 -0
- data/exe/trueandco_analytics +28 -0
- data/lib/extension_string.rb +20 -0
- data/lib/generators/trueandco_analytics/install_generator.rb +14 -0
- data/lib/generators/trueandco_analytics/templates/trueandco_analytics.rb +37 -0
- data/lib/trueandco_analytics/cli/common.rb +49 -0
- data/lib/trueandco_analytics/cli/report.rb +51 -0
- data/lib/trueandco_analytics/config/params.rb +81 -0
- data/lib/trueandco_analytics/engine.rb +31 -0
- data/lib/trueandco_analytics/helpers/trueandco_analytics_helper.rb +35 -0
- data/lib/trueandco_analytics/locales/en.yml +16 -0
- data/lib/trueandco_analytics/modules/info.rb +21 -0
- data/lib/trueandco_analytics/reports/application_report.rb +61 -0
- data/lib/trueandco_analytics/reports/details_report.rb +24 -0
- data/lib/trueandco_analytics/reports/page_buy_in_date_range_report.rb +29 -0
- data/lib/trueandco_analytics/services/logger.rb +24 -0
- data/lib/trueandco_analytics/services/redis_connect.rb +12 -0
- data/lib/trueandco_analytics/services/reports.rb +30 -0
- data/lib/trueandco_analytics/services/workers.rb +14 -0
- data/lib/trueandco_analytics/version.rb +3 -0
- data/lib/trueandco_analytics.rb +37 -0
- metadata +242 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
module TrueandcoAnalytics
|
2
|
+
module Cli
|
3
|
+
class Common
|
4
|
+
include TrueandcoAnalytics::Modules::Info
|
5
|
+
|
6
|
+
def initialize(args)
|
7
|
+
@args = args
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
if args.empty?
|
12
|
+
info(trans('help.need_help'))
|
13
|
+
exit
|
14
|
+
end
|
15
|
+
unless args[0] =~ /^-/
|
16
|
+
TrueandcoAnalytics::Cli::Report.new(args).execute
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
OptionParser.new do |opts|
|
20
|
+
opts.banner = trans('help.need_help')
|
21
|
+
|
22
|
+
opts.on_tail('-h', '--help', 'Help') do
|
23
|
+
info(opts)
|
24
|
+
info(trans('help.description').gsub('\n', "\n"))
|
25
|
+
end
|
26
|
+
opts.on_tail('-v', '--version', 'Version of gem') do
|
27
|
+
info(trans('help.gem_version'))
|
28
|
+
info(TrueandcoAnalytics::VERSION)
|
29
|
+
end
|
30
|
+
opts.on_tail('-r', '--reports', 'List names of reports') do
|
31
|
+
info(TrueandcoAnalytics::Reports.available_reports)
|
32
|
+
end
|
33
|
+
opts.on_tail('-f', '--formats', 'List available formats of reports') do
|
34
|
+
info(TrueandcoAnalytics::Reports.available_formats)
|
35
|
+
end
|
36
|
+
opts.parse!
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_reader :args
|
43
|
+
|
44
|
+
def trans(value)
|
45
|
+
I18n.t "trueandco_analytics.#{value}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module TrueandcoAnalytics
|
2
|
+
module Cli
|
3
|
+
class Report
|
4
|
+
include TrueandcoAnalytics::Modules::Info
|
5
|
+
|
6
|
+
def initialize(args)
|
7
|
+
@args = args
|
8
|
+
@report_args = (Struct.new(:name, :format, :datetime_start, :datetime_end, :path)).new
|
9
|
+
@report_args.name = args[0]
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute
|
13
|
+
need_generate_report = true
|
14
|
+
OptionParser.new do |opts|
|
15
|
+
opts.banner = trans('help.need_help')
|
16
|
+
# Info
|
17
|
+
opts.on_tail("-h", "--help", trans('help.about_help')) do
|
18
|
+
need_generate_report = false
|
19
|
+
info(opts)
|
20
|
+
info(trans('help.description').gsub('\n', "\n"))
|
21
|
+
end
|
22
|
+
# Args
|
23
|
+
opts.on("-fValue", "--format=Value", 'format') do |value|
|
24
|
+
@report_args.format = value
|
25
|
+
end
|
26
|
+
opts.on("-sValue", "--datetime_start=Value", 'datetime_strat') do |value|
|
27
|
+
@report_args.datetime_strat = value
|
28
|
+
end
|
29
|
+
opts.on("-eValue", "--datetime_end=Value", 'datetime_end') do |value|
|
30
|
+
@report_args.datetime_end = value
|
31
|
+
end
|
32
|
+
opts.on("-pValue", "--path=Value", 'path') do |value|
|
33
|
+
@report_args.path = value
|
34
|
+
end
|
35
|
+
opts.parse!
|
36
|
+
end
|
37
|
+
if need_generate_report
|
38
|
+
TrueandcoAnalytics::ReportC::Generate.new(report_args.to_h).execute
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :report_args, :args
|
45
|
+
|
46
|
+
def trans(value)
|
47
|
+
I18n.t "trueandco_analytics.#{value}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module TrueandcoAnalytics
|
2
|
+
module Config
|
3
|
+
module Params
|
4
|
+
|
5
|
+
@time_survey = 15
|
6
|
+
@time_survey_check = false
|
7
|
+
if Object.const_defined?('Rails')
|
8
|
+
@time_dead_session = Rails.env.development? ? 30 : 600
|
9
|
+
end
|
10
|
+
@user_method_check = false
|
11
|
+
@buy_selector_class = 'buy'
|
12
|
+
@buy_selector_class_check = false
|
13
|
+
@connect_redis = { redis_db: 1, }
|
14
|
+
@connect_redis_check = false
|
15
|
+
@sidekiq_configure_client_url = 'redis://localhost:6379/1'
|
16
|
+
@sidekiq_configure_client_check = false
|
17
|
+
@sidekiq_configure_server_url = 'redis://localhost:6379/1'
|
18
|
+
@sidekiq_configure_server_check = false
|
19
|
+
@sidekiq_configure_namespace = 'metric'
|
20
|
+
@sidekiq_configure_namespace_check = false
|
21
|
+
@log = nil
|
22
|
+
@log_check = false
|
23
|
+
@connect_db = nil
|
24
|
+
@connect_db_check = false
|
25
|
+
|
26
|
+
class << self
|
27
|
+
|
28
|
+
def set_params
|
29
|
+
yield self if block_given?
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.attr_writer_only_once(*args)
|
33
|
+
args.each do |arg|
|
34
|
+
self.send(:define_method, "#{arg}=".intern) do |value|
|
35
|
+
return if value.nil?
|
36
|
+
return if instance_variable_get("@#{arg}_check")
|
37
|
+
return unless value.present?
|
38
|
+
instance_variable_set("@#{arg}", value)
|
39
|
+
instance_variable_set("@#{arg}_check", true)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader :time_survey, :time_dead_session, :buy_selector_class,
|
45
|
+
:sidekiq_configure_client_url, :sidekiq_configure_server_url, :sidekiq_configure_namespace,
|
46
|
+
:connect_db, :connect_redis
|
47
|
+
|
48
|
+
attr_writer_only_once :time_survey, :user_method,
|
49
|
+
:sidekiq_configure_client_url, :sidekiq_configure_server_url, :sidekiq_configure_namespace,
|
50
|
+
:connect_db,:connect_redis
|
51
|
+
|
52
|
+
def buy_selector_class=(value)
|
53
|
+
return if value.nil?
|
54
|
+
@buy_selector_class = value.delete('.') unless @buy_selector_class_check
|
55
|
+
@buy_selector_class_check = true
|
56
|
+
end
|
57
|
+
|
58
|
+
def user_method
|
59
|
+
return OpenStruct.new(id: nil, name: '', email: '') unless @user_method_check
|
60
|
+
::ApplicationController.new.public_send(@user_method)
|
61
|
+
end
|
62
|
+
|
63
|
+
def log
|
64
|
+
return unless @log_check
|
65
|
+
@log
|
66
|
+
end
|
67
|
+
|
68
|
+
def log=(value)
|
69
|
+
return if value.nil?
|
70
|
+
@log = value.chomp('.log') unless @log_check
|
71
|
+
@log_check = true
|
72
|
+
end
|
73
|
+
|
74
|
+
def log_obj
|
75
|
+
return unless @log_check
|
76
|
+
TrueandcoAnalytics::Logger.instance
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "trueandco_analytics/helpers/trueandco_analytics_helper"
|
2
|
+
|
3
|
+
module TrueandcoAnalytics
|
4
|
+
|
5
|
+
class Engine < ::Rails::Engine
|
6
|
+
isolate_namespace TrueandcoAnalytics
|
7
|
+
|
8
|
+
initializer :append_migrations do |app|
|
9
|
+
unless app.root.to_s.match root.to_s
|
10
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
11
|
+
app.config.paths["db/migrate"] << expanded_path
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
initializer :action_controller do
|
17
|
+
::TrueandcoAnalytics::Workers.init
|
18
|
+
ActiveSupport.on_load(:action_controller) do
|
19
|
+
self.try(:helper, ::TrueandcoAnalytics::TrueandcoAnalyticsHelper) ||
|
20
|
+
self.try(:include, ::TrueandcoAnalytics::TrueandcoAnalyticsHelper)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
config.generators do |g|
|
25
|
+
g.test_framework :rspec, :fixture => false
|
26
|
+
g.fixture_replacement :factory_girl, :dir => 'spec/factories'
|
27
|
+
g.assets false
|
28
|
+
g.helper false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module TrueandcoAnalytics
|
2
|
+
module TrueandcoAnalyticsHelper
|
3
|
+
|
4
|
+
def track_user_behavior
|
5
|
+
pull_user_statistic_path = Engine.routes.url_helpers.receiver_pull_user_statistic_path
|
6
|
+
return unless @user_behavior.nil?
|
7
|
+
@user_behavior = 1
|
8
|
+
js_string = <<-USER_BEHAVIOR_TRACK
|
9
|
+
var statistics_url = "#{root_url.chomp('/')}#{pull_user_statistic_path}",
|
10
|
+
app_token = "#{form_authenticity_token}";
|
11
|
+
|
12
|
+
userLog.init(statistics_url, app_token, {
|
13
|
+
|
14
|
+
clickCount: true,
|
15
|
+
clickDetails: true,
|
16
|
+
actionItem: {
|
17
|
+
processOnAction: true,
|
18
|
+
selector: '.buy',
|
19
|
+
event: 'click'
|
20
|
+
},
|
21
|
+
processTime: #{Config::Params.time_survey},
|
22
|
+
processData: function(results, statistics_url, app_token) {
|
23
|
+
var xhr = new XMLHttpRequest();
|
24
|
+
xhr.open('POST', statistics_url, true);
|
25
|
+
xhr.setRequestHeader('X-CSRF-Token', app_token);
|
26
|
+
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
27
|
+
var data = "user_metric=" + JSON.stringify(results);
|
28
|
+
xhr.send(data);
|
29
|
+
},
|
30
|
+
});
|
31
|
+
USER_BEHAVIOR_TRACK
|
32
|
+
javascript_tag js_string, defer: 'defer'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
en:
|
2
|
+
trueandco_analytics:
|
3
|
+
help:
|
4
|
+
need_help: '(-h or --help shows available options for use)'
|
5
|
+
description: 'For generate reports use:\nbundle exec trueandco_analytics report_name --datetime_start=datetime --datetime_end=datetime --format=format --path=path_destination'
|
6
|
+
gem_version: 'Gem version is:'
|
7
|
+
|
8
|
+
error:
|
9
|
+
argument:
|
10
|
+
unknown_format: Unknown format of reports
|
11
|
+
unknown_report: Report with the specified name was not found
|
12
|
+
log: 'An error occurred in the analytics'
|
13
|
+
|
14
|
+
result:
|
15
|
+
done: 'The report is created and located: %{path}'
|
16
|
+
data: 'Its Done'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module TrueandcoAnalytics::Modules
|
2
|
+
|
3
|
+
module Info
|
4
|
+
|
5
|
+
def error_message(message)
|
6
|
+
$stderr.puts message.colorize(:red)
|
7
|
+
logger = Config::Params.log_obj
|
8
|
+
return if logger.nil?
|
9
|
+
logger.fatal { I18n.t('trueandco_analytics.error.log') + "\n" + message }
|
10
|
+
end
|
11
|
+
|
12
|
+
def info(message)
|
13
|
+
puts message.to_s.colorize(:blue)
|
14
|
+
end
|
15
|
+
|
16
|
+
def result(message)
|
17
|
+
puts I18n.t('trueandco_analytics.result.data').colorize(:green)
|
18
|
+
puts message.colorize(:green)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module TrueandcoAnalytics
|
2
|
+
class ApplicationReport
|
3
|
+
|
4
|
+
AVAILABLE_FORMATS = %w(csv arr).freeze
|
5
|
+
DEFAULT_FORMAT = AVAILABLE_FORMATS[0]
|
6
|
+
|
7
|
+
def initialize(format = nil, date_start = nil, date_end = nil)
|
8
|
+
format ||= DEFAULT_FORMAT
|
9
|
+
@format = format
|
10
|
+
unless format_available?
|
11
|
+
raise ArgumentError, I18n.t("trueandco_analytics.error.argument.unknown_format")
|
12
|
+
end
|
13
|
+
date_start = date_start.kind_of?(DateTime) ? date_start : DateTime.new(1970,1,1,0,0,0)
|
14
|
+
date_end = date_end.kind_of?(DateTime) ? date_end : DateTime.now
|
15
|
+
date_end += 1.days
|
16
|
+
arr = source_data(date_start, date_end)
|
17
|
+
|
18
|
+
@data = send("to_#{format}", arr)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.formats
|
22
|
+
AVAILABLE_FORMATS
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.reports
|
26
|
+
self.subclasses.map{ |name| name.to_s.sub(/TrueandcoAnalytics::/,'').underscore }
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.subclasses
|
30
|
+
self.descendants
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.descendants
|
34
|
+
ObjectSpace.each_object(Class).select { |klass| klass < self }
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :format, :data, :report_name
|
40
|
+
|
41
|
+
def format_available?
|
42
|
+
AVAILABLE_FORMATS.include?(format)
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_csv(arr)
|
46
|
+
return unless arr.kind_of?(Array)
|
47
|
+
return if arr.empty?
|
48
|
+
::CSV.generate do |csv|
|
49
|
+
csv << arr.first.keys
|
50
|
+
arr.each do |hash|
|
51
|
+
csv << hash.values
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_arr(arr)
|
57
|
+
return unless arr.kind_of?(Array)
|
58
|
+
arr
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module TrueandcoAnalytics
|
2
|
+
class DetailsReport < ApplicationReport
|
3
|
+
|
4
|
+
attr_reader :data
|
5
|
+
|
6
|
+
def report_name
|
7
|
+
"details-report-#{DateTime.now.strftime("%Y.%m.%d %H:%M")}.#{@format}"
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def source_data(date_start, date_end)
|
13
|
+
sql = <<-SQL_TEXT
|
14
|
+
SELECT DATE(metric_user_sessions.session_start) as 'date', metric_users.*, metric_user_sessions.*, metric_user_visits.*
|
15
|
+
FROM metric_user_visits
|
16
|
+
LEFT JOIN metric_users ON metric_user_visits.metric_user_id = metric_users.id
|
17
|
+
LEFT JOIN metric_user_sessions ON metric_user_visits.metric_user_session_id = metric_user_sessions.id
|
18
|
+
WHERE DATE(metric_user_sessions.session_start) BETWEEN CAST('#{date_start}' AS DATE) AND CAST('#{date_end}' AS DATE)
|
19
|
+
ORDER BY DATE(metric_user_sessions.session_start) DESC, metric_users.id, metric_user_sessions.session_start ASC
|
20
|
+
SQL_TEXT
|
21
|
+
ActiveRecord::Base.connection.exec_query(sql).to_a
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module TrueandcoAnalytics
|
2
|
+
class PageBuyInDateRangeReport < ApplicationReport
|
3
|
+
|
4
|
+
attr_reader :data
|
5
|
+
|
6
|
+
def report_name
|
7
|
+
"page-buy-report-#{DateTime.now.strftime("%Y.%m.%d %H:%M")}.#{@format}"
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def source_data(date_start, date_end)
|
13
|
+
sql = <<-SQL_TEXT
|
14
|
+
SELECT metric_user_visits.page_path, DATE(metric_user_sessions.session_start) as session_start, COUNT(metric_user_visits.metric_user_id) as buy_visit, all_visit.all_visit, group_concat(metric_user_visits.metric_user_id) as user_ids
|
15
|
+
FROM metric_user_visits
|
16
|
+
LEFT JOIN metric_user_sessions ON metric_user_visits.metric_user_session_id = metric_user_sessions.id
|
17
|
+
LEFT JOIN (SELECT metric_user_visits.page_path, DATE(metric_user_sessions.session_start) as session_start, COUNT(metric_user_visits.metric_user_id) as all_visit
|
18
|
+
FROM metric_user_visits
|
19
|
+
LEFT JOIN metric_user_sessions ON metric_user_visits.metric_user_session_id = metric_user_sessions.id
|
20
|
+
GROUP BY page_path, DATE(metric_user_sessions.session_start)
|
21
|
+
) AS all_visit ON metric_user_visits.page_path = all_visit.page_path AND DATE(metric_user_sessions.session_start) = all_visit.session_start
|
22
|
+
WHERE metric_user_visits.is_buy = true AND
|
23
|
+
DATE(metric_user_sessions.session_start) BETWEEN CAST('#{date_start}' AS DATE) AND CAST('#{date_end}' AS DATE)
|
24
|
+
GROUP BY page_path, DATE(metric_user_sessions.session_start)
|
25
|
+
SQL_TEXT
|
26
|
+
ActiveRecord::Base.connection.exec_query(sql).to_a
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module TrueandcoAnalytics
|
2
|
+
|
3
|
+
class Logger < Logger
|
4
|
+
include Singleton
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
super(Rails.root.join("log/#{Config::Params.log}.log"))
|
8
|
+
self.formatter = formatter
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def formatter
|
13
|
+
Proc.new { |severity, time, progname, msg|
|
14
|
+
formatted_severity = sprintf("%-5s", severity.to_s)
|
15
|
+
formatted_time = time.strftime("%Y-%m-%d %H:%M:%S")
|
16
|
+
"[#{formatted_severity} #{formatted_time} #{$$}] #{msg.to_s.strip}\n"
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
delegate :error, :debug, :fatal, :info, :warn, :add, :log, to: :instance
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|