best_boy 1.3.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/README.md +36 -3
  2. data/Rakefile +45 -7
  3. data/app/controllers/best_boy/best_boy_events_controller.rb +147 -98
  4. data/app/helpers/best_boy/best_boy_view_helper.rb +9 -0
  5. data/app/views/best_boy/best_boy_events/details.html.erb +33 -27
  6. data/app/views/best_boy/best_boy_events/monthly_details.html.erb +9 -9
  7. data/app/views/best_boy/best_boy_events/stats.html.erb +20 -19
  8. data/lib/best_boy/engine.rb +2 -0
  9. data/lib/best_boy/models/active_record/best_boy/eventable.rb +19 -0
  10. data/lib/best_boy/models/active_record/best_boy_day_report.rb +65 -0
  11. data/lib/best_boy/models/active_record/best_boy_event.rb +1 -11
  12. data/lib/best_boy/models/active_record/best_boy_month_report.rb +63 -0
  13. data/lib/best_boy/version.rb +1 -1
  14. data/lib/generators/active_record/best_boy_generator.rb +4 -1
  15. data/lib/generators/active_record/templates/create_best_boy_reports.rb +30 -0
  16. data/lib/tasks/recover_report_history.rake +146 -0
  17. data/spec/{best_boy → controllers}/best_boy_controller_spec.rb +6 -4
  18. data/spec/dummy/app/views/test_events/index.html.haml +9 -1
  19. data/spec/dummy/config/routes.rb +1 -0
  20. data/spec/dummy/db/migrate/20131108085915_create_best_boy_reports.rb +30 -0
  21. data/spec/dummy/db/schema.rb +27 -1
  22. data/spec/dummy/db/test.sqlite3 +0 -0
  23. data/spec/dummy/log/development.log +10092 -0
  24. data/spec/dummy/log/test.log +531 -0
  25. data/spec/dummy/tmp/cache/assets/development/sass/f99cb4f6f36f128b2d6950c813eec72c66616bfc/bootstrap.scssc +0 -0
  26. data/spec/dummy/tmp/cache/assets/development/sprockets/01af51e20498d87feb18694bed0d0731 +0 -0
  27. data/spec/dummy/tmp/cache/assets/development/sprockets/057daf732c672b9e0ec8bd233ec79077 +0 -0
  28. data/spec/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  29. data/spec/dummy/tmp/cache/assets/development/sprockets/1565c81151c1e0fe577f787a645bca3d +0 -0
  30. data/spec/dummy/tmp/cache/assets/development/sprockets/1c90620581903652712500bb92915909 +0 -0
  31. data/spec/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  32. data/spec/dummy/tmp/cache/assets/development/sprockets/3241270f308313221453f51980c07883 +0 -0
  33. data/spec/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  34. data/spec/dummy/tmp/cache/assets/development/sprockets/3a59d6acf2f4543a36d946d653b08dab +0 -0
  35. data/spec/dummy/tmp/cache/assets/development/sprockets/3a5f8aba9c98833836df55cdc5502ee8 +0 -0
  36. data/spec/dummy/tmp/cache/assets/development/sprockets/42804a4f90448633879c06a33e49f4e6 +0 -0
  37. data/spec/dummy/tmp/cache/assets/development/sprockets/48279ff6b6f65f85e766c3a7ae0c71f6 +0 -0
  38. data/spec/dummy/tmp/cache/assets/development/sprockets/642b8ba757f92502a3a3281a956eec2a +0 -0
  39. data/spec/dummy/tmp/cache/assets/development/sprockets/64fe0e7bbf53d3cf292e85e123137bcb +0 -0
  40. data/spec/dummy/tmp/cache/assets/development/sprockets/77e01a708457c1d4aac9446e173321a1 +0 -0
  41. data/spec/dummy/tmp/cache/assets/development/sprockets/8ac87e607a25fc208fc1da0c60b24b8b +0 -0
  42. data/spec/dummy/tmp/cache/assets/development/sprockets/8dab559778ce584628d484b9d919cfa8 +0 -0
  43. data/spec/dummy/tmp/cache/assets/development/sprockets/8fde9b9595ed926214e7858402849bb2 +0 -0
  44. data/spec/dummy/tmp/cache/assets/development/sprockets/c9e7400c0b4cf9165368acf909971237 +0 -0
  45. data/spec/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  46. data/spec/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  47. data/spec/dummy/tmp/cache/assets/development/sprockets/e58e48ebcd3216893f3b053ee6a3d7eb +0 -0
  48. data/spec/dummy/tmp/cache/assets/development/sprockets/e5cfb1a438298274f827c0cbea06e7dc +0 -0
  49. data/spec/dummy/tmp/cache/assets/development/sprockets/f14dc22e71f0438b9fc154ec08da7c10 +0 -0
  50. data/spec/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  51. data/spec/models/best_boy_event_spec.rb +20 -0
  52. data/spec/models/day_report_spec.rb +115 -0
  53. data/spec/models/eventable_spec.rb +51 -0
  54. data/spec/models/month_report_spec.rb +113 -0
  55. data/spec/spec_helper.rb +96 -40
  56. metadata +110 -123
  57. data/.gitignore +0 -10
  58. data/.rspec +0 -1
  59. data/.travis.yml +0 -3
  60. data/Gemfile +0 -3
  61. data/LICENSE.txt +0 -20
  62. data/best_boy.gemspec +0 -38
  63. data/db/bestboy.db +0 -0
  64. data/spec/best_boy/best_boy_event_spec.rb +0 -62
  65. data/spec/best_boy/eventable_spec.rb +0 -20
  66. data/spec/dummy/app/mailers/.gitkeep +0 -0
  67. data/spec/dummy/app/models/.gitkeep +0 -0
  68. data/spec/dummy/db/development.sqlite3 +0 -0
  69. data/spec/dummy/lib/assets/.gitkeep +0 -0
data/README.md CHANGED
@@ -59,6 +59,12 @@ Run the migration
59
59
  rake db:migrate
60
60
 
61
61
 
62
+ Update in Version 2
63
+ --------------------------------
64
+
65
+ From Version 2.x on BestBoy uses aggregated tables for the admin panel. See section "Some thoughts about Performance"
66
+
67
+
62
68
  Update to fit the asset-pipeline
63
69
  --------------------------------
64
70
 
@@ -120,6 +126,33 @@ After installation you can access it through:
120
126
 
121
127
  <your application path>/best_boy_admin
122
128
 
129
+ Some thoughts about Performance
130
+ -------------------------------
131
+
132
+ When you're running BestBoy for a long time, accessing and processing data in
133
+ the backend can take awfully long.
134
+
135
+ By changing to version 1.4.0, we modified BestBoy's data storage organization to
136
+ reduce database queries. This leads to far more acceptable loading times. To do
137
+ so, BestBoy creates daily and monthly reports, which are aggregations
138
+ of the persisted events. When computing statistical data, BestBoy
139
+ then uses the aggregated tables.
140
+
141
+ If you're upgrading BestBoy from an older version, there
142
+ is a rake task for 'recovering' these report structure for
143
+ an existing set of events. Simply run
144
+
145
+ bundle exec rake best_boy:recover_report_history
146
+
147
+ If you want to recover this report structure not for the whole lifetime,
148
+ you can pass a date as argument to the rake task call:
149
+
150
+ bundle exec rake best_boy:recover_report_history['2010-02-01']
151
+
152
+ The latter would destroy and recover the all reports created after
153
+ beginning of Feb 1st, 2010 up to now.
154
+
155
+ Budget some time for this task, since it can take long if your BestBoyEvent table has grown very big.
123
156
 
124
157
  Used gems and resource
125
158
  ----------------------
@@ -127,14 +160,14 @@ Used gems and resource
127
160
 
128
161
  [Stefan Petre](http://www.eyecon.ro/bootstrap-datepicker) for Datepicker in Twitter Bootstrap style
129
162
 
130
- [Winston Teo Yong Wei](https://github.com/winston/google_visualr) Google_Visulr in its version 2.1.2
163
+ [Winston Teo Yong Wei](https://github.com/winston/google_visualr) Google_Visualr in its version 2.1.2
131
164
 
132
165
 
133
166
  Contributors in alphabetic order
134
167
  --------------------------------
135
168
  @cseydel
136
169
  @danscho
137
-
170
+ @rneumann
138
171
 
139
172
  Thanks
140
173
  ------
@@ -142,4 +175,4 @@ We are extremely grateful to everyone contributing to this project.
142
175
  Big thanks to each contributor on Impressionist. This gem helped me a long way to get here in modelling and creating a gem.
143
176
 
144
177
 
145
- See LICENSE.txt for details on licenses.
178
+ See LICENSE.txt for details on licenses.
data/Rakefile CHANGED
@@ -1,11 +1,49 @@
1
- require 'rubygems'
2
- require 'bundler/setup'
3
- require 'rake'
4
- require 'rspec/core'
5
- require 'rspec/core/rake_task'
1
+ # require 'rubygems'
2
+ # require 'bundler/setup'
3
+ # require 'rake'
4
+ # require 'rspec/core'
5
+ # require 'rspec/core/rake_task'
6
+
7
+ # Bundler::GemHelper.install_tasks
8
+ # RSpec::Core::RakeTask.new(:spec)
9
+ # task :default => :spec
10
+
11
+
12
+
13
+ #!/usr/bin/env rake
14
+ begin
15
+ require 'bundler/setup'
16
+ rescue LoadError
17
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
18
+ end
19
+ begin
20
+ require 'rdoc/task'
21
+ rescue LoadError
22
+ require 'rdoc/rdoc'
23
+ require 'rake/rdoctask'
24
+ RDoc::Task = Rake::RDocTask
25
+ end
26
+
27
+ RDoc::Task.new(:rdoc) do |rdoc|
28
+ rdoc.rdoc_dir = 'rdoc'
29
+ rdoc.title = 'BestBoy'
30
+ rdoc.options << '--line-numbers'
31
+ rdoc.rdoc_files.include('README.rdoc')
32
+ rdoc.rdoc_files.include('lib/**/*.rb')
33
+ end
34
+
35
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
36
+ load 'rails/tasks/engine.rake'
37
+
38
+
6
39
 
7
40
  Bundler::GemHelper.install_tasks
8
41
 
9
- RSpec::Core::RakeTask.new(:spec)
42
+ # We use rspec
43
+ require 'rspec/core'
44
+ require 'rspec/core/rake_task'
45
+
46
+ desc "Run all specs in spec directory (excluding plugin specs)"
47
+ RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
10
48
 
11
- task :default => :spec
49
+ task :default => :spec
@@ -1,136 +1,180 @@
1
1
  module BestBoy
2
2
  class BestBoyEventsController < BestBoy.base_controller.constantize
3
3
 
4
- before_filter BestBoy.before_filter if BestBoy.before_filter.present?
5
- before_filter :prepare_chart, :only => [:charts]
4
+ before_action BestBoy.before_filter if BestBoy.before_filter.present?
5
+
6
6
  skip_before_filter BestBoy.skip_before_filter if BestBoy.skip_before_filter.present?
7
7
  skip_after_filter BestBoy.skip_after_filter if BestBoy.skip_after_filter.present?
8
8
 
9
9
  layout 'best_boy_backend'
10
10
 
11
- helper_method :available_owner_types, :available_events, :available_event_sources, :available_years,
12
- :current_owner_type, :current_event, :current_event_source, :current_month, :current_year, :collection,
13
- :render_chart, :month_name_array, :detail_count
14
11
 
12
+ helper BestBoy::BestBoyViewHelper
13
+ helper_method :available_owner_types, :available_events, :available_event_sources, :available_event_sources?, :available_years,
14
+ :current_owner_type, :current_event, :current_event_source, :current_month, :current_year, :collection,
15
+ :days_of, :render_chart, :month_name_array, :detail_count
15
16
 
16
17
  def stats
17
- counter_scope = BestBoyEvent.select("COUNT(*) as counter, event").where(:owner_type => current_owner_type).group('event')
18
-
19
- # Custom hash for current event stats - current_year, current_month, current_week, current_day (with given current_owner_type)
20
- # We fire 5 database queries, one for each group, to keep it database acnostic.
21
- # Before we had 5 * n events queries
22
- @event_counts_per_group = {}
23
- overall_hash = counter_scope.inject({}){ |hash, element| hash[element.event] = element.counter; hash}
24
- current_year_hash = counter_scope.per_year(Time.zone.now).inject({}){ |hash, element| hash[element.event] = element.counter; hash }
25
- current_month_hash = counter_scope.per_month(Time.zone.now).inject({}){ |hash, element| hash[element.event] = element.counter; hash }
26
- current_week_hash = counter_scope.per_week(Time.zone.now).inject({}){ |hash, element| hash[element.event] = element.counter; hash }
27
- current_day_hash = counter_scope.per_day(Time.zone.now).inject({}){ |hash, element| hash[element.event] = element.counter; hash }
18
+ collect_occurrences
19
+ collect_occurrences_for_selected_year
20
+ available_events_totals
21
+ selected_year_totals
22
+ end
28
23
 
29
- available_events.each do |event|
30
- @event_counts_per_group[event] ||= {}
31
- @event_counts_per_group[event]['overall'] = overall_hash[event] || 0
32
- @event_counts_per_group[event]['year'] = current_year_hash[event] || 0
33
- @event_counts_per_group[event]['month'] = current_month_hash[event] || 0
34
- @event_counts_per_group[event]['week'] = current_week_hash[event] || 0
35
- @event_counts_per_group[event]['day'] = current_day_hash[event] || 0
36
- end
24
+ def details
25
+ collect_occurrences
26
+ collect_occurrences_grouped_by_sources
27
+ collect_occurrences_for_selected_year_of current_event
28
+ selected_year_totals
29
+ end
37
30
 
38
- # Custom hash for current event stats per month (with given current_owner_type)
39
- # We fire 12 database queries, one for each month, to keep it database acnostic.
40
- # Before we had 12 * n events queries
41
- @event_counts_per_month = {}
42
- %w(1 2 3 4 5 6 7 8 9 10 11 12).each do |month|
43
- month_hash = counter_scope.per_month("1-#{month}-#{current_year}".to_time).inject({}){ |hash, element| hash[element.event] = element.counter; hash}
31
+ def monthly_details
32
+ collect_occurrences_for_month current_month
33
+ monthly_details_chart
34
+ end
44
35
 
45
- available_events.each do |event|
46
- @event_counts_per_month[event] ||= {}
47
- @event_counts_per_month[event][month] = month_hash[event] || 0
48
- end
49
- end
36
+ def charts
37
+ build_chart
50
38
  end
51
39
 
40
+ private
52
41
 
53
- def details
54
- counter_scope = BestBoyEvent.select("COUNT(*) as counter, event_source").where(owner_type: current_owner_type, event: current_event).group('event_source')
55
-
56
- # Custom hash for current event_source stats - current_year, current_month, current_week, current_day (with given current_owner_type)
57
- # We fire 5 database queries, one for each group, to keep it database acnostic.
58
- # Before we had 5 * n event_sources queries
59
- @event_source_counts_per_group = {}
60
- overall_hash = counter_scope.inject({}){ |hash, element| hash[element.event_source] = element.counter; hash}
61
- current_year_hash = counter_scope.per_year(Time.zone.now).inject({}){ |hash, element| hash[element.event_source] = element.counter; hash}
62
- current_month_hash = counter_scope.per_month(Time.zone.now).inject({}){ |hash, element| hash[element.event_source] = element.counter; hash}
63
- current_week_hash = counter_scope.per_week(Time.zone.now).inject({}){ |hash, element| hash[element.event_source] = element.counter; hash}
64
- current_day_hash = counter_scope.per_day(Time.zone.now).inject({}){ |hash, element| hash[element.event_source] = element.counter; hash}
65
-
66
- available_event_sources.each do |event_source|
67
- @event_source_counts_per_group[event_source] ||= {}
68
- @event_source_counts_per_group[event_source]['overall'] = overall_hash[event_source] || 0
69
- @event_source_counts_per_group[event_source]['year'] = current_year_hash[event_source] || 0
70
- @event_source_counts_per_group[event_source]['month'] = current_month_hash[event_source] || 0
71
- @event_source_counts_per_group[event_source]['week'] = current_week_hash[event_source] || 0
72
- @event_source_counts_per_group[event_source]['day'] = current_day_hash[event_source] || 0
73
- end
42
+ def collect_event_occurrences(event)
43
+ {
44
+ event => {
45
+ :daily => BestBoy::DayReport.daily_occurrences_for(current_owner_type, event, nil, Time.zone.now),
46
+ :weekly => BestBoy::DayReport.weekly_occurrences_for(current_owner_type, event),
47
+ :monthly => BestBoy::MonthReport.monthly_occurrences_for(current_owner_type, event, nil, Time.zone.now),
48
+ :yearly => BestBoy::MonthReport.yearly_occurrences_for(current_owner_type, event, nil, Time.zone.now),
49
+ :overall => BestBoy::MonthReport.overall_occurrences_for(current_owner_type, event)
50
+ }
51
+ }
52
+ end
53
+
54
+ def collect_occurrences
55
+ @occurrences = {}
56
+ available_events.each { |event| @occurrences.merge! collect_event_occurrences(event) }
57
+ end
58
+
59
+ def collect_source_occurrences(source)
60
+ {
61
+ source => {
62
+ :daily => BestBoy::DayReport.daily_occurrences_for(current_owner_type, current_event, source, Time.zone.now),
63
+ :weekly => BestBoy::DayReport.weekly_occurrences_for(current_owner_type, current_event, source),
64
+ :monthly => BestBoy::MonthReport.monthly_occurrences_for(current_owner_type, current_event, source, Time.zone.now),
65
+ :yearly => BestBoy::MonthReport.yearly_occurrences_for(current_owner_type, current_event, source, Time.zone.now),
66
+ :overall => BestBoy::MonthReport.overall_occurrences_for(current_owner_type, current_event, source)
67
+ }
68
+ }
69
+ end
74
70
 
75
- # Custom hash for current event_sources stats per month (with given current_owner_type and given event)
76
- # We fire 12 database queries, one for each month, to keep it database acnostic.
77
- # Before we had 12 * n event_sources queries
78
- @event_sources_counts_per_month = {}
79
- %w(1 2 3 4 5 6 7 8 9 10 11 12).each do |month|
80
- month_hash = counter_scope.per_month("1-#{month}-#{current_year}".to_time).inject({}){ |hash, element| hash[element.event_source] = element.counter; hash}
71
+ def collect_occurrences_grouped_by_sources
72
+ @sourced_occurrences = {}
73
+ available_event_sources.each { |source| @sourced_occurrences.merge! collect_source_occurrences(source) }
74
+ end
81
75
 
82
- available_event_sources.each do |event_source|
83
- @event_sources_counts_per_month[event_source] ||= {}
84
- @event_sources_counts_per_month[event_source][month] = month_hash[event_source] || 0
76
+ def collect_occurrences_for_month(month)
77
+ @selected_month_occurrences = {}
78
+ if available_event_sources?
79
+ available_event_sources.each do |source|
80
+ days_of(month).each do |day|
81
+ @selected_month_occurrences.merge!({source => {day => BestBoy::DayReport.daily_occurrences_for(current_owner_type, current_event, source, day)}}) { |k, v1, v2| v1.merge!(v2) }
82
+ end
85
83
  end
86
84
  end
85
+ days_of(month).each do |day|
86
+ @selected_month_occurrences.merge!({"All" => {day => BestBoy::DayReport.daily_occurrences_for(current_owner_type, current_event, nil, day)}}) { |k, v1, v2| v1.merge!(v2) }
87
+ end
87
88
  end
88
89
 
89
-
90
- def monthly_details
91
- counter_scope = BestBoyEvent.select("COUNT(*) as counter, event_source").where(owner_type: current_owner_type, event: current_event).group('event_source')
92
- days = 1..(Time.days_in_month("1-#{current_month}-#{current_year}".to_time.month))
93
- # Custom hash for current event_sources stats per month (with given current_owner_type and given event)
94
- # We fire a max of 31 database queries, one for each day, to keep it database acnostic.
95
- # Before we had 31 * n event_sources queries
96
- @event_sources_counts_per_day = {}
97
- days.each do |day|
98
- day_hash = counter_scope.per_day("#{day}-#{current_month}-#{current_year}".to_time).inject({}){ |hash, element| hash[element.event_source] = element.counter; hash}
99
-
100
- available_event_sources.each do |event_source|
101
- @event_sources_counts_per_day[event_source] ||= {}
102
- @event_sources_counts_per_day[event_source][day] = day_hash[event_source] || 0
90
+ def collect_occurrences_for_selected_year
91
+ @selected_year_occurrences = {}
92
+ available_events.each do |event|
93
+ @selected_year_occurrences.merge!({event => {}})
94
+ (1..12).each do |month|
95
+ date = Date.parse("#{current_year}-#{month}-1")
96
+ @selected_year_occurrences[event].merge!({month.to_s => BestBoy::MonthReport.monthly_occurrences_for(current_owner_type, event, nil, date)})
103
97
  end
104
98
  end
99
+ end
105
100
 
106
- data_table = GoogleVisualr::DataTable.new
107
- data_table.new_column('string', 'time')
108
- available_event_sources.each do |source|
109
- data_table.new_column('number', source.to_s)
101
+ def collect_occurrences_for_selected_year_of(event)
102
+ @event_selected_year_occurrences = {}
103
+ sources = available_event_sources.to_a + ["All"]
104
+
105
+ sources.each do |source|
106
+ @event_selected_year_occurrences.merge!({source => {}})
107
+ (1..12).each do |month|
108
+ date = Date.parse("#{current_year}-#{month}-1")
109
+ query_source = source == "All" ? nil : source
110
+ branch = {month.to_s => BestBoy::MonthReport.monthly_occurrences_for(current_owner_type, event, query_source, date)}
111
+ @event_selected_year_occurrences[source].merge!(branch)
112
+ end
110
113
  end
114
+ end
111
115
 
112
- days.each do |day|
113
- time = "#{day}-#{current_month}-#{current_year}".to_time
114
- data_table.add_row( [ day.to_s] + available_event_sources.map{ |event_source| @event_sources_counts_per_day[event_source][day].to_i })
116
+ def available_events_totals
117
+ @this_year_totals = { :daily => 0, :weekly => 0, :monthly => 0, :yearly => 0, :overall => 0 }
118
+ available_events.each do |event|
119
+ @this_year_totals.keys.each do |time_period|
120
+ @this_year_totals[time_period] += @occurrences[event][time_period]
121
+ end
115
122
  end
116
- @chart = GoogleVisualr::Interactive::AreaChart.new(data_table, { width: 900, height: 240, title: "" })
117
123
  end
118
124
 
125
+ def selected_year_totals
126
+ @selected_year_totals = {}
127
+ # OPTIMIZE ME:
128
+ # Query below could be replaced with a GROUP_BY or similar
129
+ # SQL-statement for performance boost, i.e. similar to
130
+ # BestBoy::MonthReport.select("SUM(occurances) AS counter, DATE_PART('month', created_at) as month").where(:owner_type => current_owner_type, created_at: current_year.beginning_of_year..current_year.end_of_year).group("DATE_PART('month', created_at)")
131
+ (1..12).each do |month|
132
+ date = Date.parse("#{current_year}-#{month}-1")
133
+ @selected_year_totals.merge!({
134
+ month => BestBoy::MonthReport
135
+ .where(owner_type: current_owner_type, event_source: nil)
136
+ .between(date.beginning_of_month, date.end_of_month).sum(:occurrences)
137
+ })
138
+ end
139
+ end
119
140
 
120
- private
141
+ def days_of(month)
142
+ reference = Date.parse("#{current_year}#{month}-1")
143
+ (reference.beginning_of_month..reference.end_of_month)
144
+ end
121
145
 
122
146
  def render_chart(chart, dom)
123
147
  chart.to_js(dom).html_safe
124
148
  end
125
149
 
126
- def prepare_chart
150
+ def chart_for(data)
151
+ @chart = GoogleVisualr::Interactive::AreaChart.new(data, { width: 900, height: 240, title: "" })
152
+ end
153
+
154
+ def row_values_for(day)
155
+ available_event_sources.collect{ |event_source| @selected_month_occurrences[event_source][day] } + [@selected_month_occurrences["All"][day]]
156
+ end
157
+
158
+ def monthly_details_chart
159
+ data_table = GoogleVisualr::DataTable.new
160
+ data_table.new_column('string', 'time')
161
+
162
+ labels = available_event_sources.to_a + ["All"]
163
+ labels.each { |label| data_table.new_column('number', label.to_s) }
164
+
165
+ days_of(params[:month]).each { |day| data_table.add_row( [day.strftime("%d")] + row_values_for(day) ) }
166
+ chart_for data_table
167
+ end
168
+
169
+ def build_chart
127
170
  data_table = GoogleVisualr::DataTable.new
128
171
  data_table.new_column('string', 'time')
129
172
  data_table.new_column('number', current_owner_type.to_s)
173
+
130
174
  time_periode_range.each do |periode|
131
175
  data_table.add_row([chart_legend_time_name(periode), custom_data_count(current_event_source, calculated_point_in_time(periode))])
132
176
  end
133
- @chart = GoogleVisualr::Interactive::AreaChart.new(data_table, { width: 900, height: 240, title: "" })
177
+ chart_for data_table
134
178
  end
135
179
 
136
180
  def week_name_array
@@ -142,11 +186,12 @@ module BestBoy
142
186
  end
143
187
 
144
188
  def custom_data_count(source, time)
145
- scope = BestBoyEvent.where(owner_type: current_owner_type)
189
+ scope = %("week", "month").include?(current_time_interval) ? BestBoy::DayReport.created_on(time) : BestBoy::MonthReport.between(time.beginning_of_month, time.end_of_month)
190
+ scope = scope.where(owner_type: current_owner_type)
146
191
  scope = scope.where(event: current_event) if current_event.present?
147
192
  scope = scope.where(event_source: source) if source.present?
148
- scope = scope.send("per_#{ current_time_interval == "year" ? "month" : "day" }", time)
149
- scope.count
193
+ scope = scope.where(event_source: nil) if source.nil?
194
+ scope.sum(:occurrences)
150
195
  end
151
196
 
152
197
  def time_periode_range
@@ -211,21 +256,25 @@ module BestBoy
211
256
  end
212
257
 
213
258
  def available_events
214
- @available_events ||= BestBoyEvent.select('DISTINCT event').where(owner_type: current_owner_type).order(:event).map(&:event)
259
+ @available_events ||= BestBoy::MonthReport.where(owner_type: current_owner_type).order(:event).uniq.pluck(:event)
215
260
  end
216
261
 
217
262
  def available_event_sources
218
263
  @available_event_sources ||= (
219
- BestBoyEvent.select("DISTINCT event_source").where(owner_type: current_owner_type, event: current_event).order(:event_source).map(&:event_source)
264
+ BestBoy::MonthReport.where(owner_type: current_owner_type).where('event_source IS NOT NULL').order(:event_source).uniq.pluck(:event_source)
220
265
  )
221
266
  end
222
267
 
268
+ def available_event_sources?
269
+ available_event_sources.first.present?
270
+ end
271
+
223
272
  def available_years
224
- @available_years = (BestBoyEvent.where(owner_type: current_owner_type).order(:created_at).first.created_at.to_date.year..Time.zone.now.year).map{ |year| year.to_s } rescue [Time.zone.now.year]
273
+ @available_years = (BestBoy::MonthReport.where(owner_type: current_owner_type, event_source: nil).order(:created_at).first.created_at.to_date.year..Time.zone.now.year).map{ |year| year.to_s } rescue [Time.zone.now.year]
225
274
  end
226
275
 
227
276
  def available_owner_types
228
- @available_owner_types ||= BestBoyEvent.select("DISTINCT owner_type").order(:owner_type).map(&:owner_type)
277
+ @available_owner_types ||= BestBoy::MonthReport.where(event_source: nil).order(:owner_type).uniq.pluck(:owner_type)
229
278
  end
230
279
 
231
280
  def detail_count
@@ -254,4 +303,4 @@ module BestBoy
254
303
  end
255
304
 
256
305
  end
257
- end
306
+ end
@@ -0,0 +1,9 @@
1
+ module BestBoy
2
+ module BestBoyViewHelper
3
+ def relative_occurrences(hash, key)
4
+ val = hash[key].to_f
5
+ val = val / hash[:overall].to_f * 100 if hash[:overall].to_i > 0
6
+ number_to_percentage(val, precision: 2)
7
+ end
8
+ end
9
+ end