mountain-goat 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/README.md +84 -70
  2. data/generators/mg/mg_generator.rb +9 -3
  3. data/generators/mg/templates/create_mountain_goat_tables.rb +4 -0
  4. data/generators/mg/templates/mountain-goat.yml +1 -0
  5. data/lib/mountain-goat.rb +12 -12
  6. data/lib/mountain-goat/controllers/mg/mountain_goat_controller.rb +1 -1
  7. data/lib/mountain-goat/controllers/mg/report_items_controller.rb +5 -5
  8. data/lib/mountain-goat/controllers/mg/reports_controller.rb +28 -1
  9. data/lib/mountain-goat/metric_tracking.rb +4 -4
  10. data/lib/mountain-goat/models/mg/report.rb +4 -4
  11. data/lib/mountain-goat/models/mg/report_item.rb +2 -2
  12. data/lib/mountain-goat/models/mg/report_mailer.rb +7 -0
  13. data/lib/mountain-goat/public/jquery.raphael.js +25 -1
  14. data/lib/mountain-goat/public/mg.css +307 -316
  15. data/lib/mountain-goat/public/mg.js +17 -16
  16. data/lib/mountain-goat/public/mg.png +0 -0
  17. data/lib/mountain-goat/version.rb +1 -1
  18. data/lib/mountain-goat/views/mountain_goat/layouts/mountain_goat.html.erb +21 -20
  19. data/lib/mountain-goat/views/mountain_goat/mg/mountain_goat/login.html.erb +11 -19
  20. data/lib/mountain-goat/views/mountain_goat/mg/report_items/_report_item_form.html.erb +1 -1
  21. data/lib/mountain-goat/views/mountain_goat/mg/report_items/_report_item_pivot_form.html.erb +4 -4
  22. data/lib/mountain-goat/views/mountain_goat/mg/reports/_report_form.html.erb +7 -2
  23. data/lib/mountain-goat/views/mountain_goat/mg/reports/_report_report_items.html.erb +1 -1
  24. data/lib/mountain-goat/views/mountain_goat/mg/reports/edit.html.erb +27 -30
  25. data/lib/mountain-goat/views/mountain_goat/mg/reports/index.html.erb +24 -20
  26. data/lib/mountain-goat/views/mountain_goat/mg/reports/new.html.erb +14 -13
  27. data/lib/mountain-goat/views/mountain_goat/mg/reports/show.html.erb +10 -13
  28. data/mountain-goat.gemspec +2 -2
  29. metadata +31 -54
  30. data/lib/mountain-goat/controllers/mg/metric_variants_controller.rb +0 -81
  31. data/lib/mountain-goat/controllers/mg/metrics_controller.rb +0 -110
  32. data/lib/mountain-goat/controllers/mg/rallies_controller.rb +0 -43
  33. data/lib/mountain-goat/models/mg/ci_meta.rb +0 -10
  34. data/lib/mountain-goat/models/mg/convert.rb +0 -147
  35. data/lib/mountain-goat/models/mg/convert_meta_type.rb +0 -20
  36. data/lib/mountain-goat/models/mg/cs_meta.rb +0 -10
  37. data/lib/mountain-goat/models/mg/metric.rb +0 -8
  38. data/lib/mountain-goat/models/mg/metric_variant.rb +0 -25
  39. data/lib/mountain-goat/models/mg/rally.rb +0 -59
  40. data/lib/mountain-goat/views/mountain_goat/mg/converts/.tmp_show.html.erb.4433~ +0 -69
  41. data/lib/mountain-goat/views/mountain_goat/mg/converts/_convert_form.html.erb +0 -26
  42. data/lib/mountain-goat/views/mountain_goat/mg/converts/_convert_meta_type_form.html.erb +0 -27
  43. data/lib/mountain-goat/views/mountain_goat/mg/converts/edit.html.erb +0 -13
  44. data/lib/mountain-goat/views/mountain_goat/mg/converts/index.html.erb +0 -25
  45. data/lib/mountain-goat/views/mountain_goat/mg/converts/new.html.erb +0 -13
  46. data/lib/mountain-goat/views/mountain_goat/mg/converts/show.html.erb +0 -44
  47. data/lib/mountain-goat/views/mountain_goat/mg/metric_variants/_metric_variant_form.html.erb +0 -37
  48. data/lib/mountain-goat/views/mountain_goat/mg/metric_variants/edit.html.erb +0 -13
  49. data/lib/mountain-goat/views/mountain_goat/mg/metric_variants/index.html.erb +0 -34
  50. data/lib/mountain-goat/views/mountain_goat/mg/metric_variants/new.html.erb +0 -15
  51. data/lib/mountain-goat/views/mountain_goat/mg/metric_variants/show.html.erb +0 -30
  52. data/lib/mountain-goat/views/mountain_goat/mg/metrics/.tmp_show.html.erb.21270~ +0 -68
  53. data/lib/mountain-goat/views/mountain_goat/mg/metrics/_metric_form.html.erb +0 -24
  54. data/lib/mountain-goat/views/mountain_goat/mg/metrics/edit.html.erb +0 -13
  55. data/lib/mountain-goat/views/mountain_goat/mg/metrics/index.html.erb +0 -14
  56. data/lib/mountain-goat/views/mountain_goat/mg/metrics/new.html.erb +0 -14
  57. data/lib/mountain-goat/views/mountain_goat/mg/metrics/show.html.erb +0 -67
  58. data/lib/mountain-goat/views/mountain_goat/mg/rallies/.tmp__rally.html.erb.40484~ +0 -12
  59. data/lib/mountain-goat/views/mountain_goat/mg/rallies/_rallies.html.erb +0 -5
  60. data/lib/mountain-goat/views/mountain_goat/mg/rallies/_rallies_form.html.erb +0 -21
  61. data/lib/mountain-goat/views/mountain_goat/mg/rallies/_rally.html.erb +0 -12
  62. data/lib/mountain-goat/views/mountain_goat/mg/rallies/edit.html.erb +0 -13
  63. data/lib/mountain-goat/views/mountain_goat/mg/rallies/index.html.erb +0 -19
  64. data/lib/mountain-goat/views/mountain_goat/mg/rallies/new.html.erb +0 -13
  65. data/lib/mountain-goat/views/mountain_goat/mg/rallies/show.html.erb +0 -14
@@ -1,81 +0,0 @@
1
-
2
- class Mg::MetricVariantsController < Mg
3
-
4
- # GET /metric_variants
5
- # GET /metric_variants.xml
6
- def index
7
- @metric_variants = MetricVariant.all
8
-
9
- respond_to do |format|
10
- format.html # index.html.erb
11
- format.xml { render :xml => @metric_variants }
12
- end
13
- end
14
-
15
- # GET /metric_variants/1
16
- # GET /metric_variants/1.xml
17
- def show
18
- @metric_variant = MetricVariant.find(params[:id])
19
-
20
- respond_to do |format|
21
- format.html # show.html.erb
22
- format.xml { render :xml => @metric_variant }
23
- end
24
- end
25
-
26
- # GET /metric_variants/new
27
- # GET /metric_variants/new.xml
28
- def new
29
- @metric = Metric.find( params[:metric_id] )
30
- @metric_variant = MetricVariant.new( :metric_id => @metric.id )
31
-
32
- respond_to do |format|
33
- format.html # new.html.erb
34
- format.xml { render :xml => @metric_variant }
35
- end
36
- end
37
-
38
- # GET /metric_variants/1/edit
39
- def edit
40
- @metric_variant = MetricVariant.find(params[:id])
41
- end
42
-
43
- # POST /metric_variants
44
- # POST /metric_variants.xml
45
- def create
46
- @metric = Metric.find( params[:metric_variant][:metric_id] )
47
- @metric_variant = MetricVariant.new(params[:metric_variant])
48
-
49
- if @metric_variant.save
50
- flash[:notice] = 'Metric variant was successfully created.'
51
- redirect_to mg_metric_url :id => @metric_variant.metric.id
52
- else
53
- render :action => "new"
54
- end
55
- end
56
-
57
- # PUT /metric_variants/1
58
- # PUT /metric_variants/1.xml
59
- def update
60
- @metric_variant = MetricVariant.find(params[:id])
61
-
62
- if @metric_variant.update_attributes(params[:metric_variant])
63
- flash[:notice] = 'Metric variant was successfully updated.'
64
- redirect_to mg_metric_url :id => @metric_variant.metric.id
65
- else
66
- render :action => "edit"
67
- end
68
- end
69
-
70
- # DELETE /metric_variants/1
71
- # DELETE /metric_variants/1.xml
72
- def destroy
73
- @metric_variant = MetricVariant.find(params[:id])
74
- @metric_variant.destroy
75
-
76
- respond_to do |format|
77
- format.html { redirect_to mg_metric_variants_url }
78
- format.xml { head :ok }
79
- end
80
- end
81
- end
@@ -1,110 +0,0 @@
1
-
2
- class Mg::MetricsController < Mg
3
-
4
- # GET /metrics
5
- # GET /metrics.xml
6
- def index
7
- @metrics = Metric.all
8
-
9
- respond_to do |format|
10
- format.html # index.html.erb
11
- format.xml { render :xml => @metrics }
12
- end
13
- end
14
-
15
- # GET /metrics/1
16
- # GET /metrics/1.xml
17
- def show
18
- @metric = Metric.find(params[:id])
19
-
20
- @rates = {}
21
- @rates[:served] = []
22
- @rates[:conversions] = []
23
- @rates[:conversion_rates] = []
24
- @rates[:titles] = {}
25
- i = 0
26
- @metric.metric_variants.each do |mv|
27
- @rates[:served].push( { :variant_type => i, :value => mv.served } )
28
- @rates[:conversions].push( { :variant_type => i, :value => mv.conversions } )
29
- @rates[:conversion_rates].push( { :variant_type => i, :value => mv.conversion_rate } )
30
- @rates[:titles].merge!({i => mv.name})
31
- i += 1
32
- end
33
-
34
- logger.warn @rates[:titles].inspect
35
-
36
- respond_to do |format|
37
- format.html # show.html.erb
38
- format.xml { render :xml => @metric }
39
- end
40
- end
41
-
42
- # GET /metrics/new
43
- # GET /metrics/new.xml
44
- def new
45
- @metric = Metric.new
46
-
47
- respond_to do |format|
48
- format.html # new.html.erb
49
- format.xml { render :xml => @metric }
50
- end
51
- end
52
-
53
- # GET /metrics/1/edit
54
- def edit
55
- @metric = Metric.find(params[:id])
56
- end
57
-
58
- # POST /metrics
59
- # POST /metrics.xml
60
- def create
61
- @metric = Metric.new(params[:metric])
62
-
63
- if @metric.save
64
- flash[:notice] = 'Metric was successfully created.'
65
- redirect_to mg_metric_url :id => @metric.id
66
- else
67
- render :action => "new"
68
- end
69
- end
70
-
71
- # PUT /metrics/1
72
- # PUT /metrics/1.xml
73
- def update
74
- @metric = Metric.find(params[:id])
75
-
76
- if @metric.update_attributes(params[:metric])
77
- flash[:notice] = 'Metric was successfully updated.'
78
- redirect_to mg_metric_url :id => @metric.id
79
- else
80
- render :action => "edit"
81
- end
82
- end
83
-
84
- # DELETE /metrics/1
85
- # DELETE /metrics/1.xml
86
- def destroy
87
- @metric = Metric.find(params[:id])
88
- @metric.destroy
89
-
90
- respond_to do |format|
91
- format.html { redirect_to(mg_metrics_url) }
92
- format.xml { head :ok }
93
- end
94
- end
95
-
96
- #TODO: This only works if we are using cookie storage? Clear session as well
97
- def fresh_metrics
98
- #clear metrics
99
- #logger.warn "Headerish #{response.headers['cookie']}"
100
- cookies.each do |cookie|
101
- if cookie[0] =~ /metric_([a-z0-9_]+)/
102
- logger.warn "Deleting cookie #{cookie[0]}"
103
- cookies.delete cookie[0], :domain => WILD_DOMAIN
104
- end
105
- end
106
-
107
- flash[:notice] = "Your metrics have been cleared from cookies."
108
- redirect_to :back
109
- end
110
- end
@@ -1,43 +0,0 @@
1
-
2
- class Mg::RalliesController < Mg
3
-
4
- def index
5
- @page = !params[:page].nil? ? params[:page].to_i : 1
6
- @convert = Convert.find(params[:convert_id]) if !params[:convert_id].nil?
7
-
8
- if @convert
9
- @rallies = @convert.rallies.find(:all, :conditions => { }, :order => "created_at DESC", :limit => 100, :offset => ( @page - 1 ) * 100 )
10
- else
11
- @rallies = Rally.find(:all, :conditions => { }, :order => "created_at DESC", :limit => 100, :offset => ( @page - 1 ) * 100 )
12
- end
13
-
14
- respond_to do |format|
15
- format.html { }
16
- end
17
- end
18
-
19
- def new_rallies
20
- recent_rally = params[:recent_rally].to_i
21
- convert = Convert.find(params[:convert_id].to_i) unless params[:convert_id].blank?
22
-
23
- if convert
24
- @rallies = convert.rallies.find(:all, :conditions => [ 'id > ?', recent_rally ], :order => "id DESC" )
25
- else
26
- @rallies = Rally.find(:all, :conditions => [ 'id > ?', recent_rally ], :order => "id DESC" )
27
- end
28
-
29
- if @rallies.count > 0
30
- render :json => { :success => true,
31
- :result => render_to_string(:partial => 'mg/rallies/rallies', :locals => { :rallies => @rallies } ),
32
- :recent_rally_id => @rallies.first.id }
33
- else
34
- render :json => { :success => false }
35
- end
36
-
37
- end
38
-
39
- def show
40
- @rally = Rally.find(params[:id])
41
- end
42
-
43
- end
@@ -1,10 +0,0 @@
1
- class Mg::CiMeta < ActiveRecord::Base
2
- set_table_name :mg_ci_metas
3
-
4
- belongs_to :convert_meta_type, :class_name => "Mg::ConvertMetaType"
5
- belongs_to :rally, :class_name => "Mg::Rally"
6
-
7
- validates_presence_of :convert_meta_type_id
8
- validates_presence_of :rally_id
9
-
10
- end
@@ -1,147 +0,0 @@
1
- class Mg::Convert < ActiveRecord::Base
2
- set_table_name :mg_converts
3
-
4
- has_many :rallies, :class_name => "Mg::Rally", :foreign_key => "convert_id"
5
- has_many :convert_meta_types, :class_name => "Mg::ConvertMetaType", :foreign_key => "convert_id"
6
- has_many :ci_metas, :through => :convert_meta_types, :class_name => "Mg::CiMeta", :foreign_key => "convert_id"
7
- has_many :cs_metas, :through => :convert_meta_types, :class_name => "Mg::CsMeta", :foreign_key => "convert_id"
8
- has_many :report_items, :as => :reportable, :class_name => "Mg::ReportItem"
9
-
10
- validates_presence_of :name
11
- validates_format_of :convert_type, :with => /[a-z0-9_]{3,50}/i, :message => "must be between 3 and 30 characters, alphanumeric with underscores"
12
- validates_uniqueness_of :convert_type
13
-
14
- accepts_nested_attributes_for :convert_meta_types, :reject_if => lambda { |a| a[:name].blank? || a[:var].blank? || a[:meta_type].blank? }, :allow_destroy => true
15
-
16
- def self.by_type(s)
17
- Mg::Convert.find( :first, :conditions => { :convert_type => s.to_s } )
18
- end
19
-
20
- def rallies_for_meta(var)
21
- cmt = self.convert_meta_types.find_by_var( var.to_s )
22
- return {} if cmt.nil?
23
- cmt.meta.map { |m| { m.data => m.rally } }
24
- end
25
-
26
- def rallies_for_meta_val(var, data)
27
- cmt = self.convert_meta_types.find_by_var( var.to_s )
28
- return [] if cmt.nil?
29
- cmt.meta.find(:all, :conditions => { :data => data } ).map { |m| m.rally }
30
- end
31
-
32
- def rallies_pivot(pivot)
33
- res = {}
34
- cmt_pivot = self.convert_meta_types.find_by_var( pivot.to_s )
35
- return {} if cmt_pivot.nil?
36
- cmt_pivot.meta.map { |c| { :created_at => c.created_at, :pivot => c.data } }.each do |c|
37
- if !res.include?(c[:pivot])
38
- res[c[:pivot]] = []
39
- end
40
-
41
- res[c[:pivot]].push c[:created_at]
42
- end
43
-
44
- res.each { |k,v| v.sort! }
45
- res
46
- end
47
-
48
- def rallies_for_meta_val_pivot(var, data, pivot)
49
- res = {}
50
- cmt = self.convert_meta_types.find_by_var( var.to_s )
51
- cmt_pivot = self.convert_meta_types.find_by_var( pivot.to_s )
52
- return {} if cmt.nil? || cmt_pivot.nil?
53
- cmt.meta.find(:all, :select => "`#{cmt.meta.table_name}`.created_at, cm.data AS pivot", :conditions => { :data => data }, :joins => "LEFT JOIN `#{cmt_pivot.meta.table_name}` cm ON cm.convert_meta_type_id = #{cmt_pivot.id} AND cm.rally_id = `#{cmt.meta.table_name}`.rally_id").each do |c|
54
- if !res.include?(c.pivot)
55
- res[c.pivot] = []
56
- end
57
-
58
- res[c.pivot].push c.created_at
59
- end
60
-
61
- res.each { |k,v| v.sort! }
62
- res
63
- end
64
-
65
- def rallies_for_meta_val_pivot_item(var, data, pivot, item)
66
- res = {}
67
- cmt = self.convert_meta_types.find_by_var( var.to_s )
68
- cmt_pivot = self.convert_meta_types.find_by_var( pivot.to_s )
69
- cmt_item = self.convert_meta_types.find_by_var( item.to_s )
70
- return {} if cmt.nil? || cmt_pivot.nil? || cmt_item.nil?
71
- cmt.meta.find(:all, :select => "`#{cmt.meta.table_name}`.created_at, cm.data AS pivot, ci.data as item", :conditions => { :data => data }, :joins => "LEFT JOIN `#{cmt_pivot.meta.table_name}` cm ON cm.convert_meta_type_id = #{cmt_pivot.id} AND cm.rally_id = `#{cmt.meta.table_name}`.rally_id LEFT JOIN `#{cmt_item.meta.table_name}` ci ON ci.convert_meta_type_id = #{cmt_item.id} AND ci.rally_id = `#{cmt.meta.table_name}`.rally_id").each do |c|
72
- if !res.include?(c.pivot)
73
- res[c.pivot] = []
74
- end
75
-
76
- if cmt_item.meta_type == 'ci_meta'
77
- c.item.to_i.times { res[c.pivot].push c.created_at }
78
- else
79
- res[c.pivot].push c.created_at if c.item == 'yes' #what else?
80
- end
81
- end
82
-
83
- res.each { |k,v| v.sort! }
84
- res.delete_if { |k,v| v.count == 0 }
85
- res
86
- end
87
-
88
- #For Reportable
89
-
90
- def reportable_title(pivot)
91
- if pivot.nil?
92
- return self.name
93
- elsif pivot.instance_of?(Mg::ConvertMetaType)
94
- return "#{self.name} by #{pivot.name}"
95
- end
96
- end
97
-
98
- def reportable_chart_items(pivot)
99
- #let's look for pageviews by day by source
100
- #rallies_for_meta_val( :clique_id, @clique.id )
101
- #logger.debug "sources: #{sources.inspect}"
102
- #We now have a map of { source => [ date1, date2, ... ], ... }
103
-
104
- #Now, we just need to insert missing data (and group)
105
- #Let's transpose that into { source => [ :x => day0, y => count ] }
106
- if pivot.nil?
107
- return Analytics.pivot_by_date( { :"Rallies" => self.rallies.map { |r| r.created_at } }, self.created_at )
108
- elsif pivot.instance_of?(Mg::ConvertMetaType)
109
- sources = self.rallies_pivot( pivot.var )
110
- logger.warn "sources: #{sources}"
111
- return Analytics.pivot_by_date(sources, self.created_at)
112
- end
113
- end
114
-
115
- def reportable_gerbil_chart(pivot)
116
- #chart = GerbilCharts::Charts::LineChart.new( :width => 350, :height => 200, :style => 'brushmetal.css', :circle_data_points => true )
117
-
118
- if pivot.nil?
119
- data = Analytics.pivot_by_date( { :"Rallies" => self.rallies.map { |r| r.created_at } }, self.created_at )
120
- elsif pivot.instance_of?(Mg::ConvertMetaType)
121
- sources = self.rallies_pivot( pivot.var )
122
- data = Analytics.pivot_by_date(sources, self.created_at)
123
- end
124
- #logger.warn "data: #{data.inspect}"
125
- #logger.warn "ts: #{data.map { |line| line[1].map { |d| d[:x] }.to_a }[0].inspect}"
126
- #logger.warn "val: #{data.map { |line| [ line[0] ] + line[1].map { |d| d[:y] }.to_a }[0].inspect}"
127
- #chart.modelgroup = GerbilCharts::Models::SimpleTimeSeriesModelGroup.new(
128
- # :title => "Rallies",
129
- # :timeseries => data.map { |line| line[1].map { |d| d[:x].to_time }.to_a }[0],
130
- # :models => data.map { |line| [ line[0].to_s ] + line[1].map { |d| d[:y] }.to_a }
131
- #)
132
- #fields = { }
133
- #fields_simple = []
134
- #fields.merge!({ d[:x].to_time.to_i => d[:x].to_s } ); fields_simple.push(d[:x])
135
- graph = SVG::Graph::TimeSeries.new( :height => 350, :width => 700, :show_data_labels => false, :x_label_format => "%m/%d/%y", :graph_title => self.reportable_title(pivot), :show_graph_title => true, :show_data_values => false, :show_data_points => false, :area_fill => true )
136
- data.each do |line|
137
- res = []
138
- line[1].each { |d| res.push(d[:x].strftime('%m/%d/%y')).push(d[:y]) }
139
- graph.add_data :data => res, :title => line[0].to_s
140
- end
141
-
142
- #logger.warn "Fields: #{fields.inspect}"
143
- #logger.warn "Fields Simple: #{fields_simple.inspect}"
144
-
145
- graph
146
- end
147
- end
@@ -1,20 +0,0 @@
1
- class Mg::ConvertMetaType < ActiveRecord::Base
2
- set_table_name :mg_convert_meta_types
3
-
4
- belongs_to :convert, :class_name => "Mg::Convert"
5
- has_many :ci_metas, :dependent => :destroy, :class_name => "Mg::CiMeta", :foreign_key => "convert_meta_type_id"
6
- has_many :cs_metas, :dependent => :destroy, :class_name => "Mg::CsMeta", :foreign_key => "convert_meta_type_id"
7
-
8
- validates_presence_of :name
9
- validates_presence_of :var
10
- validates_presence_of :meta_type
11
-
12
- def meta
13
- case self.meta_type
14
- when 'ci_meta'
15
- return self.ci_metas
16
- when 'cs_meta'
17
- return self.cs_metas
18
- end
19
- end
20
- end
@@ -1,10 +0,0 @@
1
- class Mg::CsMeta < ActiveRecord::Base
2
- set_table_name :mg_cs_metas
3
-
4
- belongs_to :convert_meta_type, :class_name => "Mg::ConvertMetaType"
5
- belongs_to :rally, :class_name => "Mg::Rally"
6
-
7
- validates_presence_of :convert_meta_type_id
8
- validates_presence_of :rally_id
9
-
10
- end
@@ -1,8 +0,0 @@
1
- class Mg::Metric < ActiveRecord::Base
2
- set_table_name :mg_metrics
3
-
4
- has_many :metric_variants, :class_name => "Mg::MetricVariant", :foreign_key => "metric_id"
5
-
6
- validates_format_of :metric_type, :with => /[a-z0-9_]{3,50}/i, :message => "must be between 3 and 30 characters, alphanumeric with underscores"
7
- validates_uniqueness_of :metric_type
8
- end
@@ -1,25 +0,0 @@
1
- class Mg::MetricVariant < ActiveRecord::Base
2
- set_table_name :mg_metric_variants
3
-
4
- belongs_to :metric, :class_name => "Mg::Metric"
5
-
6
- validates_presence_of :name
7
- validates_presence_of :metric_id
8
-
9
- def tally_serve
10
- self.update_attribute(:reward, 0) if self.reward.nil? #we should merge this with the next line, but whatever
11
- Mg::MetricVariant.update_counters(self.id, :served => 1)
12
- self.reload
13
- end
14
-
15
- #reward has a "default" or adjustable setting
16
- def tally_convert(convert, reward = nil)
17
- Mg::MetricVariant.update_counters(self.id, :conversions => 1, :reward => reward.nil? ? convert.reward : reward)
18
- self.reload
19
- end
20
-
21
- def conversion_rate
22
- return nil if self.served == 0
23
- return self.conversions / self.served.to_f * 100
24
- end
25
- end