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