reports_kits 0.7.5 → 0.7.7
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/.gitignore +6 -0
- data/.rubocop.yml +85 -0
- data/.travis.yml +21 -0
- data/Appraisals +27 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +20 -0
- data/README.md +35 -0
- data/Rakefile +2 -0
- data/app/assets/javascripts/reports_kits/application.js +14 -0
- data/app/assets/javascripts/reports_kits/lib/_init.js +9 -0
- data/app/assets/javascripts/reports_kits/lib/chart.js +73 -0
- data/app/assets/javascripts/reports_kits/lib/report.js +135 -0
- data/app/assets/javascripts/reports_kits/lib/table.js +87 -0
- data/app/assets/javascripts/reports_kits/vendor/chart.js +12269 -0
- data/app/assets/javascripts/reports_kits/vendor/daterangepicker.js +1627 -0
- data/app/assets/javascripts/reports_kits/vendor/jquery.tablesorter.min.js +4 -0
- data/app/assets/javascripts/reports_kits/vendor/moment.js +4040 -0
- data/app/assets/javascripts/reports_kits/vendor/select2.full.js +6436 -0
- data/app/assets/stylesheets/reports_kits/application.css.scss +3 -0
- data/app/assets/stylesheets/reports_kits/reports.css.sass +33 -0
- data/app/assets/stylesheets/reports_kits/select2_overrides.css.sass +7 -0
- data/app/assets/stylesheets/reports_kits/vendor/daterangepicker.css +269 -0
- data/app/assets/stylesheets/reports_kits/vendor/select2-bootstrap.css +721 -0
- data/app/assets/stylesheets/reports_kits/vendor/select2.css +484 -0
- data/config/initializers/mime_types.rb +1 -0
- data/config/routes.rb +10 -0
- data/docs/images/demo.gif +0 -0
- data/docs/images/users_by_created_at.png +0 -0
- data/gemfiles/mysql.gemfile +7 -0
- data/gemfiles/mysql.gemfile.lock +167 -0
- data/gemfiles/postgresql.gemfile +7 -0
- data/gemfiles/postgresql.gemfile.lock +165 -0
- data/gemfiles/postgresql_rails_5.1.4.gemfile +8 -0
- data/gemfiles/postgresql_rails_5.1.4.gemfile.lock +168 -0
- data/gemfiles/rails_4_mysql.gemfile +8 -0
- data/gemfiles/rails_4_mysql.gemfile.lock +165 -0
- data/gemfiles/rails_4_postgresql.gemfile +8 -0
- data/gemfiles/rails_4_postgresql.gemfile.lock +163 -0
- data/gemfiles/rails_5.1.4_postgresql.gemfile +8 -0
- data/gemfiles/rails_5.1.4_postgresql.gemfile.lock +169 -0
- data/gemfiles/rails_5_mysql.gemfile +8 -0
- data/gemfiles/rails_5_mysql.gemfile.lock +171 -0
- data/gemfiles/rails_5_postgresql.gemfile +8 -0
- data/gemfiles/rails_5_postgresql.gemfile.lock +169 -0
- data/lib/reports_kits/base_controller.rb +17 -0
- data/lib/reports_kits/cache.rb +37 -0
- data/lib/reports_kits/configuration.rb +50 -0
- data/lib/reports_kits/engine.rb +21 -0
- data/lib/reports_kits/entity.rb +3 -0
- data/lib/reports_kits/filters_controller.rb +11 -0
- data/lib/reports_kits/form_builder.rb +66 -0
- data/lib/reports_kits/helper.rb +24 -0
- data/lib/reports_kits/model.rb +16 -0
- data/lib/reports_kits/model_configuration.rb +28 -0
- data/lib/reports_kits/normalized_params.rb +16 -0
- data/lib/reports_kits/order.rb +33 -0
- data/lib/reports_kits/relative_time.rb +42 -0
- data/lib/reports_kits/report_builder.rb +88 -0
- data/lib/reports_kits/reports/abstract_series.rb +9 -0
- data/lib/reports_kits/reports/adapters/mysql.rb +26 -0
- data/lib/reports_kits/reports/adapters/postgresql.rb +26 -0
- data/lib/reports_kits/reports/composite_series.rb +48 -0
- data/lib/reports_kits/reports/contextual_filter.rb +19 -0
- data/lib/reports_kits/reports/data/add_table_aggregations.rb +105 -0
- data/lib/reports_kits/reports/data/aggregate_composite.rb +97 -0
- data/lib/reports_kits/reports/data/aggregate_one_dimension.rb +39 -0
- data/lib/reports_kits/reports/data/aggregate_two_dimensions.rb +39 -0
- data/lib/reports_kits/reports/data/chart_data_for_data_method.rb +62 -0
- data/lib/reports_kits/reports/data/chart_options.rb +155 -0
- data/lib/reports_kits/reports/data/format_one_dimension.rb +140 -0
- data/lib/reports_kits/reports/data/format_table.rb +63 -0
- data/lib/reports_kits/reports/data/format_two_dimensions.rb +143 -0
- data/lib/reports_kits/reports/data/generate.rb +156 -0
- data/lib/reports_kits/reports/data/generate_for_properties.rb +97 -0
- data/lib/reports_kits/reports/data/normalize_properties.rb +62 -0
- data/lib/reports_kits/reports/data/populate_one_dimension.rb +54 -0
- data/lib/reports_kits/reports/data/populate_two_dimensions.rb +104 -0
- data/lib/reports_kits/reports/data/utils.rb +178 -0
- data/lib/reports_kits/reports/dimension.rb +27 -0
- data/lib/reports_kits/reports/dimension_with_series.rb +144 -0
- data/lib/reports_kits/reports/filter.rb +29 -0
- data/lib/reports_kits/reports/filter_types/base.rb +48 -0
- data/lib/reports_kits/reports/filter_types/boolean.rb +47 -0
- data/lib/reports_kits/reports/filter_types/datetime.rb +51 -0
- data/lib/reports_kits/reports/filter_types/number.rb +30 -0
- data/lib/reports_kits/reports/filter_types/records.rb +26 -0
- data/lib/reports_kits/reports/filter_types/string.rb +38 -0
- data/lib/reports_kits/reports/filter_with_series.rb +97 -0
- data/lib/reports_kits/reports/generate_autocomplete_method_results.rb +29 -0
- data/lib/reports_kits/reports/generate_autocomplete_results.rb +57 -0
- data/lib/reports_kits/reports/inferrable_configuration.rb +116 -0
- data/lib/reports_kits/reports/model_settings.rb +30 -0
- data/lib/reports_kits/reports/properties.rb +10 -0
- data/lib/reports_kits/reports/properties_to_filter.rb +40 -0
- data/lib/reports_kits/reports/series.rb +121 -0
- data/lib/reports_kits/reports_controller.rb +65 -0
- data/lib/reports_kits/utils.rb +11 -0
- data/lib/reports_kits/value.rb +3 -0
- data/lib/reports_kits/version.rb +3 -0
- data/lib/reports_kits.rb +79 -0
- data/reports_kits.gemspec +26 -0
- data/spec/factories/issue_factory.rb +4 -0
- data/spec/factories/issues_label_factory.rb +4 -0
- data/spec/factories/label_factory.rb +4 -0
- data/spec/factories/pro_repo_factory.rb +5 -0
- data/spec/factories/repo_factory.rb +5 -0
- data/spec/factories/tag_factory.rb +4 -0
- data/spec/fixtures/generate_inputs.yml +254 -0
- data/spec/fixtures/generate_outputs.yml +1216 -0
- data/spec/reports_kit/form_builder_spec.rb +26 -0
- data/spec/reports_kit/relative_time_spec.rb +29 -0
- data/spec/reports_kit/reports/data/generate/contextual_filters_spec.rb +60 -0
- data/spec/reports_kit/reports/data/generate_spec.rb +1371 -0
- data/spec/reports_kit/reports/data/normalize_properties_spec.rb +196 -0
- data/spec/reports_kit/reports/dimension_with_series_spec.rb +67 -0
- data/spec/reports_kit/reports/filter_with_series_spec.rb +39 -0
- data/spec/reports_kit/reports/generate_autocomplete_results_spec.rb +69 -0
- data/spec/spec_helper.rb +77 -0
- data/spec/support/config.rb +41 -0
- data/spec/support/example_data_methods.rb +25 -0
- data/spec/support/factory_girl.rb +5 -0
- data/spec/support/helpers.rb +25 -0
- data/spec/support/models/issue.rb +14 -0
- data/spec/support/models/issues_label.rb +4 -0
- data/spec/support/models/label.rb +5 -0
- data/spec/support/models/pro/repo.rb +5 -0
- data/spec/support/models/pro/special_issue.rb +4 -0
- data/spec/support/models/repo.rb +13 -0
- data/spec/support/models/tag.rb +4 -0
- data/spec/support/schema.rb +39 -0
- metadata +134 -4
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
module ReportsKits
|
|
2
|
+
module Reports
|
|
3
|
+
class InferrableConfiguration
|
|
4
|
+
SUPPORTED_COLUMN_TYPES = [
|
|
5
|
+
:boolean,
|
|
6
|
+
:datetime,
|
|
7
|
+
:integer,
|
|
8
|
+
:string,
|
|
9
|
+
:text
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
attr_accessor :inferrable, :inferrable_type, :model_settings
|
|
13
|
+
|
|
14
|
+
delegate :key, :expression, :series, to: :inferrable
|
|
15
|
+
delegate :model_class, :settings_from_model, to: :model_settings
|
|
16
|
+
|
|
17
|
+
def initialize(inferrable, inferrable_type)
|
|
18
|
+
self.inferrable = inferrable
|
|
19
|
+
self.inferrable_type = inferrable_type
|
|
20
|
+
self.model_settings = ModelSettings.new(series.model_class, inferrable_type, key)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def configuration_strategy
|
|
24
|
+
if settings_from_model.present?
|
|
25
|
+
:model
|
|
26
|
+
elsif reflection
|
|
27
|
+
:association
|
|
28
|
+
elsif column_type
|
|
29
|
+
:column
|
|
30
|
+
else
|
|
31
|
+
inferrable_type_string = inferrable_type.to_s.singularize
|
|
32
|
+
raise ArgumentError.new("No configuration found on the #{model_class} model for #{inferrable_type_string} with key: '#{key}'")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def configured_by_association?
|
|
37
|
+
configuration_strategy == :association
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def configured_by_column?
|
|
41
|
+
configuration_strategy == :column
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def configured_by_model?
|
|
45
|
+
configuration_strategy == :model
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def configured_by_time?
|
|
49
|
+
column_type == :datetime
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def reflection
|
|
53
|
+
model_class.reflect_on_association(expression.to_sym)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def instance_class
|
|
57
|
+
return reflection.klass if reflection
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def column
|
|
62
|
+
return unless inferred_settings
|
|
63
|
+
inferred_settings[:column]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def inferred_settings
|
|
67
|
+
return { column: "#{model_class.table_name}.#{expression}" } if configured_by_column?
|
|
68
|
+
if configured_by_association?
|
|
69
|
+
return inferred_settings_from_belongs_to_or_has_one if inferred_settings_from_belongs_to_or_has_one
|
|
70
|
+
return inferred_settings_from_has_many if inferred_settings_from_has_many
|
|
71
|
+
end
|
|
72
|
+
{}
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def inferred_settings_from_belongs_to_or_has_one
|
|
76
|
+
@inferred_settings_from_belongs_to_or_has_one ||= begin
|
|
77
|
+
return unless reflection.macro.in?([:belongs_to, :has_one])
|
|
78
|
+
through_reflection = reflection.through_reflection
|
|
79
|
+
if through_reflection
|
|
80
|
+
{
|
|
81
|
+
joins: through_reflection.name,
|
|
82
|
+
column: "#{through_reflection.table_name}.#{reflection.source_reflection.foreign_key}"
|
|
83
|
+
}
|
|
84
|
+
else
|
|
85
|
+
{
|
|
86
|
+
column: "#{model_class.table_name}.#{reflection.foreign_key}"
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def inferred_settings_from_has_many
|
|
93
|
+
@inferred_settings_from_has_many ||= begin
|
|
94
|
+
return unless reflection.macro == :has_many
|
|
95
|
+
through_reflection = reflection.through_reflection
|
|
96
|
+
if through_reflection
|
|
97
|
+
{
|
|
98
|
+
joins: through_reflection.name,
|
|
99
|
+
column: "#{through_reflection.table_name}.#{reflection.source_reflection.foreign_key}"
|
|
100
|
+
}
|
|
101
|
+
else
|
|
102
|
+
{
|
|
103
|
+
joins: reflection.name,
|
|
104
|
+
column: "#{reflection.klass.table_name}.#{reflection.klass.primary_key}"
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def column_type
|
|
111
|
+
column_type = model_class.columns_hash[expression.to_s].try(:type)
|
|
112
|
+
return column_type if SUPPORTED_COLUMN_TYPES.include?(column_type)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module ReportsKits
|
|
2
|
+
module Reports
|
|
3
|
+
class ModelSettings
|
|
4
|
+
attr_accessor :model_class, :model_configuration_type, :key
|
|
5
|
+
|
|
6
|
+
def initialize(model_class, model_configuration_type, key)
|
|
7
|
+
self.model_class = model_class
|
|
8
|
+
self.model_configuration_type = model_configuration_type
|
|
9
|
+
self.key = key
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def settings_from_model
|
|
13
|
+
return {} if model_configuration.blank?
|
|
14
|
+
config_hashes = model_configuration.public_send(model_configuration_type)
|
|
15
|
+
return {} if config_hashes.blank?
|
|
16
|
+
config_hash = config_hashes.find do |hash|
|
|
17
|
+
hash[:key] == key
|
|
18
|
+
end
|
|
19
|
+
config_hash || {}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def model_configuration
|
|
25
|
+
return unless model_class && model_class.respond_to?(:reports_kit_configuration)
|
|
26
|
+
model_class.reports_kit_configuration
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module ReportsKits
|
|
2
|
+
module Reports
|
|
3
|
+
class PropertiesToFilter
|
|
4
|
+
attr_accessor :properties, :context_record
|
|
5
|
+
|
|
6
|
+
def initialize(properties, context_record: nil)
|
|
7
|
+
self.properties = properties
|
|
8
|
+
self.context_record = context_record
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def perform(filter_key)
|
|
12
|
+
filter_key = filter_key.to_s
|
|
13
|
+
filter = filters.find { |f| f.key == filter_key }
|
|
14
|
+
raise ArgumentError.new("A filter with key '#{filter_key}' is not configured in this report") unless filter
|
|
15
|
+
filter
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def filters
|
|
21
|
+
@filters ||= ui_filters + series_filters
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def series_filters
|
|
25
|
+
serieses.map(&:filters).flatten
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def ui_filters
|
|
29
|
+
return [] if properties[:ui_filters].blank?
|
|
30
|
+
properties[:ui_filters].map do |ui_filter_properties|
|
|
31
|
+
Reports::Filter.new(ui_filter_properties)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def serieses
|
|
36
|
+
Reports::Series.new_from_properties!(properties, context_record: context_record)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
module ReportsKits
|
|
2
|
+
module Reports
|
|
3
|
+
class Series < AbstractSeries
|
|
4
|
+
VALID_KEYS = [:measure, :dimensions, :contextual_filters, :context_params, :filters, :limit, :context_params, :report_options]
|
|
5
|
+
|
|
6
|
+
attr_accessor :properties, :dimensions, :contextual_filters, :filters, :context_record
|
|
7
|
+
|
|
8
|
+
def initialize(properties, context_record: nil)
|
|
9
|
+
properties = { measure: properties } if properties.is_a?(String)
|
|
10
|
+
properties = properties.deep_symbolize_keys.dup
|
|
11
|
+
measure_properties = properties[:measure]
|
|
12
|
+
properties[:measure] = measure_properties
|
|
13
|
+
properties[:measure] = { key: properties[:measure] } if properties[:measure].is_a?(String)
|
|
14
|
+
raise ArgumentError.new("Measure properties must be a String or Hash, not a #{properties.class.name}: #{properties.inspect}") unless properties.is_a?(Hash)
|
|
15
|
+
|
|
16
|
+
contextual_filter_keys = properties[:contextual_filters] || []
|
|
17
|
+
dimension_hashes = properties[:dimensions] || []
|
|
18
|
+
dimension_hashes = dimension_hashes.values if dimension_hashes.is_a?(Hash) && dimension_hashes.key?(:'0')
|
|
19
|
+
filter_hashes = properties[:filters] || []
|
|
20
|
+
filter_hashes = filter_hashes.values if filter_hashes.is_a?(Hash) && filter_hashes.key?(:'0')
|
|
21
|
+
|
|
22
|
+
self.properties = properties
|
|
23
|
+
self.context_record = context_record
|
|
24
|
+
self.dimensions = dimension_hashes.map { |dimension_hash| DimensionWithSeries.new(dimension: Dimension.new(dimension_hash), series: self) }
|
|
25
|
+
self.filters = filter_hashes.map { |filter_hash| FilterWithSeries.new(filter: Filter.new(filter_hash), series: self) }
|
|
26
|
+
self.contextual_filters = contextual_filter_keys.map { |key| ContextualFilter.new(key, model_class) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def key
|
|
30
|
+
properties[:measure][:key].underscore
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def label
|
|
34
|
+
properties[:measure][:name].presence || key.pluralize.titleize
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def limit
|
|
38
|
+
properties[:limit]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def edit_relation_method
|
|
42
|
+
ReportsKits.configuration.custom_method(properties[:report_options].try(:[], :edit_relation_method))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def relation_name
|
|
46
|
+
key.tableize
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def aggregate_function
|
|
50
|
+
aggregation_expression || [:count, model_class.primary_key]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def aggregation_expression
|
|
54
|
+
return unless aggregation_config
|
|
55
|
+
expression = aggregation_config[:expression]
|
|
56
|
+
if expression.is_a?(Array)
|
|
57
|
+
expression
|
|
58
|
+
else
|
|
59
|
+
raise ArgumentError.new("The '#{aggregation_key}' aggregation on the #{model_class} model isn't valid")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def aggregation_key
|
|
64
|
+
properties[:measure][:aggregation]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def aggregation_config
|
|
68
|
+
@aggregation_config ||= begin
|
|
69
|
+
return unless aggregation_key
|
|
70
|
+
raise ArgumentError.new("A '#{aggregation_key}' aggregation on the #{model_class} model hasn't been configured") unless model_class.respond_to?(:reports_kit_configuration)
|
|
71
|
+
config = model_class.reports_kit_configuration.aggregations.find { |aggregation| aggregation[:key] == aggregation_key }
|
|
72
|
+
raise ArgumentError.new("A '#{aggregation_key}' aggregation on the #{model_class} model hasn't been configured") unless config
|
|
73
|
+
config
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def base_relation
|
|
78
|
+
return context_record.public_send(relation_name) if context_record
|
|
79
|
+
model_class
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def model_class
|
|
83
|
+
if context_record
|
|
84
|
+
reflection = context_record.class.reflect_on_association(key.to_sym) ||
|
|
85
|
+
context_record.class.reflect_on_association(key.pluralize.to_sym)
|
|
86
|
+
return reflection.klass if reflection
|
|
87
|
+
end
|
|
88
|
+
key.camelize.constantize
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def filtered_relation
|
|
92
|
+
relation = base_relation
|
|
93
|
+
filters.each do |filter|
|
|
94
|
+
relation = filter.apply(relation)
|
|
95
|
+
end
|
|
96
|
+
contextual_filters.each do |filter|
|
|
97
|
+
relation = filter.apply(relation, properties[:context_params])
|
|
98
|
+
end
|
|
99
|
+
relation
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def has_two_dimensions?
|
|
103
|
+
dimensions.length == 2
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def self.new_from_properties!(properties, context_record:)
|
|
107
|
+
series_hashes = properties[:series].presence || properties.slice(*Series::VALID_KEYS)
|
|
108
|
+
series_hashes = [series_hashes] if series_hashes.is_a?(Hash)
|
|
109
|
+
raise ArgumentError.new('At least one series must be configured') if series_hashes.blank?
|
|
110
|
+
|
|
111
|
+
series_hashes.map do |series_hash|
|
|
112
|
+
if series_hash[:composite_operator].present?
|
|
113
|
+
CompositeSeries.new(series_hash, context_record: context_record)
|
|
114
|
+
else
|
|
115
|
+
new(series_hash, context_record: context_record)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require 'csv'
|
|
2
|
+
require 'spreadsheet'
|
|
3
|
+
|
|
4
|
+
module ReportsKits
|
|
5
|
+
class ReportsController < ReportsKits::BaseController
|
|
6
|
+
before_action :modify_context_params
|
|
7
|
+
|
|
8
|
+
VALID_PARAMS_PROPERTIES_KEYS = [:ui_filters]
|
|
9
|
+
|
|
10
|
+
def index
|
|
11
|
+
respond_to do |format|
|
|
12
|
+
format.json do
|
|
13
|
+
render json: { data: report_data }
|
|
14
|
+
end
|
|
15
|
+
format.csv do
|
|
16
|
+
properties[:format] = 'csv'
|
|
17
|
+
csv = CSV.generate do |csv|
|
|
18
|
+
report_data[:table_data].each do |row|
|
|
19
|
+
csv << row
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
send_data csv, filename: "#{report_filename}.csv"
|
|
23
|
+
end
|
|
24
|
+
format.xls do
|
|
25
|
+
properties[:format] = 'csv'
|
|
26
|
+
send_data xls_string, filename: "#{report_filename}.xls", type: 'application/vnd.ms-excel'
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def report_filename
|
|
34
|
+
report_filename_method = ReportsKits.configuration.report_filename_method
|
|
35
|
+
return 'Report' unless report_filename_method
|
|
36
|
+
instance_eval(&report_filename_method)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def report_data
|
|
40
|
+
Reports::Data::Generate.new(properties, context_record: context_record, context_params: context_params).perform
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def properties
|
|
44
|
+
@properties ||= begin
|
|
45
|
+
properties = Reports::Properties.generate(self)
|
|
46
|
+
properties.merge(params_properties).deep_symbolize_keys
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def params_properties
|
|
51
|
+
@params_properties ||= ActiveSupport::JSON.decode(params[:properties]).with_indifferent_access.slice(*VALID_PARAMS_PROPERTIES_KEYS)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def xls_string
|
|
55
|
+
spreadsheet = Spreadsheet::Workbook.new
|
|
56
|
+
sheet = spreadsheet.create_worksheet
|
|
57
|
+
report_data[:table_data].each_with_index do |row, index|
|
|
58
|
+
sheet.update_row(index, *row)
|
|
59
|
+
end
|
|
60
|
+
io = StringIO.new
|
|
61
|
+
spreadsheet.write(io)
|
|
62
|
+
io.string
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module ReportsKits
|
|
2
|
+
class Utils
|
|
3
|
+
def self.string_to_class_method(string, string_identifer)
|
|
4
|
+
class_name, method_name = string.split('.')
|
|
5
|
+
raise ArgumentError.new("The #{string_identifer} value should be a class method with a format of MyClass.my_method") unless class_name && method_name
|
|
6
|
+
klass = class_name.constantize
|
|
7
|
+
raise ArgumentError.new("The #{string_identifer} class (#{class_name}) does not respond to a method named \"#{method_name}\"") unless klass.respond_to?(method_name)
|
|
8
|
+
[klass, method_name]
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
data/lib/reports_kits.rb
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require 'rails/all'
|
|
2
|
+
|
|
3
|
+
require 'reports_kits/normalized_params'
|
|
4
|
+
require 'reports_kits/base_controller'
|
|
5
|
+
require 'reports_kits/cache'
|
|
6
|
+
require 'reports_kits/configuration'
|
|
7
|
+
require 'reports_kits/engine'
|
|
8
|
+
require 'reports_kits/entity'
|
|
9
|
+
require 'reports_kits/filters_controller'
|
|
10
|
+
require 'reports_kits/form_builder'
|
|
11
|
+
require 'reports_kits/helper'
|
|
12
|
+
require 'reports_kits/model'
|
|
13
|
+
require 'reports_kits/model_configuration'
|
|
14
|
+
require 'reports_kits/order'
|
|
15
|
+
require 'reports_kits/relative_time'
|
|
16
|
+
require 'reports_kits/report_builder'
|
|
17
|
+
require 'reports_kits/reports_controller'
|
|
18
|
+
require 'reports_kits/utils'
|
|
19
|
+
require 'reports_kits/value'
|
|
20
|
+
require 'reports_kits/version'
|
|
21
|
+
|
|
22
|
+
require 'reports_kits/reports/adapters/mysql'
|
|
23
|
+
require 'reports_kits/reports/adapters/postgresql'
|
|
24
|
+
|
|
25
|
+
require 'reports_kits/reports/data/add_table_aggregations'
|
|
26
|
+
require 'reports_kits/reports/data/aggregate_composite'
|
|
27
|
+
require 'reports_kits/reports/data/aggregate_one_dimension'
|
|
28
|
+
require 'reports_kits/reports/data/aggregate_two_dimensions'
|
|
29
|
+
require 'reports_kits/reports/data/chart_data_for_data_method'
|
|
30
|
+
require 'reports_kits/reports/data/chart_options'
|
|
31
|
+
require 'reports_kits/reports/data/format_one_dimension'
|
|
32
|
+
require 'reports_kits/reports/data/format_table'
|
|
33
|
+
require 'reports_kits/reports/data/format_two_dimensions'
|
|
34
|
+
require 'reports_kits/reports/data/generate'
|
|
35
|
+
require 'reports_kits/reports/data/generate_for_properties'
|
|
36
|
+
require 'reports_kits/reports/data/normalize_properties'
|
|
37
|
+
require 'reports_kits/reports/data/populate_one_dimension'
|
|
38
|
+
require 'reports_kits/reports/data/populate_two_dimensions'
|
|
39
|
+
require 'reports_kits/reports/data/utils'
|
|
40
|
+
|
|
41
|
+
require 'reports_kits/reports/filter_types/base'
|
|
42
|
+
require 'reports_kits/reports/filter_types/boolean'
|
|
43
|
+
require 'reports_kits/reports/filter_types/datetime'
|
|
44
|
+
require 'reports_kits/reports/filter_types/number'
|
|
45
|
+
require 'reports_kits/reports/filter_types/records'
|
|
46
|
+
require 'reports_kits/reports/filter_types/string'
|
|
47
|
+
|
|
48
|
+
require 'reports_kits/reports/abstract_series'
|
|
49
|
+
require 'reports_kits/reports/composite_series'
|
|
50
|
+
require 'reports_kits/reports/contextual_filter'
|
|
51
|
+
require 'reports_kits/reports/dimension'
|
|
52
|
+
require 'reports_kits/reports/dimension_with_series'
|
|
53
|
+
require 'reports_kits/reports/filter'
|
|
54
|
+
require 'reports_kits/reports/filter_with_series'
|
|
55
|
+
require 'reports_kits/reports/generate_autocomplete_results'
|
|
56
|
+
require 'reports_kits/reports/generate_autocomplete_method_results'
|
|
57
|
+
require 'reports_kits/reports/inferrable_configuration'
|
|
58
|
+
require 'reports_kits/reports/model_settings'
|
|
59
|
+
require 'reports_kits/reports/properties'
|
|
60
|
+
require 'reports_kits/reports/properties_to_filter'
|
|
61
|
+
require 'reports_kits/reports/series'
|
|
62
|
+
|
|
63
|
+
class ReportsKits
|
|
64
|
+
def self.configure
|
|
65
|
+
yield(configuration)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.configuration
|
|
69
|
+
@configuration ||= Configuration.new
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.parse_date_range(string)
|
|
73
|
+
ReportsKits::Reports::Data::Utils.parse_date_range(string)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def self.format_date_range(string)
|
|
77
|
+
ReportsKits::Reports::Data::Utils.format_date_range(string)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require File.expand_path('../lib/reports_kits/version', __FILE__)
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |s|
|
|
4
|
+
s.authors = ["aipanhara"]
|
|
5
|
+
s.email = ["titpanhara@gmail.com"]
|
|
6
|
+
s.summary = 'Beautiful, interactive charts for Ruby on Rails'
|
|
7
|
+
s.description = 'ReportsKits lets you easily create beautiful charts with customizable, interactive filters.'
|
|
8
|
+
s.homepage = 'https://github.com/Panhara28/reports_kits'
|
|
9
|
+
s.name = 'reports_kits'
|
|
10
|
+
s.require_paths = ["lib"]
|
|
11
|
+
s.files = `git ls-files`.split($\)
|
|
12
|
+
s.version = ReportsKits::VERSION
|
|
13
|
+
s.license = 'MIT'
|
|
14
|
+
s.add_runtime_dependency 'rails', '~> 5'
|
|
15
|
+
s.add_runtime_dependency 'spreadsheet', '~> 1.1'
|
|
16
|
+
|
|
17
|
+
s. add_development_dependency 'appraisal', '~> 0'
|
|
18
|
+
s.add_development_dependency 'rspec', '~> 3'
|
|
19
|
+
s.add_development_dependency 'database_cleaner', '~> 1'
|
|
20
|
+
s.add_development_dependency 'factory_girl', '~> 4'
|
|
21
|
+
s.add_development_dependency 'groupdate', '~> 3'
|
|
22
|
+
s.add_development_dependency 'pg', '~> 0.15'
|
|
23
|
+
s.add_development_dependency 'pry', '~> 0'
|
|
24
|
+
s.add_development_dependency 'pry-byebug', '~> 1'
|
|
25
|
+
s.add_development_dependency 'timecop', '~> 0'
|
|
26
|
+
end
|