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,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