active_reporter 0.6.1 → 0.6.5
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.
- checksums.yaml +4 -4
- data/Rakefile +9 -9
- data/lib/active_reporter/aggregator/array.rb +1 -1
- data/lib/active_reporter/aggregator/count.rb +2 -2
- data/lib/active_reporter/aggregator/count_if.rb +2 -2
- data/lib/active_reporter/aggregator/ratio.rb +1 -1
- data/lib/active_reporter/aggregator.rb +9 -9
- data/lib/active_reporter/calculator.rb +2 -2
- data/lib/active_reporter/dimension/base.rb +3 -3
- data/lib/active_reporter/dimension/bin/set.rb +3 -3
- data/lib/active_reporter/dimension/bin/table.rb +1 -1
- data/lib/active_reporter/dimension/bin.rb +23 -15
- data/lib/active_reporter/dimension/category.rb +1 -1
- data/lib/active_reporter/dimension/enum.rb +1 -1
- data/lib/active_reporter/dimension/number.rb +3 -3
- data/lib/active_reporter/dimension/time.rb +4 -4
- data/lib/active_reporter/dimension.rb +8 -8
- data/lib/active_reporter/evaluator.rb +2 -2
- data/lib/active_reporter/inflector.rb +1 -1
- data/lib/active_reporter/report/aggregation.rb +12 -12
- data/lib/active_reporter/report/definition.rb +6 -6
- data/lib/active_reporter/report/validation.rb +26 -15
- data/lib/active_reporter/report.rb +3 -3
- data/lib/active_reporter/serializer/base.rb +6 -6
- data/lib/active_reporter/serializer/csv.rb +2 -2
- data/lib/active_reporter/serializer/form_field.rb +5 -5
- data/lib/active_reporter/serializer/highcharts.rb +6 -6
- data/lib/active_reporter/serializer.rb +7 -7
- data/lib/active_reporter/tracker/base.rb +1 -1
- data/lib/active_reporter/tracker.rb +3 -3
- data/lib/active_reporter/version.rb +1 -1
- data/lib/active_reporter.rb +7 -3
- data/spec/acceptance/data_spec.rb +49 -49
- data/spec/active_reporter/aggregator_spec.rb +37 -37
- data/spec/active_reporter/dimension/base_spec.rb +29 -29
- data/spec/active_reporter/dimension/bin/set_spec.rb +29 -29
- data/spec/active_reporter/dimension/bin/table_spec.rb +7 -7
- data/spec/active_reporter/dimension/bin_spec.rb +12 -12
- data/spec/active_reporter/dimension/category_spec.rb +22 -22
- data/spec/active_reporter/dimension/enum_spec.rb +12 -12
- data/spec/active_reporter/dimension/number_spec.rb +11 -11
- data/spec/active_reporter/dimension/time_spec.rb +20 -20
- data/spec/active_reporter/report_spec.rb +162 -162
- data/spec/active_reporter/serializer/hash_table_spec.rb +13 -13
- data/spec/active_reporter/serializer/highcharts_spec.rb +30 -30
- data/spec/active_reporter/serializer/table_spec.rb +22 -22
- data/spec/dummy/Rakefile +1 -1
- data/spec/dummy/app/models/post_report.rb +1 -1
- data/spec/dummy/app/views/layouts/application.html.erb +2 -2
- data/spec/dummy/app/views/site/report.html.erb +5 -5
- data/spec/dummy/bin/bundle +2 -2
- data/spec/dummy/bin/rails +3 -3
- data/spec/dummy/bin/rake +2 -2
- data/spec/dummy/bin/setup +2 -2
- data/spec/dummy/config/application.rb +4 -4
- data/spec/dummy/config/boot.rb +3 -3
- data/spec/dummy/config/database.yml +3 -3
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/environments/production.rb +4 -4
- data/spec/dummy/config/environments/test.rb +1 -1
- data/spec/dummy/config/initializers/assets.rb +1 -1
- data/spec/dummy/config/initializers/inflections.rb +4 -4
- data/spec/dummy/config/initializers/session_store.rb +1 -1
- data/spec/dummy/config/locales/en.yml +2 -2
- data/spec/dummy/config/routes.rb +9 -9
- data/spec/dummy/config.ru +1 -1
- data/spec/dummy/log/test.log +54769 -77257
- data/spec/spec_helper.rb +13 -13
- metadata +64 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 723ed733317c2a8e7a9671f7df71388c7df0b8019824ef83a8455d1fc2358a26
|
4
|
+
data.tar.gz: 6897c073647fc646151861295592048449e80803ba000332a63b6a026942d770
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1d8eb39876e5f564fdf8b3522b9e1a6739052311b57f525baa4ac827fc0a3507a515c355a23f0b8d1e6280976a9f17dd654e80fb16e39278ceb82fed6fdb2d78
|
7
|
+
data.tar.gz: 89b76c59b8ef242536ed4758c7ff036fa35425703c57a56f1d212a72b642a5f5d24d919e2bc16c4907e93cac6e74e24d8500ca6f1d6ef2502c381cb4ab111fb0
|
data/Rakefile
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
begin
|
2
|
-
require
|
2
|
+
require "bundler/setup"
|
3
3
|
rescue LoadError
|
4
|
-
puts
|
4
|
+
puts "You must `gem install bundler` and `bundle install` to run rake tasks"
|
5
5
|
end
|
6
6
|
|
7
|
-
require
|
7
|
+
require "rdoc/task"
|
8
8
|
|
9
9
|
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
-
rdoc.rdoc_dir =
|
11
|
-
rdoc.title =
|
12
|
-
rdoc.options <<
|
13
|
-
rdoc.rdoc_files.include(
|
14
|
-
rdoc.rdoc_files.include(
|
10
|
+
rdoc.rdoc_dir = "rdoc"
|
11
|
+
rdoc.title = "ActiveReporter"
|
12
|
+
rdoc.options << "--line-numbers"
|
13
|
+
rdoc.rdoc_files.include("README.rdoc")
|
14
|
+
rdoc.rdoc_files.include("lib/**/*.rb")
|
15
15
|
end
|
16
16
|
|
17
17
|
Bundler::GemHelper.install_tasks
|
18
18
|
|
19
|
-
require
|
19
|
+
require "rspec/core/rake_task"
|
20
20
|
|
21
21
|
RSpec::Core::RakeTask.new(:spec)
|
22
22
|
|
@@ -2,7 +2,7 @@ module ActiveReporter
|
|
2
2
|
module Aggregator
|
3
3
|
class Array < ActiveReporter::Aggregator::Base
|
4
4
|
def aggregate(groups)
|
5
|
-
fail InvalidParamsError,
|
5
|
+
fail InvalidParamsError, "array aggregator is only supported in Postgres" unless ActiveReporter.database_type == :postgres
|
6
6
|
super
|
7
7
|
end
|
8
8
|
|
@@ -2,7 +2,7 @@ module ActiveReporter
|
|
2
2
|
module Aggregator
|
3
3
|
class Count < ActiveReporter::Aggregator::Base
|
4
4
|
def function
|
5
|
-
"COUNT(#{
|
5
|
+
"COUNT(#{"DISTINCT" if distinct} #{expression})"
|
6
6
|
end
|
7
7
|
|
8
8
|
def default_value
|
@@ -16,7 +16,7 @@ module ActiveReporter
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def column
|
19
|
-
opts.fetch(:column,
|
19
|
+
opts.fetch(:column, "id")
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -2,7 +2,7 @@ module ActiveReporter
|
|
2
2
|
module Aggregator
|
3
3
|
class CountIf < ActiveReporter::Aggregator::Count
|
4
4
|
def function
|
5
|
-
"COUNT(#{expression} IN (#{values.map(&:to_s).join(
|
5
|
+
"COUNT(#{expression} IN (#{values.map(&:to_s).join(",")}) OR NULL)"
|
6
6
|
end
|
7
7
|
|
8
8
|
def default_value
|
@@ -16,7 +16,7 @@ module ActiveReporter
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def column
|
19
|
-
super ||
|
19
|
+
super || "id"
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -1,9 +1,9 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
1
|
+
require "active_reporter/aggregator/base"
|
2
|
+
require "active_reporter/aggregator/array"
|
3
|
+
require "active_reporter/aggregator/average"
|
4
|
+
require "active_reporter/aggregator/count"
|
5
|
+
require "active_reporter/aggregator/count_if"
|
6
|
+
require "active_reporter/aggregator/max"
|
7
|
+
require "active_reporter/aggregator/min"
|
8
|
+
require "active_reporter/aggregator/ratio"
|
9
|
+
require "active_reporter/aggregator/sum"
|
@@ -1,2 +1,2 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "active_reporter/calculator/base"
|
2
|
+
require "active_reporter/calculator/ratio"
|
@@ -71,7 +71,7 @@ module ActiveReporter
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def order(relation)
|
74
|
-
relation.order("#{order_expression} #{sort_order} #{null_order}")
|
74
|
+
relation.order(Arel.sql("#{order_expression} #{sort_order} #{null_order}"))
|
75
75
|
end
|
76
76
|
|
77
77
|
def sort_desc?
|
@@ -79,7 +79,7 @@ module ActiveReporter
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def sort_order
|
82
|
-
sort_desc? ?
|
82
|
+
sort_desc? ? "DESC" : "ASC"
|
83
83
|
end
|
84
84
|
|
85
85
|
def nulls_last?
|
@@ -90,7 +90,7 @@ module ActiveReporter
|
|
90
90
|
|
91
91
|
def null_order
|
92
92
|
return unless ActiveReporter.database_type == :postgres
|
93
|
-
nulls_last? ?
|
93
|
+
nulls_last? ? "NULLS LAST" : "NULLS FIRST"
|
94
94
|
end
|
95
95
|
|
96
96
|
def params
|
@@ -21,7 +21,7 @@ module ActiveReporter
|
|
21
21
|
when /^([^,]+),(.+)$/ then new($1, $2)
|
22
22
|
when /^([^,]+),$/ then new($1, nil)
|
23
23
|
when /^,(.+)$/ then new(nil, $1)
|
24
|
-
when
|
24
|
+
when ",", nil then new(nil, nil)
|
25
25
|
else
|
26
26
|
raise "Unexpected SQL bin format #{value}"
|
27
27
|
end
|
@@ -104,8 +104,8 @@ module ActiveReporter
|
|
104
104
|
|
105
105
|
def [](key)
|
106
106
|
case key.to_s
|
107
|
-
when
|
108
|
-
when
|
107
|
+
when "min" then min
|
108
|
+
when "max" then max
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "active_reporter/dimension/base"
|
2
2
|
|
3
3
|
module ActiveReporter
|
4
4
|
module Dimension
|
@@ -9,15 +9,34 @@ module ActiveReporter
|
|
9
9
|
self.class::MAX_BINS
|
10
10
|
end
|
11
11
|
|
12
|
+
# report values are greater than or equal to min, grouped by bin_width
|
12
13
|
def min
|
13
14
|
@min ||= filter_min || report.records.minimum(expression)
|
14
15
|
end
|
15
16
|
alias bin_start min
|
16
17
|
|
18
|
+
# report values are less than max, grouped by bin_width
|
17
19
|
def max
|
18
20
|
@max ||= filter_max || report.records.maximum(expression)
|
19
21
|
end
|
20
22
|
|
23
|
+
def bin_end
|
24
|
+
@bin_end ||= if max.blank? || min.blank? || min > max
|
25
|
+
nil
|
26
|
+
else
|
27
|
+
bin_edge = bin_start + bin_width
|
28
|
+
|
29
|
+
loop do
|
30
|
+
break if bin_edge >= max
|
31
|
+
bin_edge += bin_width
|
32
|
+
end
|
33
|
+
|
34
|
+
bin_edge += bin_width unless filter_values_for(:max).present? # # # figure out why we need this??
|
35
|
+
|
36
|
+
bin_edge
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
21
40
|
def filter_min
|
22
41
|
filter_values_for(:min).min
|
23
42
|
end
|
@@ -95,23 +114,12 @@ module ActiveReporter
|
|
95
114
|
end
|
96
115
|
|
97
116
|
def autopopulate_bins
|
98
|
-
return [] if bin_start.blank? ||
|
99
|
-
|
100
|
-
bin_max = filter_values_for(:max).present? ? (max - bin_width) : max
|
117
|
+
return [] if bin_start.blank? || bin_end.blank?
|
101
118
|
|
102
|
-
bin_count = (
|
119
|
+
bin_count = ((bin_end - bin_start)/(bin_width)).to_i
|
103
120
|
invalid_param!(:bin_width, "is too small for the domain; would generate #{bin_count.to_i} bins") if bin_count > max_bins
|
104
121
|
|
105
|
-
|
106
|
-
bins = []
|
107
|
-
|
108
|
-
loop do
|
109
|
-
break if bin_edge > bin_max
|
110
|
-
|
111
|
-
bin = { min: bin_edge, max: bin_edge + bin_width }
|
112
|
-
bins << bin
|
113
|
-
bin_edge = bin[:max]
|
114
|
-
end
|
122
|
+
bins = bin_count.times.map { |i| { min: (bin_start + (bin_width*i)), max: (bin_start + (bin_width*i.next)) } }
|
115
123
|
|
116
124
|
bins.reverse! if sort_desc?
|
117
125
|
( nulls_last? ? bins.push(nil) : bins.unshift(nil) ) if data_contains_nil?
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "active_reporter/dimension/bin"
|
2
2
|
|
3
3
|
module ActiveReporter
|
4
4
|
module Dimension
|
@@ -9,8 +9,8 @@ module ActiveReporter
|
|
9
9
|
super
|
10
10
|
|
11
11
|
if params.key?(:bin_width)
|
12
|
-
invalid_param!(:bin_width,
|
13
|
-
invalid_param!(:bin_width,
|
12
|
+
invalid_param!(:bin_width, "must be numeric") unless ActiveReporter.numeric?(params[:bin_width])
|
13
|
+
invalid_param!(:bin_width, "must be greater than 0") unless params[:bin_width].to_f > 0
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -1,12 +1,12 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "active_reporter/inflector"
|
2
|
+
require "active_reporter/dimension/bin"
|
3
3
|
|
4
4
|
module ActiveReporter
|
5
5
|
module Dimension
|
6
6
|
class Time < Bin
|
7
7
|
STEPS = %i(seconds minutes hours days weeks months years)
|
8
8
|
BIN_STEPS = (STEPS - [:seconds]).map { |step| step.to_s.singularize(:_gem_active_reporter) }
|
9
|
-
DURATION_PATTERN = /\A\d+ (?:#{STEPS.map{ |step| "#{step}?" }.join(
|
9
|
+
DURATION_PATTERN = /\A\d+ (?:#{STEPS.map{ |step| "#{step}?" }.join("|")})\z/
|
10
10
|
|
11
11
|
def validate_params!
|
12
12
|
super
|
@@ -78,7 +78,7 @@ module ActiveReporter
|
|
78
78
|
|
79
79
|
class Set < Bin::Set
|
80
80
|
def parse(value)
|
81
|
-
::Time.zone.parse(value.to_s.gsub('"',
|
81
|
+
::Time.zone.parse(value.to_s.gsub('"', ""))
|
82
82
|
end
|
83
83
|
|
84
84
|
def cast(value)
|
@@ -1,8 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
1
|
+
require "active_reporter/dimension/base"
|
2
|
+
require "active_reporter/dimension/bin"
|
3
|
+
require "active_reporter/dimension/bin/set"
|
4
|
+
require "active_reporter/dimension/bin/table"
|
5
|
+
require "active_reporter/dimension/time"
|
6
|
+
require "active_reporter/dimension/number"
|
7
|
+
require "active_reporter/dimension/category"
|
8
|
+
require "active_reporter/dimension/enum"
|
@@ -1,2 +1,2 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "active_reporter/evaluator/base"
|
2
|
+
require "active_reporter/evaluator/block"
|
@@ -29,7 +29,6 @@ module ActiveReporter
|
|
29
29
|
|
30
30
|
def source_data
|
31
31
|
@source_data ||= aggregators.values.reduce(groups) do |relation, aggregator|
|
32
|
-
# append each aggregator into the base relation (groups)
|
33
32
|
relation.merge(aggregator.aggregate(base_relation))
|
34
33
|
end
|
35
34
|
end
|
@@ -39,12 +38,11 @@ module ActiveReporter
|
|
39
38
|
def aggregate
|
40
39
|
tracker_dimension_key = :_tracker_dimension
|
41
40
|
|
42
|
-
if trackable? && trackers.any?
|
41
|
+
if trackable? && trackers.any? && prior_bin_report.source_data.present?
|
43
42
|
prior_obj = prior_bin_report.source_data.first
|
44
43
|
prior_row = prior_bin_report.hashed_data.first.with_indifferent_access
|
45
44
|
|
46
|
-
|
47
|
-
prior_row[tracker_dimension_key] = results_key_prefix[0..-2]
|
45
|
+
prior_row[tracker_dimension_key] = groupers.without(tracker_dimension).map { |g| g.extract_sql_value(prior_obj) }
|
48
46
|
else
|
49
47
|
prior_obj = nil
|
50
48
|
prior_row = {}
|
@@ -87,7 +85,7 @@ module ActiveReporter
|
|
87
85
|
# "author.id" value (bin) changes the tracker is reset so we do not track changes from the last day of each
|
88
86
|
# "author.id" to the first day of the next "author.id".
|
89
87
|
if trackable?
|
90
|
-
current_row[tracker_dimension_key] =
|
88
|
+
current_row[tracker_dimension_key] = groupers.without(tracker_dimension).map { |g| g.extract_sql_value(current_obj) }
|
91
89
|
|
92
90
|
if current_row[tracker_dimension_key] == prior_row[tracker_dimension_key] && bins_are_adjacent?(current_obj, prior_obj)
|
93
91
|
trackers.each do |name, tracker|
|
@@ -199,11 +197,11 @@ module ActiveReporter
|
|
199
197
|
|
200
198
|
results.deep_merge!(results.collect do |row, value|
|
201
199
|
calculators.collect do |name, calculator|
|
202
|
-
row_data = hash_raw_row(row, value, [
|
200
|
+
row_data = hash_raw_row(row, value, ["totals"])
|
203
201
|
calc_report = parent_report.total_report
|
204
202
|
|
205
203
|
parent_row = match_parent_row_for_calculator(row_data, calc_report, calculator)
|
206
|
-
[[
|
204
|
+
[["totals", name.to_s], calculator.calculate(row_data, parent_row)] unless parent_row.nil?
|
207
205
|
end
|
208
206
|
end.flatten(1).to_h) unless parent_report.nil?
|
209
207
|
|
@@ -241,9 +239,9 @@ module ActiveReporter
|
|
241
239
|
# tracker? Even if there is a "correct" method for one report it may not be correct for a different report. The
|
242
240
|
# same problem applies to strings. Which character is after "z"? The ASCII hex value is "{", which would work
|
243
241
|
# fine for ordering, but maybe not for determining when a tracker should be reset. Additionally, we need to
|
244
|
-
# deal with strings of different lengths. Alphabetically you could order
|
245
|
-
# when to reset the tracker? If we get a new value of
|
246
|
-
# tracker value for the
|
242
|
+
# deal with strings of different lengths. Alphabetically you could order "A", "AA", "AAA", "B" but how do know
|
243
|
+
# when to reset the tracker? If we get a new value of "AAAA" we have entirelly new values used to calculate the
|
244
|
+
# tracker value for the "B" row, effectivally making the tracker values irrelevent.
|
247
245
|
# Even going back to the integer example, the value allowed to be stored increments by 1, but there is no
|
248
246
|
# guerentee that these are the actual values being used in the field.
|
249
247
|
# For these reasons we will not attempt to track any dimension that does not specifically specify a bin width.
|
@@ -270,7 +268,7 @@ module ActiveReporter
|
|
270
268
|
end
|
271
269
|
|
272
270
|
def trackable?
|
273
|
-
@trackable ||= tracker_dimension
|
271
|
+
@trackable ||= tracker_dimension&.min.present?
|
274
272
|
end
|
275
273
|
|
276
274
|
def evaluatable?
|
@@ -278,7 +276,9 @@ module ActiveReporter
|
|
278
276
|
end
|
279
277
|
|
280
278
|
def tracker_dimension
|
281
|
-
@tracker_dimension ||= groupers.
|
279
|
+
@tracker_dimension ||= (groupers.reverse + dimensions.values).detect do |dimension|
|
280
|
+
dimension.is_a?(ActiveReporter::Dimension::Bin) && %i[min max bin_width].all? { |a| dimension.try(a).present? }
|
281
|
+
end
|
282
282
|
end
|
283
283
|
|
284
284
|
def prior_bin_report
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "active_reporter/inflector"
|
2
2
|
|
3
3
|
module ActiveReporter
|
4
4
|
class Report
|
@@ -6,7 +6,7 @@ module ActiveReporter
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
METRICS = %i[aggregator calculator dimension tracker evaluator].collect do |type|
|
9
|
-
metrics = Dir.glob(File.join(__dir__,
|
9
|
+
metrics = Dir.glob(File.join(__dir__, "..", type.to_s, "*.rb")).collect { |file| File.basename(file, ".rb") }.without(*%w[base bin]).collect(&:to_sym).sort.freeze
|
10
10
|
[type, const_set(type.to_s.upcase, metrics)]
|
11
11
|
end.to_h.sort.freeze
|
12
12
|
|
@@ -102,7 +102,7 @@ module ActiveReporter
|
|
102
102
|
|
103
103
|
# block_evaluator(:chargeback_ratio) { |row| supplemental_report_data.detect { |data| data[:id] == row[:id] }[:count] / row[:count] }
|
104
104
|
def evaluator(name, evaluator_class, opts = {})
|
105
|
-
raise
|
105
|
+
raise "needs block" unless opts.include?(:block)
|
106
106
|
evaluators[name.to_sym] = { axis_class: evaluator_class, opts: opts }
|
107
107
|
end
|
108
108
|
|
@@ -124,20 +124,20 @@ module ActiveReporter
|
|
124
124
|
class_eval <<-METRIC_HELPERS, __FILE__, __LINE__ + 1
|
125
125
|
def #{mertic}_#{type}(name, opts = {}, &block)
|
126
126
|
opts[:block] = block if block_given?
|
127
|
-
#{type}(name, #{(type.to_s +
|
127
|
+
#{type}(name, #{(type.to_s + "/" + mertic.to_s.singularize(:_gem_active_reporter)).camelize.sub(/.*\./, "")}, opts)
|
128
128
|
end
|
129
129
|
METRIC_HELPERS
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
133
133
|
def default_report_model
|
134
|
-
name.demodulize.sub(/Report$/,
|
134
|
+
name.demodulize.sub(/Report$/, "").constantize
|
135
135
|
rescue NameError
|
136
136
|
raise $!, "#{$!} cannot be used as `report_on` class, please configure `report_on` in the report class", $!.backtrace
|
137
137
|
end
|
138
138
|
|
139
139
|
def default_model
|
140
|
-
name.demodulize.sub(/Report$/,
|
140
|
+
name.demodulize.sub(/Report$/, "").constantize
|
141
141
|
end
|
142
142
|
|
143
143
|
def report_model
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "active_reporter/inflector"
|
2
|
+
require "active_reporter/invalid_params_error"
|
3
3
|
|
4
4
|
module ActiveReporter
|
5
5
|
class Report
|
@@ -19,11 +19,11 @@ module ActiveReporter
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def validate_configuration!
|
22
|
-
incomplete_message = [
|
22
|
+
incomplete_message = ["You must declare at least one aggregator or tracker, and at lease one dimension to initialize a report", "See the README for more details"]
|
23
23
|
|
24
24
|
raise ActiveReporter::InvalidParamsError, ["#{self.class.name} does not declare any aggregators or trackers"].concat(incomplete_message).join(". ") if aggregators.empty?
|
25
25
|
raise ActiveReporter::InvalidParamsError, ["#{self.class.name} does not declare any dimensions"].concat(incomplete_message).join(". ") if dimensions.except(:totals).empty?
|
26
|
-
raise ActiveReporter::InvalidParamsError,
|
26
|
+
raise ActiveReporter::InvalidParamsError, "parent_report must be included in order to process calculations" if calculators.any? && parent_report.nil?
|
27
27
|
end
|
28
28
|
|
29
29
|
def validate_aggregators!
|
@@ -38,10 +38,13 @@ module ActiveReporter
|
|
38
38
|
end
|
39
39
|
|
40
40
|
calculators.values.each do |calculator|
|
41
|
-
|
41
|
+
case
|
42
|
+
when calculator.aggregator.nil?
|
42
43
|
add_invalid_param_error(:calculator, ":#{calculator.name} must define an aggregator (should be in #{self.class.aggregator.keys})")
|
43
|
-
|
44
|
+
when self.class.aggregators.exclude?(calculator.aggregator)
|
44
45
|
add_invalid_param_error(:calculator, ":#{calculator.name} defines an invalid aggregator :#{calculator.aggregator} (should be in #{self.class.aggregators.keys})")
|
46
|
+
when params.include?(:aggregators) && aggregators.exclude?(calculator.aggregator)
|
47
|
+
params[:aggregators].push(calculator.aggregator)
|
45
48
|
end
|
46
49
|
end
|
47
50
|
end
|
@@ -52,14 +55,22 @@ module ActiveReporter
|
|
52
55
|
end
|
53
56
|
|
54
57
|
trackers.values.each do |tracker|
|
55
|
-
|
58
|
+
case
|
59
|
+
when tracker.aggregator.nil?
|
56
60
|
add_invalid_param_error(:tracker, ":#{tracker.name} must define an aggregator (should be in #{self.class.aggregator.keys})")
|
57
|
-
|
61
|
+
when self.class.aggregators.exclude?(tracker.aggregator)
|
58
62
|
add_invalid_param_error(:tracker, ":#{tracker.name} defines an invalid aggregator :#{tracker.aggregator} (should be in #{self.class.aggregators.keys})")
|
63
|
+
when params.include?(:aggregators) && aggregators.exclude?(tracker.aggregator)
|
64
|
+
params[:aggregators].push(tracker.aggregator)
|
59
65
|
end
|
60
66
|
|
61
|
-
if tracker.
|
62
|
-
|
67
|
+
if tracker.opts.include?(:prior_aggregator)
|
68
|
+
case
|
69
|
+
when self.class.aggregators.exclude?(tracker.prior_aggregator)
|
70
|
+
add_invalid_param_error(:tracker, ":#{tracker.name} defines an invalid prior aggregator :#{tracker.prior_aggregator} (should be in #{self.class.aggregators.keys})")
|
71
|
+
when params.include?(:aggregators) && aggregators.exclude?(tracker.prior_aggregator)
|
72
|
+
params[:aggregators].push(tracker.prior_aggregator)
|
73
|
+
end
|
63
74
|
end
|
64
75
|
end
|
65
76
|
end
|
@@ -70,8 +81,8 @@ module ActiveReporter
|
|
70
81
|
invalid_groupers_message = [
|
71
82
|
[
|
72
83
|
invalid_groupers.to_sentence,
|
73
|
-
(invalid_groupers.one? ?
|
74
|
-
].join(
|
84
|
+
(invalid_groupers.one? ? "is not a" : "are not"), "valid", "dimension".pluralize(invalid_groupers.count, :_gem_active_reporter)
|
85
|
+
].join(" "),
|
75
86
|
"declared dimension include #{dimensions.keys.to_sentence}"
|
76
87
|
].join(". ")
|
77
88
|
add_invalid_param_error(:groupers, invalid_groupers_message)
|
@@ -79,11 +90,11 @@ module ActiveReporter
|
|
79
90
|
end
|
80
91
|
|
81
92
|
def validate_parent_report!
|
82
|
-
add_invalid_param_error(:parent_report,
|
93
|
+
add_invalid_param_error(:parent_report, "must be an instance of ActiveReporter::Report") unless parent_report.nil? || parent_report.kind_of?(ActiveReporter::Report)
|
83
94
|
end
|
84
95
|
|
85
96
|
def validate_total_report!
|
86
|
-
add_invalid_param_error(:total_report,
|
97
|
+
add_invalid_param_error(:total_report, "must be an instance of ActiveReporter::Report") unless @total_report.nil? || @total_report.kind_of?(ActiveReporter::Report)
|
87
98
|
end
|
88
99
|
|
89
100
|
private
|
@@ -103,7 +114,7 @@ module ActiveReporter
|
|
103
114
|
end
|
104
115
|
|
105
116
|
def error_message
|
106
|
-
(["The report configuration contains the following #{
|
117
|
+
(["The report configuration contains the following #{"error".pluralize(errors.count, :_gem_active_reporter)}:"] + errors).join("\n - ")
|
107
118
|
end
|
108
119
|
end
|
109
120
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
Dir.glob(File.join(__dir__,
|
1
|
+
Dir.glob(File.join(__dir__, "report", "*.rb")).each { |file| require file }
|
2
2
|
|
3
3
|
module ActiveReporter
|
4
4
|
class Report
|
@@ -18,8 +18,8 @@ module ActiveReporter
|
|
18
18
|
# When using a Calculator you may need the parent report data. Pass in a ActiveReporter::Report object when
|
19
19
|
# instantiating a new ActiveReporter::Report instance as :parent_report. This will allow you to calculate a data
|
20
20
|
# based on the #total_report of this passed :parent_report. For example, if the parent report includes a sum
|
21
|
-
# aggregated
|
22
|
-
# on a given row versus the total
|
21
|
+
# aggregated "views" column, the child report can use Report::Calculator::Ratio to caluclate the ratio of "views"
|
22
|
+
# on a given row versus the total "views" from the parent report.
|
23
23
|
@parent_report = @params.delete(:parent_report)
|
24
24
|
@parent_groupers = @params.delete(:parent_groupers) || ( grouper_names & Array(parent_report&.grouper_names) )
|
25
25
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "active_reporter/inflector"
|
2
2
|
|
3
3
|
module ActiveReporter
|
4
4
|
module Serializer
|
@@ -13,7 +13,7 @@ module ActiveReporter
|
|
13
13
|
# on the aggregators or dimension name.
|
14
14
|
|
15
15
|
def human_aggregator_label(aggregators)
|
16
|
-
aggregators.keys.collect { |aggregator| aggregator.to_s.humanize }.join(
|
16
|
+
aggregators.keys.collect { |aggregator| aggregator.to_s.humanize }.join(" ")
|
17
17
|
end
|
18
18
|
|
19
19
|
def human_dimension_label(dimension)
|
@@ -62,8 +62,8 @@ module ActiveReporter
|
|
62
62
|
|
63
63
|
def time_formats
|
64
64
|
{
|
65
|
-
minutes:
|
66
|
-
weeks:
|
65
|
+
minutes: "%F %k:%M", hours: "%F %k", days: "%F",
|
66
|
+
weeks: "week of %F", months: "%Y-%m", years: "%Y"
|
67
67
|
}
|
68
68
|
end
|
69
69
|
|
@@ -95,8 +95,8 @@ module ActiveReporter
|
|
95
95
|
report.filters.flat_map do |dimension|
|
96
96
|
human_dimension_label(dimension) + " = " + dimension.filter_values.map do |value|
|
97
97
|
human_dimension_value_label(dimension, value)
|
98
|
-
end.to_sentence(last_word_connector:
|
99
|
-
end.join(
|
98
|
+
end.to_sentence(last_word_connector: ", or ")
|
99
|
+
end.join("; ")
|
100
100
|
end
|
101
101
|
end
|
102
102
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "csv"
|
2
2
|
|
3
3
|
module ActiveReporter
|
4
4
|
module Serializer
|
@@ -11,7 +11,7 @@ module ActiveReporter
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def save(filename = self.filename)
|
14
|
-
File.open(filename,
|
14
|
+
File.open(filename, "w") { |f| f.write data }
|
15
15
|
end
|
16
16
|
|
17
17
|
def filename
|