compendium 1.1.3.4 → 1.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 +5 -13
- data/.travis.yml +12 -0
- data/CHANGELOG.md +18 -0
- data/Gemfile +4 -0
- data/README.md +185 -4
- data/app/assets/stylesheets/compendium/options.css.scss +2 -3
- data/app/classes/compendium/presenters/chart.rb +1 -1
- data/app/classes/compendium/presenters/csv.rb +32 -0
- data/app/classes/compendium/presenters/query.rb +4 -2
- data/app/classes/compendium/presenters/settings/query.rb +10 -2
- data/app/classes/compendium/presenters/settings/table.rb +24 -4
- data/app/classes/compendium/presenters/table.rb +33 -18
- data/app/controllers/compendium/reports_controller.rb +28 -7
- data/app/views/compendium/reports/setup.haml +16 -2
- data/compendium.gemspec +4 -3
- data/config/locales/en.yml +6 -1
- data/config/locales/es.yml +11 -0
- data/config/locales/fr.yml +11 -0
- data/lib/compendium.rb +1 -0
- data/lib/compendium/collection_query.rb +1 -1
- data/lib/compendium/count_query.rb +7 -1
- data/lib/compendium/dsl.rb +45 -4
- data/lib/compendium/engine/mount.rb +13 -5
- data/lib/compendium/errors.rb +6 -0
- data/lib/compendium/metric.rb +1 -1
- data/lib/compendium/open_hash.rb +1 -1
- data/lib/compendium/param_types.rb +2 -2
- data/lib/compendium/params.rb +1 -1
- data/lib/compendium/query.rb +23 -4
- data/lib/compendium/report.rb +14 -10
- data/lib/compendium/sum_query.rb +3 -1
- data/lib/compendium/through_query.rb +7 -2
- data/lib/compendium/version.rb +1 -1
- data/spec/count_query_spec.rb +41 -5
- data/spec/dsl_spec.rb +77 -2
- data/spec/presenters/csv_spec.rb +30 -0
- data/spec/presenters/settings/query_spec.rb +26 -0
- data/spec/presenters/settings/table_spec.rb +64 -0
- data/spec/presenters/table_spec.rb +83 -0
- data/spec/query_spec.rb +55 -8
- data/spec/report_spec.rb +24 -1
- data/spec/sum_query_spec.rb +40 -5
- metadata +73 -30
- data/config/initializers/ruby/hash.rb +0 -6
@@ -5,8 +5,8 @@ module Compendium
|
|
5
5
|
|
6
6
|
before_filter :find_report
|
7
7
|
before_filter :find_query
|
8
|
-
before_filter :validate_options, only: :run
|
9
|
-
before_filter :run_report, only: :run
|
8
|
+
before_filter :validate_options, only: [:run, :export]
|
9
|
+
before_filter :run_report, only: [:run, :export]
|
10
10
|
|
11
11
|
def setup
|
12
12
|
render_setup
|
@@ -25,6 +25,27 @@ module Compendium
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
def export
|
29
|
+
unless @report.exports?(request.format)
|
30
|
+
redirect_to action: :setup, format: nil
|
31
|
+
return
|
32
|
+
end
|
33
|
+
|
34
|
+
respond_to do |format|
|
35
|
+
format.csv do
|
36
|
+
filename = @report.report_name.to_s.parameterize + '-' + Time.current.strftime('%Y%m%d%H%I%S')
|
37
|
+
response.headers['Content-Disposition'] = 'attachment; filename="' + filename + '.csv"'
|
38
|
+
|
39
|
+
query = @report.queries[@report.exporters[:csv]]
|
40
|
+
render text: query.render_csv
|
41
|
+
end
|
42
|
+
|
43
|
+
format.any do
|
44
|
+
redirect_to action: :setup, format: nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
28
49
|
private
|
29
50
|
|
30
51
|
def find_report
|
@@ -32,11 +53,11 @@ module Compendium
|
|
32
53
|
@report_name = "#{@prefix}_report"
|
33
54
|
|
34
55
|
begin
|
35
|
-
require(@report_name) unless Rails.env.development?
|
56
|
+
require(@report_name) unless Rails.env.development? || Module.const_defined?(@report_name.classify)
|
36
57
|
@report_class = @report_name.camelize.constantize
|
37
58
|
@report = setup_report
|
38
59
|
rescue LoadError
|
39
|
-
flash[:error] = t(:invalid_report)
|
60
|
+
flash[:error] = t(:invalid_report, scope: 'compendium.reports')
|
40
61
|
redirect_to action: :index
|
41
62
|
end
|
42
63
|
end
|
@@ -46,7 +67,7 @@ module Compendium
|
|
46
67
|
@query = @report.queries[params[:query]]
|
47
68
|
|
48
69
|
unless @query
|
49
|
-
flash[:error] = t(:invalid_report_query)
|
70
|
+
flash[:error] = t(:invalid_report_query, scope: 'compendium.reports')
|
50
71
|
redirect_to action: :setup, report_name: params[:report_name]
|
51
72
|
end
|
52
73
|
end
|
@@ -61,7 +82,7 @@ module Compendium
|
|
61
82
|
end
|
62
83
|
|
63
84
|
def validate_options
|
64
|
-
render_setup
|
85
|
+
render_setup && return unless @report.valid?
|
65
86
|
end
|
66
87
|
|
67
88
|
def run_report
|
@@ -80,4 +101,4 @@ module Compendium
|
|
80
101
|
paths
|
81
102
|
end
|
82
103
|
end
|
83
|
-
end
|
104
|
+
end
|
@@ -4,11 +4,25 @@
|
|
4
4
|
= render_if_exists partial: 'report_header', path: 'compendium/reports'
|
5
5
|
|
6
6
|
.options
|
7
|
-
= form_for report, as: :report, url: compendium_reports_run_path do |f|
|
7
|
+
= form_for report, as: :report, url: compendium_reports_run_path, html: { id: 'setup_report_form' } do |f|
|
8
|
+
= hidden_field_tag :format, :html
|
9
|
+
|
8
10
|
- report.options.each do |option|
|
9
11
|
- expose option, Compendium::Presenters::Option do |opt|
|
10
12
|
.option
|
11
13
|
= opt.label(f)
|
12
14
|
.option-element-group= opt.input(self, f)
|
13
15
|
|
14
|
-
|
16
|
+
.option
|
17
|
+
= f.submit t(:generate_report, scope: 'compendium.reports'), onclick: "set_format('html')"
|
18
|
+
- if report.exports?(:csv)
|
19
|
+
= f.submit t(:export_csv, scope: 'compendium.reports'), name: :export, onclick: "set_format('csv')"
|
20
|
+
|
21
|
+
:javascript
|
22
|
+
function set_format(format)
|
23
|
+
{
|
24
|
+
var form = document.getElementById('setup_report_form'),
|
25
|
+
input = form.querySelector('input#format[type=hidden]');
|
26
|
+
|
27
|
+
input.value = format;
|
28
|
+
}
|
data/compendium.gemspec
CHANGED
@@ -18,10 +18,11 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
19
|
gem.require_paths = ["lib"]
|
20
20
|
|
21
|
-
gem.add_dependency 'rails', '>= 3.0.0'
|
21
|
+
gem.add_dependency 'rails', '>= 3.0.0', '< 4'
|
22
22
|
gem.add_dependency 'sass-rails', '>= 3.0.0'
|
23
23
|
gem.add_dependency 'compass-rails', '>= 1.0.0'
|
24
|
-
gem.add_dependency 'collection_of', '1.0.
|
24
|
+
gem.add_dependency 'collection_of', '1.0.6'
|
25
25
|
gem.add_dependency 'inheritable_attr', '>= 1.0.0'
|
26
|
-
gem.add_development_dependency '
|
26
|
+
gem.add_development_dependency 'rake', '> 11.0.1', '< 12'
|
27
|
+
gem.add_development_dependency 'rspec', '~> 2.0', '< 2.99'
|
27
28
|
end
|
data/config/locales/en.yml
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
en:
|
2
2
|
compendium:
|
3
|
+
total: "Total"
|
4
|
+
|
3
5
|
reports:
|
6
|
+
generate_report: "Generate Report"
|
7
|
+
export_csv: "Export CSV"
|
8
|
+
|
4
9
|
invalid_report: "Invalid report!"
|
5
10
|
invalid_report_query: "Invalid query!"
|
6
|
-
|
11
|
+
query_required_for_csv: "A query must be specified to export a CSV file!"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
es:
|
2
|
+
compendium:
|
3
|
+
total: "Total"
|
4
|
+
|
5
|
+
reports:
|
6
|
+
generate_report: "Generar informe"
|
7
|
+
export_csv: "Exportar CSV"
|
8
|
+
|
9
|
+
invalid_report: "¡Informe no válido!"
|
10
|
+
invalid_report_query: "¡Consulta no válida!"
|
11
|
+
query_required_for_csv: "¡Se debe especificar una consulta para exportar un archivo CSV!"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
fr:
|
2
|
+
compendium:
|
3
|
+
total: "Total"
|
4
|
+
|
5
|
+
reports:
|
6
|
+
generate_report: "Générer Rapport"
|
7
|
+
export_csv: "Exporter CSV"
|
8
|
+
|
9
|
+
invalid_report: "Rapport invalide!"
|
10
|
+
invalid_report_query: "Requête invalide!"
|
11
|
+
query_required_for_csv: "Une requête doit être spécifiée afin d'exporter un fichier CSV!"
|
data/lib/compendium.rb
CHANGED
@@ -37,7 +37,7 @@ module Compendium
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def prepare_collection(collection)
|
40
|
-
return collection if collection.is_a?(Query)
|
40
|
+
return collection if collection.is_a?(Query) || collection.is_a?(Symbol)
|
41
41
|
collection.is_a?(Hash) ? collection : Hash[collection.zip(collection)]
|
42
42
|
end
|
43
43
|
end
|
@@ -1,10 +1,16 @@
|
|
1
|
+
require 'compendium/errors'
|
1
2
|
require 'compendium/query'
|
2
3
|
|
3
4
|
module Compendium
|
4
5
|
# A CountQuery is a Query which runs an SQL count statement
|
5
6
|
# Often useful in conjunction with a grouped query
|
6
7
|
class CountQuery < Query
|
7
|
-
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
super
|
11
|
+
|
12
|
+
@options.reverse_merge!(order: 'COUNT(*)', reverse: true)
|
13
|
+
end
|
8
14
|
|
9
15
|
private
|
10
16
|
|
data/lib/compendium/dsl.rb
CHANGED
@@ -8,14 +8,17 @@ module Compendium
|
|
8
8
|
def self.extended(klass)
|
9
9
|
klass.inheritable_attr :queries, default: ::Collection[Query]
|
10
10
|
klass.inheritable_attr :options, default: ::Collection[Option]
|
11
|
+
klass.inheritable_attr :exporters, default: {}
|
11
12
|
end
|
12
13
|
|
14
|
+
# Define a query
|
13
15
|
def query(name, opts = {}, &block)
|
14
16
|
define_query(name, opts, &block)
|
15
17
|
end
|
16
18
|
alias_method :chart, :query
|
17
19
|
alias_method :data, :query
|
18
20
|
|
21
|
+
# Define a parameter for the report
|
19
22
|
def option(name, *args)
|
20
23
|
opts = args.extract_options!
|
21
24
|
type = args.shift
|
@@ -31,6 +34,8 @@ module Compendium
|
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
37
|
+
# Define a metric from a query or implicitly
|
38
|
+
# A metric is a derived statistic from a report, for instance a count of rows
|
34
39
|
def metric(name, *args, &block)
|
35
40
|
proc = args.first.is_a?(Proc) ? args.first : block
|
36
41
|
opts = args.extract_options!
|
@@ -49,10 +54,32 @@ module Compendium
|
|
49
54
|
end
|
50
55
|
end
|
51
56
|
|
57
|
+
# Define a filter to modify the results from specified query (in this case :deliveries)
|
58
|
+
# For example, this can be useful to translate columns prior to rendering, as it will apply
|
59
|
+
# for all render types (table, chart, JSON)
|
60
|
+
# Multiple queries can be set up with the same filter
|
52
61
|
def filter(*query_names, &block)
|
53
|
-
query_names
|
54
|
-
|
55
|
-
|
62
|
+
each_query(query_names) do |query|
|
63
|
+
query.add_filter(block)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Allow default table settings to be defined for a query.
|
68
|
+
# These settings are used when rendering a query to an HTML table or to CSV
|
69
|
+
def table(*query_names, &block)
|
70
|
+
each_query(query_names) do |query|
|
71
|
+
query.table_settings = block
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Define any exports the report has
|
76
|
+
def exports(type, *opts)
|
77
|
+
exporters[type] = if opts.empty?
|
78
|
+
true
|
79
|
+
elsif opts.length == 1
|
80
|
+
opts.first
|
81
|
+
else
|
82
|
+
opts
|
56
83
|
end
|
57
84
|
end
|
58
85
|
|
@@ -86,6 +113,13 @@ module Compendium
|
|
86
113
|
|
87
114
|
private
|
88
115
|
|
116
|
+
def each_query(query_names, &block)
|
117
|
+
query_names.each do |query_name|
|
118
|
+
raise ArgumentError, "query #{query_name} is not defined" unless queries.key?(query_name)
|
119
|
+
yield queries[query_name]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
89
123
|
def define_query(name, opts, &block)
|
90
124
|
params = [name.to_sym, opts, block]
|
91
125
|
query_type = Query
|
@@ -110,7 +144,14 @@ module Compendium
|
|
110
144
|
query.report = self
|
111
145
|
|
112
146
|
metrics[name] = opts[:metric] if opts.key?(:metric)
|
147
|
+
|
148
|
+
if queries[name]
|
149
|
+
raise CannotRedefineQueryType unless queries[name].instance_of?(query_type)
|
150
|
+
queries.delete(name)
|
151
|
+
end
|
152
|
+
|
113
153
|
queries << query
|
154
|
+
|
114
155
|
query
|
115
156
|
end
|
116
157
|
|
@@ -119,4 +160,4 @@ module Compendium
|
|
119
160
|
self.params_class.validates name, validations
|
120
161
|
end
|
121
162
|
end
|
122
|
-
end
|
163
|
+
end
|
@@ -1,13 +1,21 @@
|
|
1
1
|
module ActionDispatch
|
2
2
|
module Routing
|
3
3
|
class Mapper
|
4
|
+
class ExportRouter
|
5
|
+
def matches?(request)
|
6
|
+
request.params[:export].present?
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
4
10
|
def mount_compendium(options = {})
|
5
|
-
scope options[:at], controller: options.fetch(:controller, 'compendium/reports') do
|
6
|
-
get ':report_name', action: :setup, as: '
|
7
|
-
|
8
|
-
|
11
|
+
scope options[:at], controller: options.fetch(:controller, 'compendium/reports'), as: 'compendium_reports' do
|
12
|
+
get ':report_name', action: :setup, constraints: { format: :html }, as: 'setup'
|
13
|
+
match ':report_name/export', action: :export, as: 'export', via: [:get, :post]
|
14
|
+
post ':report_name(/:query)', constraints: ExportRouter.new, action: :export, as: 'export_post'
|
15
|
+
match ':report_name(/:query)', action: :run, as: 'run', via: [:get, :post]
|
16
|
+
root action: :index, as: 'root'
|
9
17
|
end
|
10
18
|
end
|
11
19
|
end
|
12
20
|
end
|
13
|
-
end
|
21
|
+
end
|
data/lib/compendium/metric.rb
CHANGED
@@ -27,7 +27,7 @@ module Compendium
|
|
27
27
|
private
|
28
28
|
|
29
29
|
def condition_failed?(ctx)
|
30
|
-
(options.key?(:if)
|
30
|
+
(options.key?(:if) && !ctx.instance_exec(&options[:if])) || (options.key?(:unless) && ctx.instance_exec(&options[:unless]))
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
data/lib/compendium/open_hash.rb
CHANGED
@@ -38,7 +38,7 @@ module Compendium
|
|
38
38
|
index = obj
|
39
39
|
else
|
40
40
|
index = obj.numeric? ? obj.to_i : @choices.index(obj)
|
41
|
-
raise IndexError if (!obj.nil?
|
41
|
+
raise IndexError if (!obj.nil? && index.nil?) || index.to_i.abs > @choices.length - 1
|
42
42
|
end
|
43
43
|
|
44
44
|
super(index)
|
@@ -63,7 +63,7 @@ module Compendium
|
|
63
63
|
class BooleanParam < Param
|
64
64
|
def initialize(obj, *)
|
65
65
|
# If given 0, 1, or a version thereof (ie. "0"), pass it along
|
66
|
-
return super obj.to_i if obj.numeric?
|
66
|
+
return super obj.to_i if obj.numeric? && (0..1).cover?(obj.to_i)
|
67
67
|
super !!obj ? 0 : 1
|
68
68
|
end
|
69
69
|
|
data/lib/compendium/params.rb
CHANGED
data/lib/compendium/query.rb
CHANGED
@@ -4,12 +4,11 @@ require 'compendium/metric'
|
|
4
4
|
require 'compendium/presenters/chart'
|
5
5
|
require 'compendium/presenters/table'
|
6
6
|
require 'collection_of'
|
7
|
-
require_relative '../../config/initializers/ruby/hash'
|
8
7
|
|
9
8
|
module Compendium
|
10
9
|
class Query
|
11
10
|
attr_reader :name, :results, :metrics, :filters
|
12
|
-
attr_accessor :options, :proc, :report
|
11
|
+
attr_accessor :options, :proc, :report, :table_settings
|
13
12
|
|
14
13
|
def initialize(*args)
|
15
14
|
@report = args.shift if arg_is_report?(args.first)
|
@@ -58,6 +57,10 @@ module Compendium
|
|
58
57
|
Compendium::Presenters::Table.new(template, self, *options, &block).render unless empty?
|
59
58
|
end
|
60
59
|
|
60
|
+
def render_csv(&block)
|
61
|
+
Compendium::Presenters::CSV.new(self, &block).render unless empty?
|
62
|
+
end
|
63
|
+
|
61
64
|
# Allow access to the chart object without having to explicitly render it
|
62
65
|
def chart(template, *options, &block)
|
63
66
|
# Access the actual chart object
|
@@ -89,6 +92,8 @@ module Compendium
|
|
89
92
|
|
90
93
|
def collect_results(context, *params)
|
91
94
|
command = context.instance_exec(*params, &proc) if proc
|
95
|
+
command = order_command(command) if options[:order]
|
96
|
+
|
92
97
|
results = fetch_results(command)
|
93
98
|
results = filter_results(results, *params) if filters.any?
|
94
99
|
@results = ResultSet.new(results) if results
|
@@ -105,6 +110,12 @@ module Compendium
|
|
105
110
|
def filter_results(results, params)
|
106
111
|
return unless results
|
107
112
|
|
113
|
+
if results.respond_to? :with_indifferent_access
|
114
|
+
results = results.with_indifferent_access
|
115
|
+
else
|
116
|
+
results.map! &:with_indifferent_access
|
117
|
+
end
|
118
|
+
|
108
119
|
filters.each do |f|
|
109
120
|
if f.arity == 2
|
110
121
|
results = f.call(results, params)
|
@@ -116,6 +127,14 @@ module Compendium
|
|
116
127
|
results
|
117
128
|
end
|
118
129
|
|
130
|
+
def order_command(command)
|
131
|
+
return command unless command.respond_to?(:order)
|
132
|
+
|
133
|
+
command = command.order(options[:order])
|
134
|
+
command = command.reverse_order if options.fetch(:reverse, false)
|
135
|
+
command
|
136
|
+
end
|
137
|
+
|
119
138
|
def execute_command(command)
|
120
139
|
return [] if command.nil?
|
121
140
|
command = command.to_sql if command.respond_to?(:to_sql)
|
@@ -127,11 +146,11 @@ module Compendium
|
|
127
146
|
end
|
128
147
|
|
129
148
|
def arg_is_report?(arg)
|
130
|
-
arg.is_a?(Report)
|
149
|
+
arg.is_a?(Report) || (arg.is_a?(Class) && arg < Report)
|
131
150
|
end
|
132
151
|
|
133
152
|
def get_associated_query(query)
|
134
153
|
query.is_a?(Query) ? query : report.queries[query]
|
135
154
|
end
|
136
155
|
end
|
137
|
-
end
|
156
|
+
end
|