trueandco_analytics 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,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,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
|
File without changes
|
@@ -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
data/config/secrets.yml
ADDED
@@ -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
|