best_boy 1.3.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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