trueandco_analytics 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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