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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/active_cohort.rb +168 -0
  3. metadata +44 -0
@@ -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: []