ActiveCohort 0.0.1
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.
- checksums.yaml +7 -0
- data/lib/active_cohort.rb +168 -0
- metadata +44 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8ad4bb05ec3fab6b833e77b81f463873eb86fbe1
|
4
|
+
data.tar.gz: 3c8782baa944cebc1f36ca2a209d09d920075e61
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9b1f88caf2d8111c3f606e33eb16f08f710f3b27ade16d688ed2411b3c9455ef4a03d26f8934463d5931cae79663c2a8f6ccc840663d8ee9ab6384380565dbbd
|
7
|
+
data.tar.gz: 28d7ae8c4984b3d15d357dbd2758573363ebb9e868c790dc6fb611a83d69b6d820aaa6dc1d18ff70b72723bf51a813d0958a39c17bf36acc913fd46656ce8bf0
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# Public: Provides a cohort analysis of a set of ActiveRecord objects. Intended to be
|
2
|
+
# consumed by domain-specific classes, such AigAnalyst, OrderAnalyst, etc.
|
3
|
+
# See the constructor's documentation for information on the options hash.
|
4
|
+
#
|
5
|
+
# Examples
|
6
|
+
#
|
7
|
+
# cohort = ActiveCohort.new(some_options_hash)
|
8
|
+
# cohort.generate_report
|
9
|
+
# # => [["", "Week 0", "Week 1", "Week 2", "Week 3", "Week 4", "Week 5"],
|
10
|
+
# ["1/9", "27.0%", "8.1%", "2.7%", "0.0%", "0.0%", "0.0%"],
|
11
|
+
# ["1/16", "37.9%", "7.6%", "0.0%", "0.0%", "0.0%"],
|
12
|
+
# ["1/23", "42.2%", "3.1%", "0.0%", "0.0%"],
|
13
|
+
# ["1/30", "31.8%", "0.0%", "0.0%"],
|
14
|
+
# ["2/6", "-", "-"]]
|
15
|
+
#
|
16
|
+
# puts cohort.to_csv
|
17
|
+
# # => ,Week 0,Week 1,Week 2,Week 3,Week 4,Week 5
|
18
|
+
# 1/9,27.0%,8.1%,2.7%,0.0%,0.0%,0.0%
|
19
|
+
# 1/16,37.9%,7.6%,0.0%,0.0%,0.0%
|
20
|
+
# 1/23,42.2%,3.1%,0.0%,0.0%
|
21
|
+
# 1/30,31.8%,0.0%,0.0%
|
22
|
+
# 2/6,-,-
|
23
|
+
class ActiveCohort
|
24
|
+
attr_accessor :subject_collection, :activation_lambda
|
25
|
+
attr_writer :start_at, :interval_timestamp_field
|
26
|
+
|
27
|
+
# Public: Initialize a ActiveCohort.
|
28
|
+
#
|
29
|
+
# Required params
|
30
|
+
# subject_collection - An ActiveRecord collection of records to perform a
|
31
|
+
# cohort analysis on.
|
32
|
+
# activation_lambda - A lambda that returns a boolean indicating whether
|
33
|
+
# a given record has activated (e.g., converted,
|
34
|
+
# signed up, purchased, etc.)
|
35
|
+
# opts - A String naming the widget.
|
36
|
+
# start_at - The date at which to begin the analysis.
|
37
|
+
# Default: 30 days ago.
|
38
|
+
# interval - A string representation of the interval to run the analysis
|
39
|
+
# over (e.g, day, week, etc.) For instance, 'week' would
|
40
|
+
# result in a week-over-week analysis.
|
41
|
+
# Default: 'day'.
|
42
|
+
# interval_timestamp_field - A String representation of the timestamp
|
43
|
+
# field on the cohort records to be used to
|
44
|
+
# offset between intervals.
|
45
|
+
# Default: 'created_at'.
|
46
|
+
def initialize(subject_collection, activation_lambda, opts={})
|
47
|
+
@subject_collection = subject_collection
|
48
|
+
@activation_lambda = activation_lambda
|
49
|
+
opts.each { |k,v| instance_variable_set("@#{k}", v) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def interval
|
53
|
+
@interval || 'day'
|
54
|
+
end
|
55
|
+
|
56
|
+
def interval=(interval)
|
57
|
+
unless interval.downcase.in? valid_intervals
|
58
|
+
raise "The interval \"#{interval}\" isn't valid.\n" +
|
59
|
+
"Use #{valid_intervals.join ', '}"
|
60
|
+
end
|
61
|
+
@interval = interval.downcase
|
62
|
+
end
|
63
|
+
|
64
|
+
def start_at
|
65
|
+
@start_at || 30.days.ago
|
66
|
+
end
|
67
|
+
|
68
|
+
def interval_timestamp_field
|
69
|
+
@interval_timestamp_field || 'created_at'
|
70
|
+
end
|
71
|
+
|
72
|
+
# Public: Generates a cohort report using params supplied to the instance in
|
73
|
+
# the constructor.
|
74
|
+
#
|
75
|
+
# Example
|
76
|
+
# cohort.generate_report
|
77
|
+
# # => [["", "Week 0", "Week 1", "Week 2", "Week 3", "Week 4", "Week 5"],
|
78
|
+
# ["1/9", "27.0%", "8.1%", "2.7%", "0.0%", "0.0%", "0.0%"],
|
79
|
+
# ["1/16", "37.9%", "7.6%", "0.0%", "0.0%", "0.0%"],
|
80
|
+
# ["1/23", "42.2%", "3.1%", "0.0%", "0.0%"],
|
81
|
+
# ["1/30", "31.8%", "0.0%", "0.0%"],
|
82
|
+
# ["2/6", "-", "-"]]
|
83
|
+
#
|
84
|
+
# Returns an Array of values representing the report.
|
85
|
+
def generate_report
|
86
|
+
validate_required_fields
|
87
|
+
@report = []
|
88
|
+
@report << header
|
89
|
+
|
90
|
+
(number_of_intervals - 1).times do |row|
|
91
|
+
@report << build_row(row)
|
92
|
+
end
|
93
|
+
@report
|
94
|
+
end
|
95
|
+
|
96
|
+
# Public: Outputs the cohort report in CSV format. Does not regenerate the
|
97
|
+
# report if the instance has already generated it.
|
98
|
+
#
|
99
|
+
# Example
|
100
|
+
# puts cohort.to_csv
|
101
|
+
# # => ,Week 0,Week 1,Week 2,Week 3,Week 4,Week 5
|
102
|
+
# 1/9,27.0%,8.1%,2.7%,0.0%,0.0%,0.0%
|
103
|
+
# 1/16,37.9%,7.6%,0.0%,0.0%,0.0%
|
104
|
+
# 1/23,42.2%,3.1%,0.0%,0.0%
|
105
|
+
# 1/30,31.8%,0.0%,0.0%
|
106
|
+
# 2/6,-,-
|
107
|
+
#
|
108
|
+
# Returns a String representation of the report with CSV formatting.
|
109
|
+
def to_csv(seperator=',')
|
110
|
+
report = @report || generate_report
|
111
|
+
report.map{ |row| row.join(seperator) }.join("\n")
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
def header
|
116
|
+
header = ['']
|
117
|
+
number_of_intervals.times do |i|
|
118
|
+
header << "#{interval.capitalize} #{i}"
|
119
|
+
end
|
120
|
+
header
|
121
|
+
end
|
122
|
+
|
123
|
+
def number_of_intervals
|
124
|
+
@interval == 'day' ? 30 : 6
|
125
|
+
end
|
126
|
+
|
127
|
+
def valid_intervals
|
128
|
+
%w(day week month)
|
129
|
+
end
|
130
|
+
|
131
|
+
def assemble_cohort(start_date, end_date)
|
132
|
+
@subject_collection.where(
|
133
|
+
@interval_timestamp_field.to_sym => start_date..end_date
|
134
|
+
)
|
135
|
+
end
|
136
|
+
|
137
|
+
def percentage_as_string(numerator, denominator)
|
138
|
+
return "-" if denominator.zero?
|
139
|
+
"#{((numerator / denominator.to_f) * 100).round(1)}%"
|
140
|
+
end
|
141
|
+
|
142
|
+
def start_date_for_cell(row, col)
|
143
|
+
row_offset = row.send(:"#{interval}")
|
144
|
+
col_offset = col.send(:"#{interval}")
|
145
|
+
(start_at + row_offset + col_offset).send(:"beginning_of_#{interval}")
|
146
|
+
end
|
147
|
+
|
148
|
+
def build_row(row)
|
149
|
+
row_values = []
|
150
|
+
row_offset = row.send(:"#{interval}")
|
151
|
+
cohort_start_date = (start_at + row_offset).send(:"beginning_of_#{interval}")
|
152
|
+
cohort_end_date = cohort_start_date.send(:"end_of_#{interval}")
|
153
|
+
cohort = assemble_cohort cohort_start_date, cohort_end_date
|
154
|
+
row_values << cohort_start_date.strftime("%-m/%-d")
|
155
|
+
(number_of_intervals - row).times do |col|
|
156
|
+
activation_start_date = start_date_for_cell(row, col)
|
157
|
+
activation_end_date = activation_start_date.send(:"end_of_#{interval}")
|
158
|
+
activated = cohort.select { |c| @activation_lambda.call(c, activation_start_date, activation_end_date) }
|
159
|
+
row_values << percentage_as_string(activated.length, cohort.length)
|
160
|
+
end
|
161
|
+
row_values
|
162
|
+
end
|
163
|
+
|
164
|
+
def validate_required_fields
|
165
|
+
raise "Missing subject_collection" unless subject_collection.present?
|
166
|
+
raise "Missing activation_lambda" unless activation_lambda.present?
|
167
|
+
end
|
168
|
+
end
|
metadata
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ActiveCohort
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Josh Saint Jacque
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-25 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Quickly product cohort reports from active record data.
|
14
|
+
email: joshsaintjacque@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/active_cohort.rb
|
20
|
+
homepage: http://rubygems.org/gems/activecohort
|
21
|
+
licenses:
|
22
|
+
- MIT
|
23
|
+
metadata: {}
|
24
|
+
post_install_message:
|
25
|
+
rdoc_options: []
|
26
|
+
require_paths:
|
27
|
+
- lib
|
28
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
requirements: []
|
39
|
+
rubyforge_project:
|
40
|
+
rubygems_version: 2.6.6
|
41
|
+
signing_key:
|
42
|
+
specification_version: 4
|
43
|
+
summary: Cohort reports for ActiveRecord.
|
44
|
+
test_files: []
|