cohortly 0.0.9.1 → 0.0.92

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,25 +1,58 @@
1
1
  class Cohortly::ReportsController < Cohortly::CohortlyController
2
2
  def index
3
3
  Cohortly::Metric.send :attr_accessor, :groups
4
- @metric_search = Cohortly::Metric.new(params[:cohortly_metric])
5
- tags = @metric_search.tags.any? ? @metric_search.tags : nil
6
- groups = @metric_search.groups
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
- @report_name = Cohortly::Metric.report_table_name(tags, groups, true)
9
- # run this in background would be better
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
- if Cohortly::Metric.respond_to? :delay
12
- Cohortly::Metric.delay.weekly_cohort_chart_for_tag(tags, groups)
13
- else
14
- Cohortly::Metric.weekly_cohort_chart_for_tag(tags, groups)
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
- @report = Cohortly::Report.new( tags, groups, true )
17
-
49
+
18
50
  respond_to do |format|
19
51
  format.html
20
52
  format.js {
21
- render :json => @report
53
+ render :json => json_res
22
54
  }
23
55
  end
24
56
  end
57
+
25
58
  end
@@ -0,0 +1,8 @@
1
+ module Cohortly
2
+ class Cohorts
3
+ def self.cohort_names; end
4
+ def self.first_user_start_date(group_name = nil); end
5
+ def self.range(time_range); end
6
+ def self.group(group_name); end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module Cohortly
2
+ class GroupCohort < Cohortly::UserCohort
3
+ def store!
4
+ return unless self.name
5
+ self.user_ids = Cohortly::Cohorts.group_name(self.name)
6
+ self.save
7
+ end
8
+ end
9
+ end
@@ -51,7 +51,7 @@ module Cohortly
51
51
  :raw => true,
52
52
  :query => query})
53
53
  end
54
-
54
+
55
55
  def self.cohort_chart_for_tag(tags = nil, groups = nil)
56
56
  self.cohort_chart(tags, groups, false)
57
57
  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::TagConfig.all_groups.sort.each_slice(3) do |groups| %>
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
- <% (Cohortly::Metric.collection.distinct(:tags) - Cohortly::TagConfig.all_groups).sort.each_slice(8) do |tags| %>
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
- this.model.get('groups') ? 'Groups: ' + this.model.get('groups').join(', ') + ' | ': 'Groups: __ | ',
37
+ this.model.get('groups') ? 'Groups: ' + this.model.get('groups').join(', ') + ' | ': 'Groups: __ | ',
38
38
  '',
39
- this.model.get('tags') ? 'Tags: ' + this.model.get('tags').join(', ') : 'Tags: __ ',
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')).map(this.render_row).join('');
60
+ return _(_(this.model.get('data')).keys()).sortBy(function(x)
61
+ { return x }).map(this.render_row).join('');
59
62
  },
60
- render_row: function(row) {
61
- var row_key = row['_id'];
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>' + row['_id']+ '</td>',
66
- '<td>' + base_n + '</td>',
67
- this.render_cells(row, base_n),
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(row, base_n) {
72
- return _(_(row.value).keys()).sortBy(function(x){return x}).slice(0,14).map(function(key) {
73
- var num_users = _(row.value[key]).keys().length;
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() {
@@ -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::ReportMeta.connection Cohortly::Metric.connection
13
- Cohortly::ReportMeta.set_database_name cfg.database
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
@@ -1,3 +1,3 @@
1
1
  module Cohortly
2
- VERSION = "0.0.9.1"
2
+ VERSION = "0.0.92"
3
3
  end
@@ -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 :reseed_reports => :environment do
5
- Cohortly::ReportMeta.delete_all
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::Metric.cohort_chart([tag], nil, true)
11
- puts "tag: #{tag}"
12
- end
13
- Cohortly::TagConfig.all_groups.each do |group|
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::ReportMeta.all.each do |rep|
24
- puts rep.collection_name
25
- rep.run
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.9.1
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-10-27 00:00:00 -07:00
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/report.rb
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