cohortly 0.0.9.1 → 0.0.92
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/app/controllers/cohortly/reports_controller.rb +45 -12
- data/app/models/cohortly/cohorts.rb +8 -0
- data/app/models/cohortly/group_cohort.rb +9 -0
- data/app/models/cohortly/metric.rb +1 -1
- data/app/models/cohortly/period_cohort.rb +24 -0
- data/app/models/cohortly/tag_report.rb +126 -0
- data/app/models/cohortly/user_cohort.rb +18 -0
- data/app/views/cohortly/metrics/_groups.html.erb +1 -1
- data/app/views/cohortly/metrics/_groups_intersect.html.erb +14 -0
- data/app/views/cohortly/metrics/_tags.html.erb +1 -1
- data/app/views/cohortly/reports/index.html.erb +3 -1
- data/app/views/layouts/cohortly/application.html.erb +15 -13
- data/lib/cohortly/engine.rb +4 -2
- data/lib/cohortly/version.rb +1 -1
- data/lib/tasks/run_reports.rake +43 -16
- data/test/dummy/app/models/cohortly/cohorts.rb +15 -0
- data/test/dummy/log/development.log +157 -0
- metadata +10 -6
- data/app/models/cohortly/report.rb +0 -118
- data/app/models/cohortly/report_meta.rb +0 -17
- data/test/dummy/tmp/pids/server.pid +0 -1
@@ -1,25 +1,58 @@
|
|
1
1
|
class Cohortly::ReportsController < Cohortly::CohortlyController
|
2
2
|
def index
|
3
3
|
Cohortly::Metric.send :attr_accessor, :groups
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
Cohortly::Metric.send :attr_accessor, :groups_intersect
|
5
|
+
@metric_search = Cohortly::Metric.new(params[:cohortly_metric])
|
6
|
+
json_res = { }
|
7
|
+
|
8
|
+
if params[:cohortly_metric]
|
9
|
+
tags = @metric_search.tags.any? ? @metric_search.tags : ['upload']
|
10
|
+
groups = @metric_search.groups
|
11
|
+
groups_intersect = @metric_search.groups_intersect
|
12
|
+
|
13
|
+
reports = Cohortly::TagReport.where(:tags => tags).all
|
14
|
+
|
15
|
+
@tag_report = reports.inject(Cohortly::TagReport.new) { |accum, tag_report| accum.merge(tag_report) }
|
16
|
+
|
17
|
+
user_base_func = lambda { |user_ids| user_ids.length }
|
18
|
+
|
19
|
+
if groups || groups_intersect
|
20
|
+
users_constraint = false
|
21
|
+
if groups && groups.any?
|
22
|
+
users_constraint = Cohortly::UserCohort.union(Cohortly::GroupCohort.where(:name => groups).all)
|
23
|
+
end
|
24
|
+
if groups_intersect && groups_intersect.any?
|
25
|
+
group_intersect_users = Cohortly::UserCohort.intersect(Cohortly::GroupCohort.where(:name => groups_intersect).all)
|
26
|
+
users_constraint = users_constraint ? users_constraint & group_intersect_users : group_intersect_users
|
27
|
+
end
|
28
|
+
if users_constraint
|
29
|
+
user_base_func = lambda { |user_ids| (users_constraint & user_ids).length }
|
30
|
+
@tag_report.intersect(users_constraint.map(&:to_s))
|
31
|
+
end
|
32
|
+
end
|
7
33
|
|
8
|
-
|
9
|
-
|
34
|
+
@base_n = Cohortly::PeriodCohort.all.inject({ }) do |base_n, per_coh|
|
35
|
+
base_n.merge( per_coh.name => {
|
36
|
+
:count => user_base_func.call(per_coh.user_ids),
|
37
|
+
:pretty_date => DateTime.strptime( per_coh.name, '%Y-%W').beginning_of_week.strftime('%m-%d-%Y') } )
|
38
|
+
end
|
10
39
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
40
|
+
json_res = {
|
41
|
+
:groups => groups,
|
42
|
+
:groups_intersect => groups_intersect,
|
43
|
+
:tags => tags,
|
44
|
+
:weekly => true,
|
45
|
+
:data => @tag_report.data_without_empty_rows,
|
46
|
+
:base_n => @base_n
|
47
|
+
}
|
15
48
|
end
|
16
|
-
|
17
|
-
|
49
|
+
|
18
50
|
respond_to do |format|
|
19
51
|
format.html
|
20
52
|
format.js {
|
21
|
-
render :json =>
|
53
|
+
render :json => json_res
|
22
54
|
}
|
23
55
|
end
|
24
56
|
end
|
57
|
+
|
25
58
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Cohortly
|
2
|
+
class PeriodCohort < Cohortly::UserCohort
|
3
|
+
key :start_time, Time
|
4
|
+
key :weekly, Boolean
|
5
|
+
|
6
|
+
def end_time
|
7
|
+
self.start_time + period
|
8
|
+
end
|
9
|
+
|
10
|
+
def period
|
11
|
+
self.weekly ? 1.week : 1.month
|
12
|
+
end
|
13
|
+
|
14
|
+
def key_pattern
|
15
|
+
self.weekly ? "%Y-%W" : "%Y-%m"
|
16
|
+
end
|
17
|
+
|
18
|
+
def store!
|
19
|
+
self.user_ids = Cohortly::Cohorts.range(self.start_time..self.end_time)
|
20
|
+
self.name = (self.start_time + 3.days).strftime(key_pattern)
|
21
|
+
self.save
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Cohortly
|
2
|
+
class TagReport
|
3
|
+
include MongoMapper::Document
|
4
|
+
|
5
|
+
key :collection_name, String
|
6
|
+
key :last_update_on, Time
|
7
|
+
key :data, Hash
|
8
|
+
key :tags, Array
|
9
|
+
|
10
|
+
def run
|
11
|
+
if self.last_update_on.nil?
|
12
|
+
self.recalc_table
|
13
|
+
else
|
14
|
+
self.update_table
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def tag_query
|
19
|
+
self.tags.any? ? { :tags => self.tags.first } : { }
|
20
|
+
end
|
21
|
+
|
22
|
+
def cell_query(cohort_range, cell_range)
|
23
|
+
{ :created_at => {
|
24
|
+
:$gt => cell_range.begin,
|
25
|
+
:$lt => cell_range.end},
|
26
|
+
:user_start_date => {
|
27
|
+
:$gt => cohort_range.begin,
|
28
|
+
:$lt => cohort_range.end } }.tap { |x|
|
29
|
+
self.tags ? x.merge!( tag_query ) : x
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def start_time
|
34
|
+
Cohortly::Metric.where(tag_query).sort(:user_start_date).limit(1).first.user_start_date.utc.beginning_of_week
|
35
|
+
end
|
36
|
+
|
37
|
+
def cohort_iter(starting_time)
|
38
|
+
cohort_time = starting_time
|
39
|
+
while cohort_time <= Time.now
|
40
|
+
yield cohort_time..(cohort_time + 1.week)
|
41
|
+
cohort_time += 1.week
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def cell_iter(cell_starting_time)
|
46
|
+
cohort_time = start_time
|
47
|
+
while cohort_time <= Time.now
|
48
|
+
cell_time = [cohort_time, cell_starting_time].max
|
49
|
+
while cell_time <= Time.now
|
50
|
+
yield cohort_time..(cohort_time + 1.week), cell_time..(cell_time + 1.week)
|
51
|
+
cell_time += 1.week
|
52
|
+
end
|
53
|
+
cohort_time += 1.week
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def recalc_table
|
58
|
+
self.data = { }
|
59
|
+
self.last_update_on = Time.now
|
60
|
+
self.cell_iter(self.start_time) { |cohort_range, cell_range| self.store_cell(cohort_range, cell_range)}
|
61
|
+
end
|
62
|
+
|
63
|
+
def update_table
|
64
|
+
starting_time = self.last_update_on.utc.beginning_of_week
|
65
|
+
self.last_update_on = Time.now
|
66
|
+
self.cell_iter(starting_time)
|
67
|
+
end
|
68
|
+
|
69
|
+
def store_cell(cohort_range, cell_range)
|
70
|
+
cohort_key = cohort_range.begin.strftime('%Y-%W')
|
71
|
+
cell_key = cell_range.begin.strftime('%Y-%W')
|
72
|
+
p cohort_key + " " + cell_key
|
73
|
+
self.data[cohort_key] ||= { }
|
74
|
+
self.data[cohort_key][cell_key] ||= { }
|
75
|
+
Cohortly::Metric.collection.distinct( :user_id, cell_query(cohort_range, cell_range) ).each do |user_id|
|
76
|
+
self.data[cohort_key][cell_key][user_id.to_s] = 1
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def merge(tag_report)
|
81
|
+
TagReport::Product.new(:tag_report => self, :tags => self.tags, :data => self.data).merge(tag_report)
|
82
|
+
end
|
83
|
+
|
84
|
+
class Product
|
85
|
+
attr_accessor :tag_report, :tags, :data
|
86
|
+
def initialize(options = { })
|
87
|
+
self.tags = options[:tags]
|
88
|
+
self.tag_report = options[:tag_report]
|
89
|
+
self.data = options[:data]
|
90
|
+
end
|
91
|
+
|
92
|
+
def merge(tag_report)
|
93
|
+
TagReport::Product.new(:tag_report => self.tag_report,
|
94
|
+
:tags => self.tags | tag_report.tags,
|
95
|
+
:data => self.deep_merge(self.data, tag_report.data))
|
96
|
+
end
|
97
|
+
|
98
|
+
def deep_merge(data1, data2)
|
99
|
+
(data1.keys + data2.keys).uniq.inject({}) do |accum, key|
|
100
|
+
if (data1[key] || data2[key]).is_a?(Hash)
|
101
|
+
accum[key] = deep_merge(data1[key] || { }, data2[key] || { })
|
102
|
+
else
|
103
|
+
accum[key] = data1[key] || data2[key]
|
104
|
+
end
|
105
|
+
accum
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# user_ids need to be strings
|
110
|
+
def intersect(user_ids)
|
111
|
+
self.tag_report.cell_iter(self.tag_report.start_time) do |cohort_range, cell_range|
|
112
|
+
cohort_key = cohort_range.begin.strftime('%Y-%W')
|
113
|
+
cell_key = cell_range.begin.strftime('%Y-%W')
|
114
|
+
cell = self.data[cohort_key][cell_key]
|
115
|
+
intersected_ids = cell.keys & user_ids
|
116
|
+
self.data[cohort_key][cell_key] = intersected_ids.inject({ }) { |accum, user_id| accum.merge!(user_id => 1); accum }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def data_without_empty_rows
|
121
|
+
self.data.keys.sort.inject({ }) { |new_data, key| puts ; self.data[key].values.collect(&:length).sum > 0 ? new_data.merge(key => self.data[key]) : new_data }
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Cohortly
|
2
|
+
class UserCohort
|
3
|
+
include MongoMapper::Document
|
4
|
+
key :user_ids, Array
|
5
|
+
key :name, String
|
6
|
+
key :_type, String
|
7
|
+
timestamps!
|
8
|
+
|
9
|
+
def self.intersect(cohorts)
|
10
|
+
cohorts.inject(cohorts.first.user_ids) { |user_ids, cohort| user_ids & cohort.user_ids }
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.union(cohorts)
|
14
|
+
cohorts.inject([]) { |user_ids, cohort| user_ids | cohort.user_ids }
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<div class="tags">
|
2
|
-
<% Cohortly::
|
2
|
+
<% Cohortly::Cohorts.group_names.sort.each_slice(3) do |groups| %>
|
3
3
|
<div class="tag_group" style="float:left; padding: 10px;">
|
4
4
|
<% groups.sort.each do |group| %>
|
5
5
|
<div class"tag">
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<div class="tags">
|
2
|
+
<% Cohortly::Cohorts.group_names.sort.each_slice(3) do |groups| %>
|
3
|
+
<div class="tag_group" style="float:left; padding: 10px;">
|
4
|
+
<% groups.sort.each do |group| %>
|
5
|
+
<div class"tag">
|
6
|
+
<%= check_box_tag 'cohortly_metric[groups_intersect][]', group,
|
7
|
+
params[:cohortly_metric] && params[:cohortly_metric][:groups_intersect] &&
|
8
|
+
params[:cohortly_metric][:groups_intersect].include?(group) %><label><%= group %></label>
|
9
|
+
</div>
|
10
|
+
<% end %>
|
11
|
+
</div>
|
12
|
+
<% end %>
|
13
|
+
<div style="clear:both;"></div>
|
14
|
+
</div>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<div class="tags">
|
2
|
-
<%
|
2
|
+
<% Cohortly::TagReport.collection.distinct(:tags).sort.each_slice(8) do |tags| %>
|
3
3
|
<div class="tag_group" style="float:left; padding: 10px;">
|
4
4
|
<% tags.sort.each do |tag| %>
|
5
5
|
<div class"tag">
|
@@ -2,8 +2,10 @@
|
|
2
2
|
|
3
3
|
<%= form_for @metric_search, :url => cohortly_reports_path, :html =>
|
4
4
|
{:method => :get, :class => 'cohortly_report_form'} do |f| %>
|
5
|
-
<h3>Groups</h3>
|
5
|
+
<h3>Groups Union</h3>
|
6
6
|
<%= render :partial => 'cohortly/metrics/groups' %>
|
7
|
+
<h3>Groups Intersect</h3>
|
8
|
+
<%= render :partial => 'cohortly/metrics/groups_intersect' %>
|
7
9
|
<h3>Tags</h3>
|
8
10
|
<%= render :partial => 'cohortly/metrics/tags' %>
|
9
11
|
<%= f.submit 'Get Report'%>
|
@@ -34,9 +34,11 @@
|
|
34
34
|
render: function() {
|
35
35
|
$('.result_table', this.el).html(
|
36
36
|
['<h3>',
|
37
|
-
|
37
|
+
this.model.get('groups') ? 'Groups: ' + this.model.get('groups').join(', ') + ' | ': 'Groups: __ | ',
|
38
38
|
'',
|
39
|
-
|
39
|
+
this.model.get('groups_intersect') ? 'Groups Int: ' + this.model.get('groups_intersect').join(', ') + ' | ': 'Groups Int: __ |',
|
40
|
+
'',
|
41
|
+
this.model.get('tags') ? 'Tags: ' + this.model.get('tags').join(', ') : 'Tags: __ ',
|
40
42
|
'</h3>',
|
41
43
|
'<table class="one-column-emphasis">',
|
42
44
|
'<colgroup><col class="oce-first"></colgroup>',
|
@@ -55,22 +57,23 @@
|
|
55
57
|
].join('');
|
56
58
|
},
|
57
59
|
render_rows: function() {
|
58
|
-
return _(this.model.get('data')).
|
60
|
+
return _(_(this.model.get('data')).keys()).sortBy(function(x)
|
61
|
+
{ return x }).map(this.render_row).join('');
|
59
62
|
},
|
60
|
-
render_row: function(
|
61
|
-
var
|
62
|
-
var base_n = this.model.get('base_n')[row_key];
|
63
|
+
render_row: function(row_key) {
|
64
|
+
var base_n_data = this.model.get('base_n')[row_key];
|
63
65
|
return [
|
64
66
|
'<tr>',
|
65
|
-
'<td>' +
|
66
|
-
'<td>' +
|
67
|
-
this.render_cells(
|
67
|
+
'<td>' + base_n_data.pretty_date + '</td>',
|
68
|
+
'<td>' + base_n_data.count + '</td>',
|
69
|
+
this.render_cells(row_key, base_n_data.count),
|
68
70
|
'</tr>'
|
69
71
|
].join('');
|
70
72
|
},
|
71
|
-
render_cells: function(
|
72
|
-
|
73
|
-
|
73
|
+
render_cells: function(row_key, base_n) {
|
74
|
+
var row = this.model.get('data')[row_key];
|
75
|
+
return _(_(row).keys()).sortBy(function(x){return x}).slice(0,14).map(function(key) {
|
76
|
+
var num_users = _(row[key]).keys().length;
|
74
77
|
var percent = (base_n > 0) ? (num_users / base_n) : 0;
|
75
78
|
return [
|
76
79
|
'<td style="text-align:right;">',
|
@@ -79,7 +82,6 @@
|
|
79
82
|
].join('');
|
80
83
|
}).join('');
|
81
84
|
}
|
82
|
-
|
83
85
|
});
|
84
86
|
|
85
87
|
$(function() {
|
data/lib/cohortly/engine.rb
CHANGED
@@ -9,8 +9,10 @@ module Cohortly
|
|
9
9
|
Cohortly::Metric.connection Mongo::Connection.new(cfg.host, cfg.port)
|
10
10
|
Cohortly::Metric.set_database_name cfg.database
|
11
11
|
Cohortly::Metric.database.authenticate(cfg.username, cfg.password) if(cfg.password)
|
12
|
-
Cohortly::
|
13
|
-
Cohortly::
|
12
|
+
Cohortly::TagReport.connection Cohortly::Metric.connection
|
13
|
+
Cohortly::TagReport.set_database_name cfg.database
|
14
|
+
Cohortly::UserCohort.connection Cohortly::Metric.connection
|
15
|
+
Cohortly::UserCohort.set_database_name cfg.database
|
14
16
|
end
|
15
17
|
end
|
16
18
|
Cohortly::StoredProcedures.store_procedures
|
data/lib/cohortly/version.rb
CHANGED
data/lib/tasks/run_reports.rake
CHANGED
@@ -1,28 +1,55 @@
|
|
1
1
|
namespace :cohortly do
|
2
2
|
namespace :run do
|
3
3
|
desc "run the reports for all the tags"
|
4
|
-
task :
|
5
|
-
Cohortly::
|
6
|
-
Cohortly::Metric.cohort_chart(nil, nil, true)
|
7
|
-
puts "main report"
|
8
|
-
real_tags = (Cohortly::Metric.collection.distinct(:tags) - Cohortly::TagConfig.all_groups)
|
4
|
+
task :recalc_reports => :environment do
|
5
|
+
real_tags = Cohortly::Metric.collection.distinct(:tags)
|
9
6
|
real_tags.each do |tag|
|
10
|
-
Cohortly::
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
real_tags.each do |tag|
|
15
|
-
puts "tag: #{tag} group: #{group}"
|
16
|
-
Cohortly::Metric.cohort_chart([tag], [group], true)
|
17
|
-
end
|
7
|
+
report = Cohortly::TagReport.where(:tags => tag).first
|
8
|
+
report ||= Cohortly::TagReport.new(:tags => [tag])
|
9
|
+
report.recalc_table
|
10
|
+
report.save
|
18
11
|
end
|
12
|
+
|
13
|
+
# the empty report
|
14
|
+
report = Cohortly::TagReport.where(:tags => []).first
|
15
|
+
report ||= Cohortly::TagReport.new(:tags => [])
|
16
|
+
report.recalc_table
|
17
|
+
report.save
|
19
18
|
end
|
20
19
|
|
21
20
|
desc "update all existing reports"
|
22
21
|
task :updates => :environment do
|
23
|
-
Cohortly::
|
24
|
-
|
25
|
-
|
22
|
+
real_tags = Cohortly::Metric.collection.distinct(:tags)
|
23
|
+
real_tags.each do |tag|
|
24
|
+
report = Cohortly::TagReport.where(:tags => tag).first
|
25
|
+
report ||= Cohortly::TagReport.new(:tags => [tag])
|
26
|
+
report.run
|
27
|
+
report.save
|
28
|
+
end
|
29
|
+
|
30
|
+
# the empty report
|
31
|
+
report = Cohortly::TagReport.where(:tags => []).first
|
32
|
+
report ||= Cohortly::TagReport.new(:tags => [])
|
33
|
+
report.run
|
34
|
+
report.save
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "build cohorts"
|
38
|
+
task :build_cohorts => :environment do
|
39
|
+
Cohortly::Cohorts.group_names.each do |name|
|
40
|
+
cohort = Cohortly::GroupCohort.find_or_create_by_name(name)
|
41
|
+
cohort.store!
|
42
|
+
end
|
43
|
+
|
44
|
+
#weekly cohort
|
45
|
+
cur_time = Cohortly::Cohorts.first_user_start_date.utc.beginning_of_week
|
46
|
+
while(cur_time < Time.now) do
|
47
|
+
time_key = (cur_time + 3.days).strftime("%Y-%W")
|
48
|
+
cohort = Cohortly::PeriodCohort.find_or_create_by_name(time_key)
|
49
|
+
cohort.start_time = cur_time
|
50
|
+
cohort.weekly = true
|
51
|
+
cohort.store!
|
52
|
+
cur_time += 1.week
|
26
53
|
end
|
27
54
|
end
|
28
55
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Cohortly
|
2
|
+
class Cohorts
|
3
|
+
def group_names
|
4
|
+
[:rand_1, :rand_2, :rand_3]
|
5
|
+
end
|
6
|
+
|
7
|
+
def group_name(name)
|
8
|
+
Cohortly::Metric.collection.distinct(:user_id, :tag => [name])
|
9
|
+
end
|
10
|
+
|
11
|
+
def range(time_range)
|
12
|
+
Cohortly::Metric.collection.distinct(:user_id, :user_start_time => { :$gte => time_range.begin, :lt => time_range.end })
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -9997,3 +9997,160 @@ Started GET "/cohortly/reports.js?utf8=%E2%9C%93" for 127.0.0.1 at 2011-10-27 00
|
|
9997
9997
|
Processing by Cohortly::ReportsController#index as JS
|
9998
9998
|
Parameters: {"utf8"=>"✓"}
|
9999
9999
|
Completed 200 OK in 166ms (Views: 112.0ms)
|
10000
|
+
|
10001
|
+
|
10002
|
+
Started GET "/" for 127.0.0.1 at 2011-10-30 14:52:13 -0400
|
10003
|
+
Processing by StuffController#index as HTML
|
10004
|
+
Rendered stuff/index.html.erb within layouts/application (1.5ms)
|
10005
|
+
Completed 200 OK in 101ms (Views: 95.9ms)
|
10006
|
+
|
10007
|
+
|
10008
|
+
Started GET "/cohortly/reports" for 127.0.0.1 at 2011-10-30 14:52:16 -0400
|
10009
|
+
Processing by Cohortly::ReportsController#index as HTML
|
10010
|
+
Completed in 56ms
|
10011
|
+
|
10012
|
+
ArgumentError (wrong number of arguments (0 for 1)):
|
10013
|
+
|
10014
|
+
|
10015
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.1ms)
|
10016
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (68.4ms)
|
10017
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (76.5ms)
|
10018
|
+
|
10019
|
+
|
10020
|
+
Started GET "/cohortly/reports" for 127.0.0.1 at 2011-10-30 14:54:50 -0400
|
10021
|
+
Processing by Cohortly::ReportsController#index as HTML
|
10022
|
+
Completed in 64ms
|
10023
|
+
|
10024
|
+
NameError (undefined local variable or method `cohort' for #<Cohortly::ReportsController:0x00000100e57a80>):
|
10025
|
+
|
10026
|
+
|
10027
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.1ms)
|
10028
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (56.9ms)
|
10029
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (61.4ms)
|
10030
|
+
|
10031
|
+
|
10032
|
+
Started GET "/cohortly/reports" for 127.0.0.1 at 2011-10-30 14:55:07 -0400
|
10033
|
+
Processing by Cohortly::ReportsController#index as HTML
|
10034
|
+
Completed in 75ms
|
10035
|
+
|
10036
|
+
NoMethodError (undefined method `stftime' for 2011-04-25 00:00:00 UTC:Time):
|
10037
|
+
|
10038
|
+
|
10039
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.1ms)
|
10040
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (65.4ms)
|
10041
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (70.0ms)
|
10042
|
+
|
10043
|
+
|
10044
|
+
Started GET "/cohortly/reports" for 127.0.0.1 at 2011-10-30 14:55:18 -0400
|
10045
|
+
Processing by Cohortly::ReportsController#index as HTML
|
10046
|
+
Completed in 52ms
|
10047
|
+
|
10048
|
+
NoMethodError (undefined method `start' for 2011-04-25 00:00:00 UTC..2011-05-02 00:00:00 UTC:Range):
|
10049
|
+
|
10050
|
+
|
10051
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.0ms)
|
10052
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (51.4ms)
|
10053
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (55.7ms)
|
10054
|
+
|
10055
|
+
|
10056
|
+
Started GET "/cohortly/reports" for 127.0.0.1 at 2011-10-30 14:55:28 -0400
|
10057
|
+
Processing by Cohortly::ReportsController#index as HTML
|
10058
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/metrics/_groups.html.erb (1.3ms)
|
10059
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/metrics/_tags.html.erb (1183.8ms)
|
10060
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/reports/index.html.erb within layouts/cohortly/application (1213.2ms)
|
10061
|
+
Completed in 3077ms
|
10062
|
+
|
10063
|
+
ActionView::Template::Error (comparison of String with Array failed):
|
10064
|
+
1: <div class="tags">
|
10065
|
+
2: <% (Cohortly::Metric.collection.distinct(:tags) - Cohortly::TagConfig.all_groups).sort.each_slice(8) do |tags| %>
|
10066
|
+
3: <div class="tag_group" style="float:left; padding: 10px;">
|
10067
|
+
4: <% tags.sort.each do |tag| %>
|
10068
|
+
5: <div class"tag">
|
10069
|
+
|
10070
|
+
|
10071
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.1ms)
|
10072
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (75.2ms)
|
10073
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/template_error.erb within rescues/layout (80.2ms)
|
10074
|
+
|
10075
|
+
|
10076
|
+
Started GET "/cohortly/reports" for 127.0.0.1 at 2011-10-30 15:06:30 -0400
|
10077
|
+
Processing by Cohortly::ReportsController#index as HTML
|
10078
|
+
Completed in 55ms
|
10079
|
+
|
10080
|
+
NoMethodError (undefined method `utc' for nil:NilClass):
|
10081
|
+
|
10082
|
+
|
10083
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.0ms)
|
10084
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (50.8ms)
|
10085
|
+
Rendered /Users/bhauman/.rvm/gems/ruby-1.9.2-p136@cohortly/gems/actionpack-3.0.4/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (55.3ms)
|
10086
|
+
|
10087
|
+
|
10088
|
+
Started GET "/cohortly/reports" for 127.0.0.1 at 2011-10-30 15:09:32 -0400
|
10089
|
+
Processing by Cohortly::ReportsController#index as HTML
|
10090
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/metrics/_groups.html.erb (0.7ms)
|
10091
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/metrics/_tags.html.erb (1129.6ms)
|
10092
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/reports/index.html.erb within layouts/cohortly/application (1136.1ms)
|
10093
|
+
Completed 200 OK in 2988ms (Views: 1140.6ms)
|
10094
|
+
|
10095
|
+
|
10096
|
+
Started GET "/cohortly/reports.js?utf8=%E2%9C%93" for 127.0.0.1 at 2011-10-30 15:09:43 -0400
|
10097
|
+
Processing by Cohortly::ReportsController#index as JS
|
10098
|
+
Parameters: {"utf8"=>"✓"}
|
10099
|
+
Completed 200 OK in 2108ms (Views: 285.1ms)
|
10100
|
+
|
10101
|
+
|
10102
|
+
Started GET "/cohortly/reports" for 127.0.0.1 at 2011-10-30 15:10:28 -0400
|
10103
|
+
Processing by Cohortly::ReportsController#index as HTML
|
10104
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/metrics/_groups.html.erb (0.7ms)
|
10105
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/metrics/_tags.html.erb (1147.7ms)
|
10106
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/reports/index.html.erb within layouts/cohortly/application (1179.5ms)
|
10107
|
+
Completed 200 OK in 3007ms (Views: 1184.0ms)
|
10108
|
+
|
10109
|
+
|
10110
|
+
Started GET "/cohortly/reports" for 127.0.0.1 at 2011-10-30 15:11:45 -0400
|
10111
|
+
Processing by Cohortly::ReportsController#index as HTML
|
10112
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/metrics/_groups.html.erb (0.7ms)
|
10113
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/metrics/_tags.html.erb (1181.6ms)
|
10114
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/reports/index.html.erb within layouts/cohortly/application (1188.1ms)
|
10115
|
+
Completed 200 OK in 3150ms (Views: 1193.9ms)
|
10116
|
+
|
10117
|
+
|
10118
|
+
Started GET "/cohortly/reports.js?utf8=%E2%9C%93" for 127.0.0.1 at 2011-10-30 15:11:52 -0400
|
10119
|
+
Processing by Cohortly::ReportsController#index as JS
|
10120
|
+
Parameters: {"utf8"=>"✓"}
|
10121
|
+
Completed 200 OK in 2224ms (Views: 299.9ms)
|
10122
|
+
|
10123
|
+
|
10124
|
+
Started GET "/cohortly/reports" for 127.0.0.1 at 2011-10-30 15:12:48 -0400
|
10125
|
+
Processing by Cohortly::ReportsController#index as HTML
|
10126
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/metrics/_groups.html.erb (0.8ms)
|
10127
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/metrics/_tags.html.erb (1193.8ms)
|
10128
|
+
Rendered /Users/bhauman/workspace/cohortly/app/views/cohortly/reports/index.html.erb within layouts/cohortly/application (1200.4ms)
|
10129
|
+
Completed 200 OK in 3060ms (Views: 1204.8ms)
|
10130
|
+
|
10131
|
+
|
10132
|
+
Started GET "/cohortly/reports.js?utf8=%E2%9C%93" for 127.0.0.1 at 2011-10-30 15:13:09 -0400
|
10133
|
+
Processing by Cohortly::ReportsController#index as JS
|
10134
|
+
Parameters: {"utf8"=>"✓"}
|
10135
|
+
Completed 200 OK in 2218ms (Views: 391.1ms)
|
10136
|
+
|
10137
|
+
|
10138
|
+
Started GET "/" for 127.0.0.1 at 2011-10-30 15:15:58 -0400
|
10139
|
+
Processing by StuffController#index as HTML
|
10140
|
+
Rendered stuff/index.html.erb within layouts/application (1.4ms)
|
10141
|
+
Completed 200 OK in 22ms (Views: 5.5ms)
|
10142
|
+
|
10143
|
+
|
10144
|
+
Started GET "/cohortly/reports.js?utf8=%E2%9C%93" for 127.0.0.1 at 2011-10-30 15:16:02 -0400
|
10145
|
+
Processing by Cohortly::ReportsController#index as JS
|
10146
|
+
Parameters: {"utf8"=>"✓"}
|
10147
|
+
Completed 200 OK in 2273ms (Views: 298.8ms)
|
10148
|
+
DEPRECATION WARNING: ActionController::Routing::Routes is deprecated. Instead, use Rails.application.routes. (called from block in irb_binding at (irb):15)
|
10149
|
+
DEPRECATION WARNING: RAILS_DEFAULT_LOGGER is deprecated. Please use ::Rails.logger. (called from block in irb_binding at (irb):15)
|
10150
|
+
DEPRECATION WARNING: RAILS_ENV is deprecated. Please use ::Rails.env. (called from block in irb_binding at (irb):15)
|
10151
|
+
DEPRECATION WARNING: RAILS_ROOT is deprecated. Please use ::Rails.root.to_s. (called from block in irb_binding at (irb):15)
|
10152
|
+
DEPRECATION WARNING: ActiveSupport::JSON::CircularReferenceError is deprecated! Use ActiveSupport::JSON::Encoding::CircularReferenceError instead. (called from block in irb_binding at (irb):15)
|
10153
|
+
DEPRECATION WARNING: ActiveSupport::JSON::CircularReferenceError is deprecated! Use ActiveSupport::JSON::Encoding::CircularReferenceError instead. (called from block in irb_binding at (irb):15)
|
10154
|
+
DEPRECATION WARNING: ActionController::Routing::Routes is deprecated. Instead, use Rails.application.routes. (called from block in irb_binding at (irb):20)
|
10155
|
+
DEPRECATION WARNING: ActiveSupport::JSON::CircularReferenceError is deprecated! Use ActiveSupport::JSON::Encoding::CircularReferenceError instead. (called from block in irb_binding at (irb):20)
|
10156
|
+
DEPRECATION WARNING: ActiveSupport::JSON::CircularReferenceError is deprecated! Use ActiveSupport::JSON::Encoding::CircularReferenceError instead. (called from block in irb_binding at (irb):20)
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: cohortly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.92
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Bruce Hauman
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-11-02 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -29,11 +29,15 @@ files:
|
|
29
29
|
- app/controllers/cohortly/cohortly_controller.rb
|
30
30
|
- app/controllers/cohortly/metrics_controller.rb
|
31
31
|
- app/controllers/cohortly/reports_controller.rb
|
32
|
+
- app/models/cohortly/cohorts.rb
|
33
|
+
- app/models/cohortly/group_cohort.rb
|
32
34
|
- app/models/cohortly/metric.rb
|
33
|
-
- app/models/cohortly/
|
34
|
-
- app/models/cohortly/report_meta.rb
|
35
|
+
- app/models/cohortly/period_cohort.rb
|
35
36
|
- app/models/cohortly/stored_procedures.rb
|
37
|
+
- app/models/cohortly/tag_report.rb
|
38
|
+
- app/models/cohortly/user_cohort.rb
|
36
39
|
- app/views/cohortly/metrics/_groups.html.erb
|
40
|
+
- app/views/cohortly/metrics/_groups_intersect.html.erb
|
37
41
|
- app/views/cohortly/metrics/_metric.html.erb
|
38
42
|
- app/views/cohortly/metrics/_pagination.html.erb
|
39
43
|
- app/views/cohortly/metrics/_tags.html.erb
|
@@ -58,6 +62,7 @@ files:
|
|
58
62
|
- test/dummy/app/controllers/stuff_controller.rb
|
59
63
|
- test/dummy/app/helpers/application_helper.rb
|
60
64
|
- test/dummy/app/helpers/stuff_helper.rb
|
65
|
+
- test/dummy/app/models/cohortly/cohorts.rb
|
61
66
|
- test/dummy/app/views/layouts/application.html.erb
|
62
67
|
- test/dummy/app/views/stuff/index.html.erb
|
63
68
|
- test/dummy/app/views/stuff/my_stuff.html.erb
|
@@ -97,7 +102,6 @@ files:
|
|
97
102
|
- test/dummy/public/javascripts/vendor/underscore.js
|
98
103
|
- test/dummy/Rakefile
|
99
104
|
- test/dummy/script/rails
|
100
|
-
- test/dummy/tmp/pids/server.pid
|
101
105
|
- test/integration/navigation_test.rb
|
102
106
|
- test/support/integration_case.rb
|
103
107
|
- test/support/test_tag_config.rb
|
@@ -136,6 +140,7 @@ test_files:
|
|
136
140
|
- test/dummy/app/controllers/stuff_controller.rb
|
137
141
|
- test/dummy/app/helpers/application_helper.rb
|
138
142
|
- test/dummy/app/helpers/stuff_helper.rb
|
143
|
+
- test/dummy/app/models/cohortly/cohorts.rb
|
139
144
|
- test/dummy/app/views/layouts/application.html.erb
|
140
145
|
- test/dummy/app/views/stuff/index.html.erb
|
141
146
|
- test/dummy/app/views/stuff/my_stuff.html.erb
|
@@ -175,7 +180,6 @@ test_files:
|
|
175
180
|
- test/dummy/public/javascripts/vendor/underscore.js
|
176
181
|
- test/dummy/Rakefile
|
177
182
|
- test/dummy/script/rails
|
178
|
-
- test/dummy/tmp/pids/server.pid
|
179
183
|
- test/integration/navigation_test.rb
|
180
184
|
- test/support/integration_case.rb
|
181
185
|
- test/support/test_tag_config.rb
|
@@ -1,118 +0,0 @@
|
|
1
|
-
module Cohortly
|
2
|
-
class Report
|
3
|
-
# this is the reduced collection
|
4
|
-
attr_accessor :collection, :weekly, :key_pattern, :groups, :tags, :report_meta
|
5
|
-
def initialize( tags = nil, groups = nil, weekly = true )
|
6
|
-
self.collection = Cohortly::Metric.report_table_name(tags, groups, weekly)
|
7
|
-
self.report_meta = ReportMeta.find_or_create_by_collection_name(self.collection)
|
8
|
-
self.groups = groups
|
9
|
-
self.tags = tags
|
10
|
-
self.weekly = weekly
|
11
|
-
self.key_pattern = self.weekly ? "%Y-%W" : "%Y-%m"
|
12
|
-
end
|
13
|
-
|
14
|
-
def data
|
15
|
-
@data ||= (Cohortly::Metric.database[self.report_meta.store_name].find().collect {|x| x}).sort_by {|x| x['_id'] }
|
16
|
-
end
|
17
|
-
|
18
|
-
def fix_data_lines
|
19
|
-
data.each do |line|
|
20
|
-
period_cohorts_from(line['_id']).collect do |key|
|
21
|
-
if line["value"][key].nil?
|
22
|
-
line["value"][key] = { }
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def offset
|
29
|
-
self.weekly ? 1.week : 1.month
|
30
|
-
end
|
31
|
-
|
32
|
-
def start_key
|
33
|
-
data.first['_id']
|
34
|
-
end
|
35
|
-
|
36
|
-
def end_key
|
37
|
-
time_to_key(Time.now.utc)
|
38
|
-
end
|
39
|
-
|
40
|
-
def time_to_key(time)
|
41
|
-
time.strftime(self.key_pattern)
|
42
|
-
end
|
43
|
-
|
44
|
-
def key_to_time(report_key)
|
45
|
-
DateTime.strptime(report_key, self.key_pattern).to_time.utc
|
46
|
-
end
|
47
|
-
|
48
|
-
def user_count_in_cohort(report_key)
|
49
|
-
params = { :user_start_date => { :$gt => key_to_time(report_key) - 1.week,
|
50
|
-
:$lt => (key_to_time(report_key) )}}
|
51
|
-
params[:tags] = { :$in => groups } if self.groups
|
52
|
-
Cohortly::Metric.collection.distinct(:user_id, params).length
|
53
|
-
end
|
54
|
-
|
55
|
-
def period_cohorts
|
56
|
-
return [] unless data.first
|
57
|
-
start_time = key_to_time(start_key)
|
58
|
-
end_time = key_to_time(end_key)
|
59
|
-
cur_time = start_time
|
60
|
-
res = [start_key]
|
61
|
-
cur_time += self.offset
|
62
|
-
while(cur_time < end_time) do
|
63
|
-
res << time_to_key(cur_time)
|
64
|
-
cur_time += self.offset
|
65
|
-
end
|
66
|
-
res
|
67
|
-
end
|
68
|
-
|
69
|
-
def period_cohorts_from(cohort_key)
|
70
|
-
index = period_cohorts.index(cohort_key)
|
71
|
-
if index
|
72
|
-
period_cohorts[index..-1]
|
73
|
-
else
|
74
|
-
[]
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def report_line(cohort_key)
|
79
|
-
line = data.find {|x| x['_id'] == cohort_key}
|
80
|
-
return [] unless line
|
81
|
-
period_cohorts_from(cohort_key).collect do |key|
|
82
|
-
if line["value"][key]
|
83
|
-
line["value"][key].keys.length
|
84
|
-
else
|
85
|
-
0
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def percent_line(cohort_key)
|
91
|
-
line = report_line(cohort_key)
|
92
|
-
base = user_count_in_cohort(cohort_key)
|
93
|
-
line.collect { |x| (x && base > 0.0 ) ? (x/base.to_f * 100).round : 0 }.unshift base
|
94
|
-
end
|
95
|
-
|
96
|
-
def report_totals
|
97
|
-
period_cohorts.collect do |cohort_key|
|
98
|
-
report_line(cohort_key)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def base_n
|
103
|
-
@base_n ||= self.period_cohorts.inject({ }) { |accum, key| accum[key] = user_count_in_cohort(key); accum }
|
104
|
-
end
|
105
|
-
|
106
|
-
def as_json(*args)
|
107
|
-
fix_data_lines
|
108
|
-
{ :name => report_meta.collection_name,
|
109
|
-
:store_name => report_meta.store_name,
|
110
|
-
:groups => self.groups,
|
111
|
-
:tags => self.tags,
|
112
|
-
:weekly => self.weekly,
|
113
|
-
:data => data,
|
114
|
-
:base_n => base_n
|
115
|
-
}
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Cohortly
|
2
|
-
class ReportMeta
|
3
|
-
include MongoMapper::Document
|
4
|
-
|
5
|
-
key :collection_name, String
|
6
|
-
key :last_update_on, Time
|
7
|
-
|
8
|
-
def store_name
|
9
|
-
"cohortly_report_#{self.id}"
|
10
|
-
end
|
11
|
-
|
12
|
-
def run
|
13
|
-
args = Metric.report_name_to_args(self.collection_name)
|
14
|
-
Cohortly::Metric.cohort_chart(*args)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1 +0,0 @@
|
|
1
|
-
5601
|