reports_kits 0.7.5 → 0.7.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|