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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +152 -0
  4. data/Rakefile +36 -0
  5. data/app/assets/javascripts/trueandco_analytics.js +170 -0
  6. data/app/commands/trueandco_analytics/arr_jsons_to_arr_hash.rb +20 -0
  7. data/app/commands/trueandco_analytics/metric_c/add_list.rb +65 -0
  8. data/app/commands/trueandco_analytics/report_c/generate.rb +31 -0
  9. data/app/commands/trueandco_analytics/session_c/create.rb +65 -0
  10. data/app/commands/trueandco_analytics/session_c/put_data.rb +46 -0
  11. data/app/commands/trueandco_analytics/user_c/create_or_update_user_if_exist.rb +41 -0
  12. data/app/controllers/trueandco_analytics/application_controller.rb +5 -0
  13. data/app/controllers/trueandco_analytics/reciver_controller.rb +19 -0
  14. data/app/models/trueandco_analytics/application_record.rb +5 -0
  15. data/app/models/trueandco_analytics/metric_user.rb +8 -0
  16. data/app/models/trueandco_analytics/metric_user_session.rb +8 -0
  17. data/app/models/trueandco_analytics/metric_user_visit.rb +8 -0
  18. data/app/views/trueandco_analytics/receiver/pull_user_statistic.erb +0 -0
  19. data/app/workers/trueandco_analytics/user_session_metric_worker.rb +21 -0
  20. data/config/routes.rb +3 -0
  21. data/config/secrets.yml +2 -0
  22. data/db/migrate/20170802133950_create_metric_users.rb +19 -0
  23. data/db/migrate/20170802134044_create_metric_user_visits.rb +20 -0
  24. data/db/migrate/20170802134059_create_metric_user_sessions.rb +27 -0
  25. data/exe/trueandco_analytics +28 -0
  26. data/lib/extension_string.rb +20 -0
  27. data/lib/generators/trueandco_analytics/install_generator.rb +14 -0
  28. data/lib/generators/trueandco_analytics/templates/trueandco_analytics.rb +37 -0
  29. data/lib/trueandco_analytics/cli/common.rb +49 -0
  30. data/lib/trueandco_analytics/cli/report.rb +51 -0
  31. data/lib/trueandco_analytics/config/params.rb +81 -0
  32. data/lib/trueandco_analytics/engine.rb +31 -0
  33. data/lib/trueandco_analytics/helpers/trueandco_analytics_helper.rb +35 -0
  34. data/lib/trueandco_analytics/locales/en.yml +16 -0
  35. data/lib/trueandco_analytics/modules/info.rb +21 -0
  36. data/lib/trueandco_analytics/reports/application_report.rb +61 -0
  37. data/lib/trueandco_analytics/reports/details_report.rb +24 -0
  38. data/lib/trueandco_analytics/reports/page_buy_in_date_range_report.rb +29 -0
  39. data/lib/trueandco_analytics/services/logger.rb +24 -0
  40. data/lib/trueandco_analytics/services/redis_connect.rb +12 -0
  41. data/lib/trueandco_analytics/services/reports.rb +30 -0
  42. data/lib/trueandco_analytics/services/workers.rb +14 -0
  43. data/lib/trueandco_analytics/version.rb +3 -0
  44. data/lib/trueandco_analytics.rb +37 -0
  45. 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
@@ -0,0 +1,12 @@
1
+ module TrueandcoAnalytics
2
+ module RedisConnect
3
+ redis_conect = Config::Params.connect_redis
4
+
5
+ @redis = Redis.new(redis_conect.merge({ driver: :hiredis }))
6
+ class << self
7
+ def get
8
+ @redis
9
+ end
10
+ end
11
+ end
12
+ end