mixpanel_test_service 0.0.6 → 0.0.7
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.
- data/lib/mixpanel_test/analysis.rb +152 -0
- data/lib/mixpanel_test/parser.rb +55 -0
- data/lib/mixpanel_test/service.rb +45 -29
- metadata +9 -7
@@ -0,0 +1,152 @@
|
|
1
|
+
module MixpanelTest
|
2
|
+
|
3
|
+
class Analysis
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
|
7
|
+
@events_count = {}
|
8
|
+
@events_properties_counts = {}
|
9
|
+
@events_properties_values_counts = {}
|
10
|
+
@properties_counts = {}
|
11
|
+
@properties_values_counts = {}
|
12
|
+
@super_properties_counts = {}
|
13
|
+
@super_properties_values_counts = {}
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize_for_event(ev)
|
18
|
+
|
19
|
+
name = ev["event"]
|
20
|
+
|
21
|
+
initialize_for_event_property_list(name, ev["properties"].keys)
|
22
|
+
|
23
|
+
ev["properties"].each do |k, v|
|
24
|
+
|
25
|
+
# Initialize property counters
|
26
|
+
@events_properties_values_counts[name][k][v] ||= 0
|
27
|
+
@properties_values_counts[k][v] ||= 0
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize_for_event_property_list(name, props)
|
34
|
+
|
35
|
+
# Initialize counters
|
36
|
+
@events_count[name] ||= 0
|
37
|
+
@events_properties_counts[name] ||= {}
|
38
|
+
@events_properties_values_counts[name] ||= {}
|
39
|
+
|
40
|
+
props.each do |k|
|
41
|
+
|
42
|
+
# Initialize property counters
|
43
|
+
@events_properties_counts[name][k] ||= 0
|
44
|
+
@events_properties_values_counts[name][k] ||= {}
|
45
|
+
@properties_counts[k] ||= 0
|
46
|
+
@properties_values_counts[k] ||= {}
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_documentation_object(obj)
|
53
|
+
# Each event and property counts for an undefined number > 0.
|
54
|
+
# Discard notes
|
55
|
+
# Only useful for merging two documentation objects
|
56
|
+
|
57
|
+
obj["events"].each do |name, props|
|
58
|
+
|
59
|
+
initialize_for_event_property_list(name, props)
|
60
|
+
@events_count[name] += 1
|
61
|
+
|
62
|
+
props.each do |p_name|
|
63
|
+
@events_properties_counts[name][p_name] += 1
|
64
|
+
@properies_count[p_name] += 1
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
obj["properties"].each do |p|
|
70
|
+
@properties_count[p["name"]] ||= 0
|
71
|
+
@properties_count[p["name"]] += 1
|
72
|
+
|
73
|
+
p["values"].each do |v|
|
74
|
+
@properties_values_count[p["name"]][v] ||= 0
|
75
|
+
@properties_values_count[p["name"]][v] += 1
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_events(arr)
|
81
|
+
arr.each do |ev| add_event(ev) end
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_event(ev)
|
85
|
+
|
86
|
+
initialize_for_event(ev)
|
87
|
+
name = ev["event"]
|
88
|
+
|
89
|
+
ev["properties"].each do |k, v|
|
90
|
+
|
91
|
+
# Increment property counters
|
92
|
+
@properties_counts[k] += 1
|
93
|
+
@events_properties_counts[name][k] += 1
|
94
|
+
@events_properties_values_counts[name][k][v] += 1
|
95
|
+
@properties_values_counts[k][v] += 1
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
def format_verbose
|
102
|
+
|
103
|
+
puts "Mixpanel events caught:"
|
104
|
+
pp @events_count
|
105
|
+
|
106
|
+
puts "Mixpanel properties caught for each event:"
|
107
|
+
pp @events_properties_counts
|
108
|
+
|
109
|
+
puts "All Mixpanel properties caught:"
|
110
|
+
pp @properties_counts
|
111
|
+
|
112
|
+
puts "Mixpanel values frequency by property:"
|
113
|
+
pp @properties_values_counts
|
114
|
+
|
115
|
+
puts "Mixpanel values frequency by property and event:"
|
116
|
+
pp @events_properties_values_counts
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
def format_documentation
|
121
|
+
|
122
|
+
pp documentation_object
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
def documentation_object
|
127
|
+
default_notes = "Undocumented"
|
128
|
+
{
|
129
|
+
"events" => @events_count.keys.map do |k|
|
130
|
+
{
|
131
|
+
k => {
|
132
|
+
"notes" => default_notes,
|
133
|
+
"properties" => @events_properties_counts[k].keys
|
134
|
+
}
|
135
|
+
}
|
136
|
+
end,
|
137
|
+
"properties" => @properties_counts.keys.map do |k|
|
138
|
+
{
|
139
|
+
"name" => k,
|
140
|
+
"notes" => default_notes,
|
141
|
+
"values" => @properties_values_counts[k].keys
|
142
|
+
}
|
143
|
+
end
|
144
|
+
}
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'uri'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module MixpanelTest
|
6
|
+
|
7
|
+
class Parser
|
8
|
+
|
9
|
+
module InstanceMethods
|
10
|
+
|
11
|
+
# Extract the query parameters as a hash from the query string
|
12
|
+
def parse_query_params(qs)
|
13
|
+
qs.to_s.split('&').map do |s| s.split('=') end.map do |a| {URI.unescape(a[0]) => URI.unescape(a[1])} end.inject(&:merge) || {}
|
14
|
+
end
|
15
|
+
|
16
|
+
# Decode the data string.
|
17
|
+
def decode_data(encoded_data)
|
18
|
+
|
19
|
+
# Decode the data
|
20
|
+
data = Base64.decode64(encoded_data)
|
21
|
+
|
22
|
+
# Eliminate extemporaneous chars outside the JSON
|
23
|
+
data = data.match(/\{.*\}/)[0]
|
24
|
+
|
25
|
+
# Parse with JSON
|
26
|
+
data = JSON.parse(data)
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def decode_cookie(cookie)
|
31
|
+
|
32
|
+
#begin
|
33
|
+
# split cookie and find a mixpanel section
|
34
|
+
section = cookie.split('; ').find do |tok| tok.match(/^mp_/) end;
|
35
|
+
|
36
|
+
# Decode and find json
|
37
|
+
data = URI.unescape(section).match(/\{.*\}/)[0]
|
38
|
+
|
39
|
+
# Parse as JSON
|
40
|
+
JSON.parse(data)
|
41
|
+
|
42
|
+
#rescue
|
43
|
+
|
44
|
+
# raise BadCookieError, but first, figure out how to test this functionality
|
45
|
+
|
46
|
+
#end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
extend InstanceMethods
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -1,16 +1,21 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'net/http/server'
|
3
|
+
require File.expand_path('../parser', __FILE__)
|
4
|
+
require File.expand_path('../analysis', __FILE__)
|
3
5
|
require 'thread'
|
4
|
-
require 'base64'
|
5
6
|
require 'json'
|
6
7
|
require 'net/http'
|
7
8
|
|
8
9
|
module MixpanelTest
|
9
10
|
class Service
|
11
|
+
class PathNotFoundError < StandardError ; end
|
12
|
+
class NoDataError < StandardError ; end
|
10
13
|
|
11
|
-
|
14
|
+
include MixpanelTest::Parser::InstanceMethods
|
15
|
+
|
16
|
+
attr_accessor :events, :people # The live queue of received events
|
12
17
|
attr_writer :log_events # Whether to log all events
|
13
|
-
attr_reader :all_events # Log of all events
|
18
|
+
attr_reader :all_events, :all_people, :all_imported_events # Log of all events
|
14
19
|
|
15
20
|
@@js_headers = {'Connection' => "keep-alive", "Content-Type" => "application/x-javascript", "Transfer-Encoding" => "chunked", 'Cache-Control' => 'max-age=86400'}
|
16
21
|
@@api_headers = {
|
@@ -29,23 +34,28 @@ module MixpanelTest
|
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
32
|
-
def shutdown
|
33
|
-
@server.shutdown
|
34
|
-
end
|
35
|
-
|
36
|
-
def stopped?
|
37
|
-
@server.stopped?
|
38
|
-
end
|
39
|
-
|
40
37
|
def stop
|
38
|
+
puts "Stopping Mixpanel test service"
|
41
39
|
@server.stop
|
40
|
+
Thread.pass until @server.stopped?
|
41
|
+
puts "Mixpanel test server stopped"
|
42
|
+
end
|
43
|
+
|
44
|
+
def analysis
|
45
|
+
@analysis ||= Analysis.new
|
46
|
+
@analysis.add_events(@all_events)
|
47
|
+
@analysis
|
42
48
|
end
|
43
49
|
|
44
50
|
def initialize(options={})
|
45
51
|
|
46
52
|
@events = []
|
53
|
+
@people = []
|
47
54
|
@all_events = []
|
55
|
+
@all_people = []
|
56
|
+
@all_imported_events = []
|
48
57
|
@log_events = options[:log_events]
|
58
|
+
@log_people = options[:log_people]
|
49
59
|
@events_mutex = Mutex.new
|
50
60
|
|
51
61
|
@mixpanel_js_cache = {}
|
@@ -65,29 +75,35 @@ module MixpanelTest
|
|
65
75
|
else
|
66
76
|
|
67
77
|
# Parse the query string
|
68
|
-
query_params = req[:uri][:query]
|
69
|
-
|
78
|
+
query_params = parse_query_params(req[:uri][:query])
|
70
79
|
|
71
80
|
if query_params["data"]
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
data = decode_data(query_params["data"])
|
82
|
+
|
83
|
+
if req[:uri][:path].to_s.match(/engage/)
|
84
|
+
|
85
|
+
# Save
|
86
|
+
transaction do
|
87
|
+
@people << data
|
88
|
+
@all_people << data if @log_people
|
89
|
+
end
|
90
|
+
elsif req[:uri][:path].to_s.match(/track/)
|
91
|
+
# Save
|
92
|
+
|
93
|
+
transaction do
|
94
|
+
@events << data
|
95
|
+
@all_events << data if @log_events
|
96
|
+
end
|
97
|
+
elsif req[:uri][:path].to_s.match(/import/)
|
98
|
+
transaction do
|
99
|
+
@all_imported_events << data
|
100
|
+
end
|
101
|
+
else
|
102
|
+
raise PathNotFoundError
|
85
103
|
end
|
86
104
|
|
87
|
-
@all_events << data if @log_events
|
88
|
-
|
89
105
|
else
|
90
|
-
|
106
|
+
raise NoDataError
|
91
107
|
end
|
92
108
|
|
93
109
|
next [200, @@api_headers, ["1"]]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mixpanel_test_service
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2012-09-05 00:00:00.000000000Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: net-http-server
|
16
|
-
requirement: &
|
16
|
+
requirement: &9783660 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - =
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.2.2
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *9783660
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: pry
|
27
|
-
requirement: &
|
27
|
+
requirement: &9778460 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *9778460
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec
|
38
|
-
requirement: &
|
38
|
+
requirement: &9749500 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *9749500
|
47
47
|
description: Mixpanel test service - logs all Mixpanel events sent to it
|
48
48
|
email: a.johncant@gmail.com
|
49
49
|
executables: []
|
@@ -51,6 +51,8 @@ extensions: []
|
|
51
51
|
extra_rdoc_files: []
|
52
52
|
files:
|
53
53
|
- test/server.rb
|
54
|
+
- lib/mixpanel_test/parser.rb
|
55
|
+
- lib/mixpanel_test/analysis.rb
|
54
56
|
- lib/mixpanel_test/service.rb
|
55
57
|
- lib/mixpanel_test.rb
|
56
58
|
homepage: https://github.com/johncant/mixpanel_test_service
|