reports_kit 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +6 -5
- data/app/assets/javascripts/reports_kit/lib/chart.js +33 -8
- data/app/assets/javascripts/reports_kit/lib/report.js +27 -26
- data/app/assets/javascripts/reports_kit/lib/table.js +58 -0
- data/app/assets/javascripts/reports_kit/vendor/jquery.tablesorter.min.js +4 -0
- data/app/assets/stylesheets/reports_kit/reports.css.sass +20 -0
- data/config/initializers/mime_types.rb +1 -0
- data/docs/dimensions.md +34 -26
- data/docs/display_options.md +15 -12
- data/docs/filters.md +2 -2
- data/docs/measures.md +4 -3
- data/gemfiles/mysql.gemfile.lock +14 -1
- data/gemfiles/postgresql.gemfile.lock +14 -1
- data/lib/reports_kit.rb +15 -0
- data/lib/reports_kit/cache.rb +37 -0
- data/lib/reports_kit/configuration.rb +13 -1
- data/lib/reports_kit/helper.rb +54 -3
- data/lib/reports_kit/model_configuration.rb +6 -1
- data/lib/reports_kit/order.rb +33 -0
- data/lib/reports_kit/relative_time.rb +42 -0
- data/lib/reports_kit/report_builder.rb +34 -15
- data/lib/reports_kit/reports/abstract_measure.rb +9 -0
- data/lib/reports_kit/reports/adapters/mysql.rb +8 -1
- data/lib/reports_kit/reports/adapters/postgresql.rb +8 -1
- data/lib/reports_kit/reports/composite_measure.rb +43 -0
- data/lib/reports_kit/reports/data/chart_options.rb +5 -0
- data/lib/reports_kit/reports/data/composite_aggregation.rb +96 -0
- data/lib/reports_kit/reports/data/entity.rb +7 -0
- data/lib/reports_kit/reports/data/format_one_dimension.rb +120 -0
- data/lib/reports_kit/reports/data/format_two_dimensions.rb +141 -0
- data/lib/reports_kit/reports/data/generate.rb +72 -25
- data/lib/reports_kit/reports/data/generate_for_properties.rb +75 -0
- data/lib/reports_kit/reports/data/one_dimension.rb +15 -49
- data/lib/reports_kit/reports/data/populate_one_dimension.rb +36 -0
- data/lib/reports_kit/reports/data/populate_two_dimensions.rb +104 -0
- data/lib/reports_kit/reports/data/two_dimensions.rb +15 -110
- data/lib/reports_kit/reports/data/utils.rb +77 -12
- data/lib/reports_kit/reports/data/value.rb +7 -0
- data/lib/reports_kit/reports/dimension.rb +4 -110
- data/lib/reports_kit/reports/dimension_with_measure.rb +137 -0
- data/lib/reports_kit/reports/filter.rb +5 -64
- data/lib/reports_kit/reports/filter_types/base.rb +1 -1
- data/lib/reports_kit/reports/filter_types/boolean.rb +9 -7
- data/lib/reports_kit/reports/filter_types/datetime.rb +7 -5
- data/lib/reports_kit/reports/filter_types/number.rb +2 -0
- data/lib/reports_kit/reports/filter_with_measure.rb +84 -0
- data/lib/reports_kit/reports/generate_autocomplete_results.rb +1 -1
- data/lib/reports_kit/reports/inferrable_configuration.rb +32 -13
- data/lib/reports_kit/reports/measure.rb +48 -12
- data/lib/reports_kit/reports_controller.rb +42 -3
- data/lib/reports_kit/version.rb +1 -1
- data/reports_kit.gemspec +2 -0
- data/spec/fixtures/generate_inputs.yml +146 -21
- data/spec/fixtures/generate_outputs.yml +768 -17
- data/spec/reports_kit/relative_time_spec.rb +29 -0
- data/spec/reports_kit/report_builder_spec.rb +28 -0
- data/spec/reports_kit/reports/data/generate_spec.rb +614 -27
- data/spec/reports_kit/reports/dimension_with_measure_spec.rb +69 -0
- data/spec/reports_kit/reports/{filter_spec.rb → filter_with_measure_spec.rb} +4 -3
- data/spec/spec_helper.rb +7 -2
- data/spec/support/config.rb +11 -0
- data/spec/support/helpers.rb +3 -3
- data/spec/support/models/issue.rb +7 -0
- metadata +53 -4
- data/spec/reports_kit/reports/dimension_spec.rb +0 -54
@@ -2,16 +2,38 @@ module ReportsKit
|
|
2
2
|
module Reports
|
3
3
|
module Data
|
4
4
|
class Utils
|
5
|
-
def self.
|
5
|
+
def self.format_display_time(time)
|
6
6
|
time.strftime('%b %-d, \'%y')
|
7
7
|
end
|
8
8
|
|
9
|
+
def self.format_configuration_time(time)
|
10
|
+
time.strftime('%b %-d, %Y')
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.format_time_value(value)
|
14
|
+
time = RelativeTime.parse(value, prevent_exceptions: true)
|
15
|
+
return value unless time
|
16
|
+
Utils.format_configuration_time(time)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.format_number(number)
|
20
|
+
number_i = number.to_i
|
21
|
+
return number_i if number == number_i
|
22
|
+
number.round(Generate::ROUND_PRECISION)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.parse_date_string(string)
|
26
|
+
begin
|
27
|
+
Date.parse(string)
|
28
|
+
rescue ArgumentError
|
29
|
+
RelativeTime.parse(string)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
9
33
|
def self.populate_sparse_hash(hash, dimension:)
|
10
|
-
return hash if hash.blank?
|
11
34
|
keys = hash.keys
|
12
|
-
is_nested =
|
13
|
-
if
|
14
|
-
is_nested = true
|
35
|
+
is_nested = dimension.measure.has_two_dimensions?
|
36
|
+
if is_nested
|
15
37
|
keys_values = arrays_values_to_nested_hash(hash)
|
16
38
|
keys = keys_values.keys
|
17
39
|
else
|
@@ -36,22 +58,22 @@ module ReportsKit
|
|
36
58
|
end
|
37
59
|
|
38
60
|
def self.populate_sparse_keys(keys, dimension:)
|
39
|
-
return keys if keys.blank?
|
40
61
|
first_key = dimension.first_key || keys.first
|
41
62
|
return keys unless first_key.is_a?(Time) || first_key.is_a?(Date)
|
63
|
+
first_key = first_key.to_date
|
42
64
|
granularity = dimension.granularity
|
43
65
|
|
44
|
-
first_key = first_key.beginning_of_week if granularity == 'week'
|
66
|
+
first_key = first_key.beginning_of_week(ReportsKit.configuration.first_day_of_week) if granularity == 'week'
|
45
67
|
keys = keys.sort
|
46
|
-
last_key = dimension.last_key || keys.last
|
47
|
-
last_key = last_key.beginning_of_week if granularity == 'week'
|
68
|
+
last_key = (dimension.last_key || keys.last).to_date
|
69
|
+
last_key = last_key.beginning_of_week(ReportsKit.configuration.first_day_of_week) if granularity == 'week'
|
48
70
|
|
49
71
|
if granularity == 'week'
|
50
|
-
beginning_of_current_week =
|
72
|
+
beginning_of_current_week = Date.today.beginning_of_week(ReportsKit.configuration.first_day_of_week)
|
51
73
|
last_key = [beginning_of_current_week, last_key].compact.max
|
52
74
|
end
|
53
75
|
|
54
|
-
date = first_key
|
76
|
+
date = first_key
|
55
77
|
populated_keys = []
|
56
78
|
interval = granularity == 'week' ? 1.week : 1.day
|
57
79
|
loop do
|
@@ -94,11 +116,19 @@ module ReportsKit
|
|
94
116
|
Hash[dimension_ids_dimension_instances]
|
95
117
|
end
|
96
118
|
|
119
|
+
def self.dimension_key_to_entity(dimension_key, dimension, dimension_ids_dimension_instances)
|
120
|
+
instance = dimension_ids_dimension_instances ? dimension_ids_dimension_instances[dimension_key] : dimension_key
|
121
|
+
label = dimension_key_to_label(dimension_key, dimension, dimension_ids_dimension_instances)
|
122
|
+
Entity.new(dimension_key, label, instance)
|
123
|
+
end
|
124
|
+
|
97
125
|
def self.dimension_key_to_label(dimension_instance, dimension, ids_dimension_instances)
|
126
|
+
label = dimension.key_to_label(dimension_instance)
|
127
|
+
return label if label
|
98
128
|
return dimension_instance.to_s if dimension.configured_by_column? && dimension.column_type == :integer
|
99
129
|
case dimension_instance
|
100
130
|
when Time, Date
|
101
|
-
Utils.
|
131
|
+
Utils.format_display_time(dimension_instance)
|
102
132
|
when Fixnum
|
103
133
|
raise ArgumentError.new("ids_dimension_instances must be present for Dimension with identifier: #{dimension_instance}") unless ids_dimension_instances
|
104
134
|
instance = ids_dimension_instances[dimension_instance.to_i]
|
@@ -108,6 +138,41 @@ module ReportsKit
|
|
108
138
|
dimension_instance.to_s.gsub(/\.0$/, '')
|
109
139
|
end
|
110
140
|
end
|
141
|
+
|
142
|
+
def self.raw_value_to_value(raw_value, value_format_method)
|
143
|
+
formatted_value = format_number(raw_value)
|
144
|
+
formatted_value = value_format_method.call(raw_value) if value_format_method
|
145
|
+
Value.new(raw_value, formatted_value)
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.normalize_filters(measure_properties, ui_filters)
|
149
|
+
measure_properties[:filters] = measure_properties[:filters].map do |filter_properties|
|
150
|
+
filter_properties = { key: filter_properties } if filter_properties.is_a?(String)
|
151
|
+
key = filter_properties[:key]
|
152
|
+
ui_key = filter_properties[:ui_key]
|
153
|
+
value = ui_filters[key.to_sym]
|
154
|
+
value ||= ui_filters[ui_key.to_sym] if ui_key
|
155
|
+
if value
|
156
|
+
filter_properties[:criteria] ||= {}
|
157
|
+
filter_properties[:criteria][:value] = value
|
158
|
+
end
|
159
|
+
filter_properties
|
160
|
+
end
|
161
|
+
measure_properties
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.normalize_properties(properties, ui_filters: nil)
|
165
|
+
ui_filters ||= properties[:ui_filters]
|
166
|
+
properties = properties.dup
|
167
|
+
properties[:measures] = [properties.delete(:measure)] if properties[:measure]
|
168
|
+
return properties if ui_filters.blank? || properties[:measures].blank?
|
169
|
+
properties[:measures] = properties[:measures].map do |measure_properties|
|
170
|
+
measure_properties = normalize_properties(measure_properties, ui_filters: ui_filters)
|
171
|
+
next(measure_properties) if measure_properties[:filters].blank?
|
172
|
+
normalize_filters(measure_properties, ui_filters)
|
173
|
+
end
|
174
|
+
properties
|
175
|
+
end
|
111
176
|
end
|
112
177
|
end
|
113
178
|
end
|
@@ -1,128 +1,22 @@
|
|
1
1
|
module ReportsKit
|
2
2
|
module Reports
|
3
3
|
class Dimension
|
4
|
-
|
5
|
-
DEFAULT_GRANULARITY = 'week'
|
6
|
-
VALID_GRANULARITIES = %w(day week).freeze
|
7
|
-
ADAPTER_NAMES_CLASSES = {
|
8
|
-
'mysql2' => Adapters::Mysql,
|
9
|
-
'postgresql' => Adapters::Postgresql
|
10
|
-
}.freeze
|
11
|
-
|
12
|
-
attr_accessor :properties, :measure, :configuration
|
13
|
-
|
14
|
-
delegate :configured_by_association?, :configured_by_column?, :configured_by_model?, :configured_by_time?,
|
15
|
-
:settings_from_model, :reflection, :instance_class, :model_class, :column_type,
|
16
|
-
to: :configuration
|
17
|
-
|
18
|
-
def initialize(properties, measure:)
|
19
|
-
self.configuration = InferrableConfiguration.new(self, :dimensions)
|
20
|
-
self.measure = measure
|
4
|
+
attr_accessor :properties
|
21
5
|
|
6
|
+
def initialize(properties)
|
22
7
|
raise ArgumentError.new('Blank properties') if properties.blank?
|
23
8
|
properties = { key: properties } if properties.is_a?(String)
|
24
|
-
raise ArgumentError.new("
|
9
|
+
raise ArgumentError.new("Dimension properties must be a String or Hash, not a #{properties.class.name}: #{properties.inspect}") unless properties.is_a?(Hash)
|
25
10
|
properties = properties.deep_symbolize_keys
|
26
11
|
self.properties = properties
|
27
|
-
missing_group_setting = settings && !settings.key?(:group)
|
28
|
-
raise ArgumentError.new("Dimension settings for dimension '#{key}' of #{model_class} must include :group") if missing_group_setting
|
29
12
|
end
|
30
13
|
|
31
14
|
def key
|
32
15
|
properties[:key]
|
33
16
|
end
|
34
17
|
|
35
|
-
def granularity
|
36
|
-
@granularity ||= begin
|
37
|
-
return unless configured_by_time?
|
38
|
-
granularity = properties[:granularity] || DEFAULT_GRANULARITY
|
39
|
-
raise ArgumentError.new("Invalid granularity: #{granularity}") unless VALID_GRANULARITIES.include?(granularity)
|
40
|
-
granularity
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
18
|
def label
|
45
|
-
key.titleize
|
46
|
-
end
|
47
|
-
|
48
|
-
def settings
|
49
|
-
inferred_settings.merge(settings_from_model)
|
50
|
-
end
|
51
|
-
|
52
|
-
def inferred_settings
|
53
|
-
configuration.inferred_settings.merge(inferred_dimension_settings)
|
54
|
-
end
|
55
|
-
|
56
|
-
def inferred_dimension_settings
|
57
|
-
{
|
58
|
-
group: group_expression
|
59
|
-
}
|
60
|
-
end
|
61
|
-
|
62
|
-
def group_expression
|
63
|
-
if configured_by_model?
|
64
|
-
settings_from_model[:group]
|
65
|
-
elsif configured_by_association?
|
66
|
-
"#{model_class.table_name}.#{reflection.foreign_key}"
|
67
|
-
elsif configured_by_column? && configured_by_time?
|
68
|
-
granularity == 'day' ? day_expression : week_expression
|
69
|
-
elsif configured_by_column?
|
70
|
-
column_expression
|
71
|
-
else
|
72
|
-
raise ArgumentError.new('Invalid group_expression')
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def joins
|
77
|
-
settings_from_model[:joins] if configured_by_model?
|
78
|
-
end
|
79
|
-
|
80
|
-
def dimension_instances_limit
|
81
|
-
if configured_by_time?
|
82
|
-
properties[:limit]
|
83
|
-
else
|
84
|
-
properties[:limit] || DEFAULT_DIMENSION_INSTANCES_LIMIT
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def first_key
|
89
|
-
return unless configured_by_time? && datetime_filters.present?
|
90
|
-
datetime_filters.map(&:start_at).compact.sort.first
|
91
|
-
end
|
92
|
-
|
93
|
-
def last_key
|
94
|
-
return unless configured_by_time? && datetime_filters.present?
|
95
|
-
datetime_filters.map(&:end_at).compact.sort.last
|
96
|
-
end
|
97
|
-
|
98
|
-
def datetime_filters
|
99
|
-
return [] unless measure.filters.present?
|
100
|
-
measure.filters.map(&:filter_type).select { |filter_type| filter_type.is_a?(FilterTypes::Datetime) }
|
101
|
-
end
|
102
|
-
|
103
|
-
def should_be_sorted_by_count?
|
104
|
-
!configured_by_time?
|
105
|
-
end
|
106
|
-
|
107
|
-
def adapter
|
108
|
-
@adapter ||= begin
|
109
|
-
adapter_name = model_class.connection_config[:adapter]
|
110
|
-
adapter = ADAPTER_NAMES_CLASSES[adapter_name]
|
111
|
-
raise ArgumentError.new("Unsupported adapter: #{adapter_name}") unless adapter
|
112
|
-
adapter
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def column_expression
|
117
|
-
"#{model_class.table_name}.#{key}"
|
118
|
-
end
|
119
|
-
|
120
|
-
def day_expression
|
121
|
-
adapter.truncate_to_day(column_expression)
|
122
|
-
end
|
123
|
-
|
124
|
-
def week_expression
|
125
|
-
adapter.truncate_to_week(column_expression)
|
19
|
+
properties.key?(:label) ? properties[:label] : key.titleize
|
126
20
|
end
|
127
21
|
end
|
128
22
|
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module ReportsKit
|
2
|
+
module Reports
|
3
|
+
class DimensionWithMeasure
|
4
|
+
DEFAULT_DIMENSION_INSTANCES_LIMIT = 30
|
5
|
+
DEFAULT_GRANULARITY = 'week'
|
6
|
+
VALID_GRANULARITIES = %w(day week).freeze
|
7
|
+
ADAPTER_NAMES_CLASSES = {
|
8
|
+
'mysql2' => Adapters::Mysql,
|
9
|
+
'postgresql' => Adapters::Postgresql
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
attr_accessor :dimension, :measure, :configuration
|
13
|
+
|
14
|
+
delegate :key, :properties, :label, to: :dimension
|
15
|
+
delegate :configured_by_association?, :configured_by_column?, :configured_by_model?, :configured_by_time?,
|
16
|
+
:settings_from_model, :reflection, :instance_class, :model_class, :column_type,
|
17
|
+
to: :configuration
|
18
|
+
|
19
|
+
def initialize(dimension:, measure:)
|
20
|
+
self.dimension = dimension
|
21
|
+
self.measure = measure
|
22
|
+
self.configuration = InferrableConfiguration.new(self, :dimensions)
|
23
|
+
missing_group_setting = settings && !settings.key?(:group)
|
24
|
+
raise ArgumentError.new("Dimension settings for dimension '#{key}' of #{model_class} must include :group") if missing_group_setting
|
25
|
+
end
|
26
|
+
|
27
|
+
def granularity
|
28
|
+
@granularity ||= begin
|
29
|
+
return unless configured_by_time?
|
30
|
+
granularity = properties[:granularity] || DEFAULT_GRANULARITY
|
31
|
+
raise ArgumentError.new("Invalid granularity: #{granularity}") unless VALID_GRANULARITIES.include?(granularity)
|
32
|
+
granularity
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def settings
|
37
|
+
inferred_settings.merge(settings_from_model)
|
38
|
+
end
|
39
|
+
|
40
|
+
def inferred_settings
|
41
|
+
configuration.inferred_settings.merge(inferred_dimension_settings)
|
42
|
+
end
|
43
|
+
|
44
|
+
def inferred_dimension_settings
|
45
|
+
{
|
46
|
+
group: group_expression
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def group_expression
|
51
|
+
if configured_by_model?
|
52
|
+
settings_from_model[:group]
|
53
|
+
elsif configured_by_association?
|
54
|
+
inferred_settings_from_association[:column]
|
55
|
+
elsif configured_by_column? && configured_by_time?
|
56
|
+
granularity == 'day' ? day_expression : week_expression
|
57
|
+
elsif configured_by_column?
|
58
|
+
column_expression
|
59
|
+
else
|
60
|
+
raise ArgumentError.new('Invalid group_expression')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def inferred_settings_from_association
|
65
|
+
through_reflection = reflection.through_reflection
|
66
|
+
if through_reflection
|
67
|
+
{
|
68
|
+
joins: through_reflection.name,
|
69
|
+
column: "#{through_reflection.table_name}.#{reflection.source_reflection.foreign_key}"
|
70
|
+
}
|
71
|
+
else
|
72
|
+
{
|
73
|
+
column: "#{model_class.table_name}.#{reflection.foreign_key}"
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def joins
|
79
|
+
return settings_from_model[:joins] if configured_by_model?
|
80
|
+
inferred_settings_from_association[:joins] if configured_by_association?
|
81
|
+
end
|
82
|
+
|
83
|
+
def dimension_instances_limit
|
84
|
+
if configured_by_time?
|
85
|
+
properties[:limit]
|
86
|
+
else
|
87
|
+
properties[:limit] || DEFAULT_DIMENSION_INSTANCES_LIMIT
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def first_key
|
92
|
+
return unless configured_by_time? && datetime_filters.present?
|
93
|
+
datetime_filters.map(&:start_at).compact.sort.first
|
94
|
+
end
|
95
|
+
|
96
|
+
def last_key
|
97
|
+
return unless configured_by_time? && datetime_filters.present?
|
98
|
+
datetime_filters.map(&:end_at).compact.sort.last
|
99
|
+
end
|
100
|
+
|
101
|
+
def key_to_label(key)
|
102
|
+
return unless settings[:key_to_label]
|
103
|
+
settings[:key_to_label].call(key)
|
104
|
+
end
|
105
|
+
|
106
|
+
def datetime_filters
|
107
|
+
return [] unless measure.filters.present?
|
108
|
+
measure.filters.map(&:filter_type).select { |filter_type| filter_type.is_a?(FilterTypes::Datetime) }
|
109
|
+
end
|
110
|
+
|
111
|
+
def should_be_sorted_by_count?
|
112
|
+
!configured_by_time?
|
113
|
+
end
|
114
|
+
|
115
|
+
def adapter
|
116
|
+
@adapter ||= begin
|
117
|
+
adapter_name = model_class.connection_config[:adapter]
|
118
|
+
adapter = ADAPTER_NAMES_CLASSES[adapter_name]
|
119
|
+
raise ArgumentError.new("Unsupported adapter: #{adapter_name}") unless adapter
|
120
|
+
adapter
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def column_expression
|
125
|
+
"#{model_class.table_name}.#{key}"
|
126
|
+
end
|
127
|
+
|
128
|
+
def day_expression
|
129
|
+
adapter.truncate_to_day(column_expression)
|
130
|
+
end
|
131
|
+
|
132
|
+
def week_expression
|
133
|
+
adapter.truncate_to_week(column_expression)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -1,35 +1,12 @@
|
|
1
1
|
module ReportsKit
|
2
2
|
module Reports
|
3
3
|
class Filter
|
4
|
-
|
5
|
-
association: FilterTypes::Records,
|
6
|
-
boolean: FilterTypes::Boolean,
|
7
|
-
datetime: FilterTypes::Datetime,
|
8
|
-
integer: FilterTypes::Number,
|
9
|
-
string: FilterTypes::String
|
10
|
-
}
|
11
|
-
COLUMN_TYPES_FILTER_TYPE_CLASSES = {
|
12
|
-
boolean: FilterTypes::Boolean,
|
13
|
-
datetime: FilterTypes::Datetime,
|
14
|
-
integer: FilterTypes::Number,
|
15
|
-
string: FilterTypes::String
|
16
|
-
}
|
17
|
-
|
18
|
-
attr_accessor :properties, :measure, :configuration
|
19
|
-
|
20
|
-
delegate :configured_by_association?, :configured_by_column?, :configured_by_model?, :configured_by_time?,
|
21
|
-
:settings_from_model, :configuration_strategy, :instance_class, :column_type, :column,
|
22
|
-
to: :configuration
|
23
|
-
|
24
|
-
def initialize(properties, measure:)
|
25
|
-
self.configuration = InferrableConfiguration.new(self, :filters)
|
26
|
-
self.measure = measure
|
4
|
+
attr_accessor :properties
|
27
5
|
|
6
|
+
def initialize(properties)
|
28
7
|
properties = { key: properties } if properties.is_a?(String)
|
29
|
-
raise ArgumentError.new("
|
8
|
+
raise ArgumentError.new("Filter properties must be a String or Hash, not a #{properties.class.name}: #{properties.inspect}") unless properties.is_a?(Hash)
|
30
9
|
self.properties = properties.deep_symbolize_keys
|
31
|
-
self.properties[:criteria] = filter_type.default_criteria unless self.properties[:criteria]
|
32
|
-
self.properties = self.properties
|
33
10
|
end
|
34
11
|
|
35
12
|
def key
|
@@ -40,44 +17,8 @@ module ReportsKit
|
|
40
17
|
key.titleize
|
41
18
|
end
|
42
19
|
|
43
|
-
def
|
44
|
-
|
45
|
-
end
|
46
|
-
|
47
|
-
def inferred_settings
|
48
|
-
configuration.inferred_settings.merge(inferred_filter_settings)
|
49
|
-
end
|
50
|
-
|
51
|
-
def inferred_filter_settings
|
52
|
-
{
|
53
|
-
column: column
|
54
|
-
}
|
55
|
-
end
|
56
|
-
|
57
|
-
def type_klass
|
58
|
-
type_klass_for_configuration_strategy = CONFIGURATION_STRATEGIES_FILTER_TYPE_CLASSES[configuration_strategy]
|
59
|
-
return type_klass_for_configuration_strategy if type_klass_for_configuration_strategy
|
60
|
-
type_klass_for_column_type = COLUMN_TYPES_FILTER_TYPE_CLASSES[column_type]
|
61
|
-
return type_klass_for_column_type if type_klass_for_column_type
|
62
|
-
return filter_type_class_from_model if configured_by_model?
|
63
|
-
raise ArgumentError.new("No configuration found for filter with key: '#{key}'")
|
64
|
-
end
|
65
|
-
|
66
|
-
def filter_type
|
67
|
-
type_klass.new(settings, properties)
|
68
|
-
end
|
69
|
-
|
70
|
-
def filter_type_class_from_model
|
71
|
-
return unless settings
|
72
|
-
type_key = settings[:type_key]
|
73
|
-
raise ArgumentError.new("No type specified for filter with key: '#{key}'") unless type_key
|
74
|
-
type_class = CONFIGURATION_STRATEGIES_FILTER_TYPE_CLASSES[type_key]
|
75
|
-
raise ArgumentError.new("Invalid type ('#{type_key}') specified for filter with key: '#{key}'") unless type_class
|
76
|
-
type_class
|
77
|
-
end
|
78
|
-
|
79
|
-
def apply(relation)
|
80
|
-
filter_type.apply_filter(relation)
|
20
|
+
def normalized_properties
|
21
|
+
properties
|
81
22
|
end
|
82
23
|
end
|
83
24
|
end
|