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,31 @@
1
+ module TrueandcoAnalytics
2
+ module ReportC
3
+ class Generate
4
+ include TrueandcoAnalytics::Modules::Info
5
+
6
+ def initialize(name: nil, format: nil, datetime_start: nil, datetime_end: nil, path: nil)
7
+ @name = name
8
+ @format = format
9
+ @datetime_start = datetime_start
10
+ @datetime_end = datetime_end
11
+ @path = path || CURRENT_PATH
12
+ end
13
+
14
+ def execute
15
+ report = ::TrueandcoAnalytics::Reports.get_report_obj(name, format, datetime_start, datetime_end)
16
+ return if report.nil? || report.data.empty?
17
+ @path += report.report_name if File.directory?(path)
18
+ dirname = File.dirname(path)
19
+ unless File.directory?(dirname)
20
+ FileUtils.mkdir_p(dirname)
21
+ end
22
+ File.open(path, 'w+') { |f| f.write(report.data) }
23
+ result(I18n.t('trueandco_analytics.result.done', path: path))
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :name, :format, :datetime_start, :datetime_end, :path
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,65 @@
1
+ module TrueandcoAnalytics
2
+ module SessionC
3
+ class Create
4
+
5
+ def initialize(metric_user, first_point, timestamp_end, request_info)
6
+
7
+ if metric_user.nil? || first_point.nil? || timestamp_end.nil? ||request_info.nil?
8
+ raise ArgumentError.new("#{self.to_s} metric_user=#{metric_user}
9
+ first_point=#{first_point} timestamp_end=#{timestamp_end} request_info=#{request_info}")
10
+ end
11
+
12
+ @metric_user = metric_user
13
+ @ip = request_info['remote_ip']
14
+ @user_agent = request_info['user_agent']
15
+ @session_start = first_point['time']['timestamp']
16
+ @referer = first_point['userReferrer']
17
+ @live_time_s = timestamp_end - @session_start
18
+ end
19
+
20
+ def execute
21
+ Browser::Base.include(Browser::Aliases)
22
+ browser = Browser.new(user_agent, accept_language: "en-us")
23
+ data = {
24
+ metric_user: metric_user,
25
+ user_agent: user_agent,
26
+ referer: referer,
27
+ session_start: datetime_session_start,
28
+ live_time_s: 60,
29
+ ip_4: ip_v4(ip),
30
+ ip_6: ip_v6(ip),
31
+ browser: browser.name,
32
+ is_mobile: browser.mobile? || false
33
+ }
34
+ MetricUserSession.create(data)
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :metric_user, :ip, :user_agent, :session_start, :live_time_s, :referer
40
+
41
+ def datetime_session_start
42
+ DateTime.strptime(session_start.to_s,'%s')
43
+ end
44
+
45
+ def ip_v4(ip)
46
+ ip_o = get_ip_object(ip)
47
+ result = 0
48
+ result = ip_o.to_i if ip_o.ipv4?
49
+ result
50
+ end
51
+
52
+
53
+ def ip_v6(ip)
54
+ ip_o = get_ip_object(ip)
55
+ result = 0
56
+ result = ip_o.data if ip_o.ipv6?
57
+ result
58
+ end
59
+
60
+ def get_ip_object(ip)
61
+ @ip_o ||= IPAddress.parse ip
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,46 @@
1
+ module TrueandcoAnalytics
2
+ module SessionC
3
+ class PutData
4
+
5
+ def initialize(json_data, request_info, userInfo)
6
+ @json_data = json_data
7
+ @userInfo = userInfo
8
+ @request_info = request_info
9
+ @redis = RedisConnect.get
10
+ @time_survey = Config::Params.time_survey
11
+ @time_dead_session = Config::Params.time_dead_session
12
+ end
13
+
14
+ def execute
15
+ 1.times do
16
+ break if userInfo.nil? || userInfo.empty?
17
+ user_key = ::FNV.new.fnv1_32 userInfo.to_s
18
+ mange_job(user_key)
19
+ redis.rpush(user_key, json_data)
20
+ redis.expire(user_key, time_dead_session + 3)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :json_data, :userInfo, :request_info, :redis, :time_dead_session, :time_survey
27
+
28
+ def mange_job(user_key)
29
+ user_job_key = "#{user_key}:job_key"
30
+ unless redis.exists user_job_key
31
+ create_job(user_key, user_job_key)
32
+ end
33
+ if redis.ttl(user_job_key) < time_survey * 3
34
+ job_id = redis.get(user_job_key)
35
+ Sidekiq::ScheduledSet.new.find_job(job_id)&.delete
36
+ create_job(user_key, user_job_key)
37
+ end
38
+ end
39
+
40
+ def create_job(user_key, user_job_key)
41
+ job_id = UserSessionMetricWorker.perform_at(time_dead_session.seconds.from_now, user_key, request_info)
42
+ redis.setex(user_job_key, time_dead_session, job_id)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,41 @@
1
+ module TrueandcoAnalytics
2
+ module UserC
3
+ class CreateOrUpdateUserIfExist
4
+
5
+ def initialize(user_agent, timestamp_end)
6
+ if user_agent.nil? || timestamp_end.nil?
7
+ raise ArgumentError.new("#{self.to_s} user_agent=#{user_agent} timestamp_end=#{timestamp_end}")
8
+ end
9
+ @user_agent = user_agent
10
+ @timestamp_end = timestamp_end
11
+ @user_id = Config::Params.user_method&.id
12
+ end
13
+
14
+ def execute
15
+ user = nil
16
+ user = TrueandcoAnalytics::MetricUser.find_by(user_id: user_id) if user_id
17
+ user = TrueandcoAnalytics::MetricUser.find_by(hash_id: hash_id) if user.nil?
18
+ user = if user.nil?
19
+ TrueandcoAnalytics::MetricUser.create(user_id: user_id || 0, datetime_create: datetime_end,
20
+ last_active_datetime: datetime_end, hash_id: hash_id)
21
+ else
22
+ user.update_attribute(:last_active_datetime, datetime_end)
23
+ user
24
+ end
25
+ user
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :user_agent, :timestamp_end, :user_id
31
+
32
+ def datetime_end
33
+ DateTime.strptime(timestamp_end.to_s,'%s')
34
+ end
35
+
36
+ def hash_id
37
+ @hash_id ||= FNV.new.fnv1_32(user_agent)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,5 @@
1
+ module TrueandcoAnalytics
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+ end
5
+ end
@@ -0,0 +1,19 @@
1
+ require_dependency "trueandco_analytics/application_controller"
2
+
3
+ module TrueandcoAnalytics
4
+ class ReceiverController < ApplicationController
5
+
6
+ def pull_user_statistic
7
+ data = JSON.parse(params[:user_metric])
8
+ userInfo = request.user_agent
9
+ page_path = request.url.match(/(?:\/\/.+?\/)(.+?)(?=\?|$)/)[1]
10
+ data = data.merge({ page_path: page_path }).to_json
11
+ request_info = {
12
+ 'remote_ip' => request.remote_ip,
13
+ 'user_agent' => request.user_agent
14
+ }
15
+ SessionC::PutData.new(data, request_info, userInfo).execute
16
+ render nothing: true, status: 201, content_type: 'text/plain'
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ module TrueandcoAnalytics
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ module TrueandcoAnalytics
2
+ class MetricUser < ApplicationRecord
3
+ self.table_name = 'metric_users'
4
+
5
+ has_many :metric_user_visits, dependent: :destroy
6
+ has_many :metric_user_sessions, dependent: :destroy
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module TrueandcoAnalytics
2
+ class MetricUserSession < ApplicationRecord
3
+ self.table_name = 'metric_user_sessions'
4
+
5
+ has_many :metric_user_visits, dependent: :destroy
6
+ belongs_to :metric_user
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module TrueandcoAnalytics
2
+ class MetricUserVisit < ApplicationRecord
3
+ self.table_name = 'metric_user_visits'
4
+
5
+ belongs_to :metric_user_session
6
+ belongs_to :metric_user
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ module TrueandcoAnalytics
2
+
3
+ class UserSessionMetricWorker
4
+ include Sidekiq::Worker
5
+ sidekiq_options queue: :user_metric
6
+
7
+ def perform(*args)
8
+ info_pages,info_request = args[0],args[1]
9
+ @redis = RedisConnect.get
10
+ user_session_data_json = @redis.lrange(info_pages, 0, -1)
11
+ return if user_session_data_json.nil?
12
+ user_session_data = ArrJsonsToArrHash.new(user_session_data_json).execute
13
+ return unless user_session_data.any?
14
+ first_point = user_session_data[0]
15
+ last_point = user_session_data[-1]
16
+ metric_user = UserC::CreateOrUpdateUserIfExist.new(info_request['user_agent'], last_point['time']['timestamp']).execute
17
+ session = SessionC::Create.new(metric_user, first_point, last_point['time']['timestamp'], info_request).execute
18
+ MetricC::AddList.new(metric_user, session, user_session_data).execute
19
+ end
20
+ end
21
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ TrueandcoAnalytics::Engine.routes.draw do
2
+ post 'receiver/pull_user_statistic'
3
+ end
@@ -0,0 +1,2 @@
1
+ production:
2
+ secret_key_base: ppsh
@@ -0,0 +1,19 @@
1
+ class CreateMetricUsers < ActiveRecord::Migration.try(:send, '[]', '5.1')
2
+ def up
3
+ drop_table :metric_users if (table_exists? :metric_users)
4
+ create_table :metric_users do |t|
5
+ t.integer :user_id, null: false, default: 0
6
+ t.timestamp :datetime_create, null: false
7
+ t.timestamp :last_active_datetime, null: false
8
+ end
9
+
10
+ add_column :metric_users, :hash_id, 'integer unsigned', null: false
11
+
12
+ add_index(:metric_users, :user_id)
13
+ add_index(:metric_users, :hash_id)
14
+ end
15
+
16
+ def down
17
+ drop_table :metric_users
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ class CreateMetricUserVisits < ActiveRecord::Migration.try(:send, '[]', '5.1')
2
+ def up
3
+ drop_table :metric_user_visits if (table_exists? :metric_user_visits)
4
+ create_table :metric_user_visits do |t|
5
+ t.integer :metric_user_id, null: false, default: 0
6
+ t.integer :metric_user_session_id, null: false, default: 0
7
+ t.string :page_path, null: false
8
+ t.boolean :is_buy, null: false, default: false
9
+ t.text :user_action, null: false
10
+ end
11
+ add_column :metric_user_visits, :time_s, 'mediumint unsigned', null: false, default: 0
12
+
13
+ add_index(:metric_user_visits, :metric_user_id)
14
+ add_index(:metric_user_visits, :metric_user_session_id)
15
+ end
16
+
17
+ def down
18
+ drop_table :metric_user_visits
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ class CreateMetricUserSessions < ActiveRecord::Migration.try(:send, '[]', '5.1')
2
+ def up
3
+ drop_table :metric_user_sessions if (table_exists? :metric_user_sessions)
4
+ create_table :metric_user_sessions do |t|
5
+ t.integer :metric_user_id, null: false, default: 0
6
+ t.binary :ip_6, limit: 16, null: false, default: 0
7
+ t.string :user_agent, null: false
8
+ t.string :referer, null: false
9
+ t.boolean :is_mobile, null: false, default: false
10
+ t.string :browser, null: false, default: ''
11
+ t.timestamp :session_start, null: false
12
+ end
13
+
14
+ add_column :metric_user_sessions, :ip_4, 'integer unsigned', null: false, default: 0
15
+ add_column :metric_user_sessions, :live_time_s, 'integer unsigned', null: false
16
+
17
+ add_index(:metric_user_sessions, :metric_user_id)
18
+ add_index(:metric_user_sessions, :ip_4)
19
+ add_index(:metric_user_sessions, :session_start)
20
+ end
21
+
22
+ def down
23
+ drop_table :metric_user_sessions
24
+ end
25
+ end
26
+
27
+
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
3
+ CURRENT_PATH = Dir.pwd + '/'
4
+
5
+ require 'bundler/setup'
6
+ require 'i18n'
7
+ require 'optparse'
8
+ require 'fileutils'
9
+ require 'trueandco_analytics'
10
+ require 'trueandco_analytics/cli/common'
11
+ require 'trueandco_analytics/cli/report'
12
+ require "#{ENGINE_ROOT}/app/commands/trueandco_analytics/report_c/generate"
13
+ require 'active_record'
14
+ require 'mysql2'
15
+ require "#{CURRENT_PATH}config/initializers/trueandco_analytics"
16
+
17
+ models_dir = "#{ENGINE_ROOT}/app/models/trueandco_analytics/"
18
+ require "#{models_dir}application_record.rb"
19
+ Dir["#{models_dir}/*.rb"].each do |f|
20
+ require f
21
+ end
22
+
23
+ connect_db = TrueandcoAnalytics::Config::Params.connect_db
24
+ 1.times do
25
+ break if connect_db.nil?
26
+ ActiveRecord::Base.establish_connection(connect_db)
27
+ TrueandcoAnalytics::Cli::Common.new(ARGV).execute
28
+ end
@@ -0,0 +1,20 @@
1
+ class String
2
+ def underscore!
3
+ gsub!(/(.)([A-Z])/,'\1_\2')
4
+ downcase!
5
+ end
6
+
7
+ def underscore
8
+ dup.tap { |s| s.underscore! }
9
+ end
10
+
11
+ def camelize(uppercase_first_letter = true)
12
+ string = self
13
+ if uppercase_first_letter
14
+ string = string.sub(/^[a-z\d]*/) { $&.capitalize }
15
+ else
16
+ string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { $&.downcase }
17
+ end
18
+ string.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.gsub('/', '::')
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails/generators'
2
+ module TrueandcoAnalytics
3
+ class InstallGenerator < Rails::Generators::Base
4
+ desc "Configuration UserMetrix gem"
5
+
6
+ def self.source_root
7
+ @source_root ||= File.join(File.dirname(__FILE__), 'templates')
8
+ end
9
+
10
+ def copy_initializer
11
+ template 'trueandco_analytics.rb', 'config/initializers/trueandco_analytics.rb'
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,37 @@
1
+ TrueandcoAnalytics.setup do |config|
2
+ # Time survey Analytics seconds
3
+ config.time_survey = 15
4
+
5
+ # Specify the method available in the ApplicationController to the receiving user object. Containing id, email.
6
+ #config.user_method = 'current_user'
7
+
8
+ # Specify css the selector class for the button purchase
9
+ config.buy_selector_class = 'buy'
10
+
11
+ # Specify the database where redis will store the statistics for the session user
12
+ config.connect_redis = {
13
+ #url: "redis://your_host:6379",
14
+ #redis_password: '',
15
+ redis_db: 1
16
+ }
17
+
18
+ # Specify Sidekiq config
19
+ config.sidekiq_configure_client_url = 'redis://localhost:6379/1'
20
+ config.sidekiq_configure_server_url = 'redis://localhost:6379/1'
21
+ config.sidekiq_configure_namespace = 'metric'
22
+
23
+ #Log name in rails app
24
+ config.log = nil
25
+
26
+ #Specify the database to use in console mode
27
+ =begin
28
+ config.connect_db = {
29
+ adapter: :mysql2,
30
+ database: 'development_db_name',
31
+ username: 'ivan',
32
+ password: '3443555',
33
+ host: '127.0.0.1',
34
+ port: 3306
35
+ }
36
+ =end
37
+ end