simple_drilldown 0.9.11 → 0.11.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 -2
- data/app/views/drilldown/_chart.html.erb +2 -2
- data/app/views/drilldown/data_2.builder +1 -1
- data/app/views/drilldown/data_3.builder +1 -1
- data/app/views/drilldown/excel_export_xlsx.xlsx.axlsx +1 -1
- data/app/views/drilldown/index.html.erb +3 -2
- data/lib/generators/{drilldown_controller → simple_drilldown/controller}/USAGE +1 -1
- data/lib/generators/simple_drilldown/controller/controller_generator.rb +13 -0
- data/lib/generators/{drilldown_controller → simple_drilldown/controller}/templates/drilldown_controller.rb.erb +0 -0
- data/lib/generators/{drilldown_controller → simple_drilldown/controller}/templates/drilldown_controller_test.rb.erb +0 -0
- data/lib/generators/simple_drilldown/views/USAGE +9 -0
- data/lib/generators/simple_drilldown/views/views_generator.rb +11 -0
- data/lib/simple_drilldown/controller.rb +63 -65
- data/lib/simple_drilldown/helper.rb +9 -5
- data/lib/simple_drilldown/search.rb +1 -1
- data/lib/simple_drilldown/version.rb +1 -1
- metadata +9 -7
- data/lib/generators/drilldown_controller/drilldown_controller_generator.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1e99331c0a6ab073aece756d21fc1e639e426321975ffd23f64c1becddc375d8
|
4
|
+
data.tar.gz: 6db7888ff16123847f4df9f3b780fa9232af484c26abcacd38f45a56a25cccca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0002a2f6172dcd658e8bc329ed3985b3829690844c93b01d6a597083a0e59123b9c3fe5ad78f189a6041b8ad2ec82da0ff6e62a55dfe6e4118d2611aa9bde0c9
|
7
|
+
data.tar.gz: b877aab5468b90bccf608b854b3f362db63d36c4edd6806be9daed15136d54000f435a9047acd308b9016c5a194a4eed8fa86c58a1d73cc21bcbcc12d442b8b9
|
data/README.md
CHANGED
@@ -66,7 +66,7 @@ end
|
|
66
66
|
Create a new controller to focus on posts. Each drilldown controller focuses on
|
67
67
|
one main entity.
|
68
68
|
|
69
|
-
bin/rails g
|
69
|
+
bin/rails g simple_drilldown:controller User
|
70
70
|
|
71
71
|
```ruby
|
72
72
|
class PostsDrilldownController < DrilldownController
|
@@ -115,8 +115,12 @@ results.
|
|
115
115
|
|
116
116
|
### Views
|
117
117
|
|
118
|
-
|
118
|
+
This gem includes views for the drilldown visualization using Bootstrap.
|
119
119
|
|
120
|
+
You can override any views by creating them in your `app/views/drilldown` directory.
|
121
|
+
If you would like a local copy of the views for overriding you can use the generator.
|
122
|
+
|
123
|
+
bin/rails g simple_drilldown:views
|
120
124
|
|
121
125
|
|
122
126
|
## Excel export
|
@@ -41,12 +41,12 @@
|
|
41
41
|
options << [@dimensions[i][:pretty_name], @dimensions[i][:url_param_name]] if @dimensions[i]
|
42
42
|
options += @remaining_dimensions.keys.map { |name| [controller.c_dimension_defs[name][:pretty_name], name] } %>
|
43
43
|
<%= t(i == 0 ? :group_by : :then_by) %>:
|
44
|
-
<%= form.select 'dimensions', options, { :selected => @search.dimensions && @search.dimensions[i] },
|
44
|
+
<%= form.select 'dimensions', options.sort, { :selected => @search.dimensions && @search.dimensions[i] },
|
45
45
|
{ onChange: 'form.submit()', name: 'search[dimensions][]', id: "search_dimensions_#{i}" } %>
|
46
46
|
<% end %>
|
47
47
|
|
48
48
|
<br/>
|
49
|
-
<%= t :chart_type
|
49
|
+
<%= t :chart_type %>:
|
50
50
|
<%= form.radio_button 'display_type', SimpleDrilldown::Search::DisplayType::BAR, { :onChange => 'form.submit()' } %>
|
51
51
|
<%= form.label :display_type_bar, t(:bar) %>
|
52
52
|
<%= form.radio_button 'display_type', SimpleDrilldown::Search::DisplayType::PIE, { :disabled => @search.dimensions.size >= 2, :onChange => 'form.submit()' } %>
|
@@ -4,7 +4,7 @@ xml.chart(
|
|
4
4
|
xAxisName: (@dimensions[0][:pretty_name] || 'Elections').gsub("'", ''), palette: '2',
|
5
5
|
caption: caption, subcaption: subcaption,
|
6
6
|
showNames: '1',
|
7
|
-
showValues: @result[:rows].size > 15 || @result[:rows][0] && @result[:rows][0][:rows].size > 4 ? 0 : 1,
|
7
|
+
showValues: @result[:rows].size > 15 || (@result[:rows][0] && @result[:rows][0][:rows].size > 4) ? 0 : 1,
|
8
8
|
decimals: '0',
|
9
9
|
numberPrefix: '', clustered: '0', exeTime: '1.5', showPlotBorder: '0', zGapPlot: '30',
|
10
10
|
zDepth: '90', divLineEffect: 'emboss', startAngX: '10', endAngX: '18', startAngY: '-10',
|
@@ -5,7 +5,7 @@ xml.chart(
|
|
5
5
|
caption: caption, subcaption: subcaption,
|
6
6
|
showNames: '1',
|
7
7
|
showValues:
|
8
|
-
@result[:rows].size > 15 || @result[:rows][0] && @result[:rows][0][:rows].size > 4 ? 0 : 1,
|
8
|
+
@result[:rows].size > 15 || (@result[:rows][0] && @result[:rows][0][:rows].size > 4) ? 0 : 1,
|
9
9
|
decimals: '0',
|
10
10
|
numberPrefix: '', clustered: '0', exeTime: '1.5', showPlotBorder: '0', zGapPlot: '30',
|
11
11
|
zDepth: '90', divLineEffect: 'emboss', startAngX: '10', endAngX: '18', startAngY: '-10',
|
@@ -11,7 +11,7 @@ def excel_summary_row_xlsx(sheet, result, parent_result = nil, dimension = 0, he
|
|
11
11
|
else
|
12
12
|
headers + [{
|
13
13
|
value: result[:value],
|
14
|
-
display_row_count: result[:nodes] + result[:row_count] * (@search.list ? 1 : 0)
|
14
|
+
display_row_count: result[:nodes] + (result[:row_count] * (@search.list ? 1 : 0))
|
15
15
|
}]
|
16
16
|
end
|
17
17
|
else
|
@@ -17,8 +17,9 @@
|
|
17
17
|
<div class="col-md-3" valign="top">
|
18
18
|
<ul class="nav nav-tabs">
|
19
19
|
<li class="nav-item">
|
20
|
-
<a id="filter-tab" class="nav-link active" data-target="#filter" data-toggle="tab"><%= t :filter %></a></li>
|
21
|
-
<li
|
20
|
+
<a id="filter-tab" class="nav-link active" data-target="#filter" data-toggle="tab" data-bs-toggle="tab" data-bs-target="#filter"><%= t :filter %></a></li>
|
21
|
+
<li class="nav-item">
|
22
|
+
<a id="fields-tab" class="nav-link" data-target="#fields" data-toggle="tab" data-bs-toggle="tab" data-bs-target="#fields"><%= t :fields %></a>
|
22
23
|
</li>
|
23
24
|
</ul>
|
24
25
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleDrilldown
|
4
|
+
class ControllerGenerator < Rails::Generators::NamedBase
|
5
|
+
source_root File.expand_path('templates', __dir__)
|
6
|
+
|
7
|
+
def copy_drilldown_controller_file
|
8
|
+
template 'drilldown_controller.rb.erb', "app/controllers/#{file_name}_drilldown_controller.rb"
|
9
|
+
template 'drilldown_controller_test.rb.erb', "test/controllers/#{file_name}_drilldown_controller_test.rb"
|
10
|
+
route "draw_drilldown :#{singular_name}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
File without changes
|
File without changes
|
@@ -36,9 +36,7 @@ module SimpleDrilldown
|
|
36
36
|
# No default target class found
|
37
37
|
end
|
38
38
|
end
|
39
|
-
|
40
|
-
|
41
|
-
base.c_base_condition = "#{base.c_target_class.table_name}.deleted_at IS NULL"
|
39
|
+
base.update_base_condition
|
42
40
|
end
|
43
41
|
|
44
42
|
def base_condition(base_condition)
|
@@ -63,6 +61,7 @@ module SimpleDrilldown
|
|
63
61
|
|
64
62
|
def target_class(target_class)
|
65
63
|
self.c_target_class = target_class
|
64
|
+
update_base_condition
|
66
65
|
end
|
67
66
|
|
68
67
|
def select(select)
|
@@ -136,7 +135,9 @@ module SimpleDrilldown
|
|
136
135
|
interval: interval,
|
137
136
|
label_method: label_method,
|
138
137
|
legal_values: legal_values,
|
139
|
-
pretty_name: I18n.t(
|
138
|
+
pretty_name: I18n.t(:"drilldown.dimension.#{name}",
|
139
|
+
default: [:"drilldown.#{name}", :"activerecord.models.#{name}", name.to_sym,
|
140
|
+
name.to_s.titleize]),
|
140
141
|
queries: queries,
|
141
142
|
reverse: reverse,
|
142
143
|
select_expression:
|
@@ -148,25 +149,18 @@ module SimpleDrilldown
|
|
148
149
|
|
149
150
|
def legal_values_for(field, preserve_filter = false)
|
150
151
|
lambda do |search|
|
151
|
-
|
152
|
-
|
153
|
-
filter_conditions, _t, includes = make_conditions(
|
152
|
+
filter = search.filter.dup
|
153
|
+
filter.delete(field.to_s) unless preserve_filter
|
154
|
+
filter_conditions, _t, includes = make_conditions(filter)
|
154
155
|
dimension_def = c_dimension_defs[field.to_s]
|
155
156
|
result_sets = dimension_def[:queries].map do |query|
|
156
|
-
if query[:includes]
|
157
|
-
if query[:includes].is_a?(Array)
|
158
|
-
includes += query[:includes]
|
159
|
-
else
|
160
|
-
includes << query[:includes]
|
161
|
-
end
|
162
|
-
includes.uniq!
|
163
|
-
end
|
157
|
+
includes = merge_includes(includes, query[:includes]) if query[:includes]
|
164
158
|
rows_query = c_target_class.unscoped.where(c_base_condition)
|
165
159
|
.select("#{query[:select]} AS value")
|
166
160
|
.joins(make_join([], c_target_class.name.underscore.to_sym, includes))
|
167
161
|
.order('value')
|
168
162
|
.group(:value)
|
169
|
-
|
163
|
+
rows_query = rows_query.without_deleted if c_target_class.try :paranoid?
|
170
164
|
rows_query = rows_query.where(filter_conditions) if filter_conditions
|
171
165
|
rows_query = rows_query.where(query[:where]) if query[:where]
|
172
166
|
rows = rows_query.to_a
|
@@ -275,8 +269,8 @@ module SimpleDrilldown
|
|
275
269
|
fk_col = ass.options[:foreign_key] || "#{model}_id"
|
276
270
|
sql = +"LEFT JOIN #{include_table} #{include_alias} ON #{include_alias}.#{fk_col} = #{model_table}.id"
|
277
271
|
sql << " AND #{include_alias}.deleted_at IS NULL" if ass.klass.paranoid?
|
278
|
-
if ass.scope && (
|
279
|
-
ass_order
|
272
|
+
if ass.scope && (base_ass_order = ScopeHolder.new(ass.scope).to_s)
|
273
|
+
/^(?<ass_order>\S*)(?<ass_order_desc>\s+DESC)?/i =~ base_ass_order
|
280
274
|
ass_order_prefixed = ass_order.dup
|
281
275
|
ActiveRecord::Base.connection.columns(include_table).map(&:name).each do |cname|
|
282
276
|
ass_order_prefixed.gsub!(/\b#{cname}\b/, "#{include_alias}.#{cname}")
|
@@ -284,10 +278,10 @@ module SimpleDrilldown
|
|
284
278
|
paranoid_clause = 'AND t2.deleted_at IS NULL' if ass.klass.paranoid?
|
285
279
|
# FIXME(uwe): Should we add "where" from the ScopeHolder here as well?
|
286
280
|
# Ref: SimpleDrilldown::Changes#changes_for
|
287
|
-
|
288
|
-
SELECT MIN(#{ass_order}) FROM #{include_table} t2 WHERE t2.#{fk_col} = #{model_table}.id #{paranoid_clause}
|
281
|
+
aggregate_query = <<~SQL
|
282
|
+
SELECT #{ass_order_desc ? :MAX : :MIN}(#{ass_order}) FROM #{include_table} t2 WHERE t2.#{fk_col} = #{model_table}.id #{paranoid_clause}
|
289
283
|
SQL
|
290
|
-
sql << " AND #{ass_order_prefixed} = (#{
|
284
|
+
sql << " AND #{ass_order_prefixed} = (#{aggregate_query})"
|
291
285
|
end
|
292
286
|
sql
|
293
287
|
else
|
@@ -301,6 +295,51 @@ module SimpleDrilldown
|
|
301
295
|
raise "Unknown join class: #{include.inspect}"
|
302
296
|
end
|
303
297
|
end
|
298
|
+
|
299
|
+
def merge_includes(*args)
|
300
|
+
hash = hash_includes(*args)
|
301
|
+
result = hash.dup.map do |k, v|
|
302
|
+
if v.blank?
|
303
|
+
hash.delete(k)
|
304
|
+
k
|
305
|
+
end
|
306
|
+
end.compact
|
307
|
+
result << hash unless hash.blank?
|
308
|
+
case result.size
|
309
|
+
when 0
|
310
|
+
nil
|
311
|
+
when 1
|
312
|
+
result[0]
|
313
|
+
else
|
314
|
+
result
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def hash_includes(*args)
|
319
|
+
args.inject({}) do |h, inc|
|
320
|
+
case inc
|
321
|
+
when Array
|
322
|
+
inc.each do |v|
|
323
|
+
h = hash_includes(h, v)
|
324
|
+
end
|
325
|
+
when Hash
|
326
|
+
inc.each do |k, v|
|
327
|
+
h[k] = merge_includes(h[k], v)
|
328
|
+
end
|
329
|
+
when NilClass, FalseClass
|
330
|
+
# Leave as it is
|
331
|
+
when String, Symbol
|
332
|
+
h[inc] ||= []
|
333
|
+
else
|
334
|
+
raise "Unknown include type: #{inc.inspect}"
|
335
|
+
end
|
336
|
+
h
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def update_base_condition
|
341
|
+
self.c_base_condition = "#{c_target_class.table_name}.deleted_at IS NULL" if c_target_class.try :paranoid?
|
342
|
+
end
|
304
343
|
end
|
305
344
|
|
306
345
|
def initialize
|
@@ -535,18 +574,18 @@ module SimpleDrilldown
|
|
535
574
|
def populate_list(conditions, includes, result, values)
|
536
575
|
return result[:rows].each { |r| populate_list(conditions, includes, r, values + [r[:value]]) } if result[:rows]
|
537
576
|
|
538
|
-
list_includes = merge_includes(includes, c_list_includes)
|
577
|
+
list_includes = self.class.merge_includes(includes, c_list_includes)
|
539
578
|
@search.fields.each do |field|
|
540
579
|
field_def = c_fields[field.to_sym]
|
541
580
|
raise "Field definition missing for: #{field.inspect}" unless field_def
|
542
581
|
|
543
582
|
field_includes = field_def[:include]
|
544
|
-
list_includes = merge_includes(list_includes, field_includes) if field_includes
|
583
|
+
list_includes = self.class.merge_includes(list_includes, field_includes) if field_includes
|
545
584
|
end
|
546
585
|
if @search.list_change_times
|
547
586
|
@history_fields.each do |f|
|
548
587
|
if @search.fields.include? f
|
549
|
-
list_includes = merge_includes(list_includes, assignment: { order: :"#{f}_changes" })
|
588
|
+
list_includes = self.class.merge_includes(list_includes, assignment: { order: :"#{f}_changes" })
|
550
589
|
end
|
551
590
|
end
|
552
591
|
end
|
@@ -557,47 +596,6 @@ module SimpleDrilldown
|
|
557
596
|
result[:records] = base_query.to_a
|
558
597
|
end
|
559
598
|
|
560
|
-
def merge_includes(*args)
|
561
|
-
hash = hash_includes(*args)
|
562
|
-
result = hash.dup.map do |k, v|
|
563
|
-
if v.blank?
|
564
|
-
hash.delete(k)
|
565
|
-
k
|
566
|
-
end
|
567
|
-
end.compact
|
568
|
-
result << hash unless hash.blank?
|
569
|
-
case result.size
|
570
|
-
when 0
|
571
|
-
nil
|
572
|
-
when 1
|
573
|
-
result[0]
|
574
|
-
else
|
575
|
-
result
|
576
|
-
end
|
577
|
-
end
|
578
|
-
|
579
|
-
def hash_includes(*args)
|
580
|
-
args.inject({}) do |h, inc|
|
581
|
-
case inc
|
582
|
-
when Array
|
583
|
-
inc.each do |v|
|
584
|
-
h = hash_includes(h, v)
|
585
|
-
end
|
586
|
-
when Hash
|
587
|
-
inc.each do |k, v|
|
588
|
-
h[k] = merge_includes(h[k], v)
|
589
|
-
end
|
590
|
-
when NilClass, FalseClass
|
591
|
-
# Leave as it is
|
592
|
-
when String, Symbol
|
593
|
-
h[inc] ||= []
|
594
|
-
else
|
595
|
-
raise "Unknown include type: #{inc.inspect}"
|
596
|
-
end
|
597
|
-
h
|
598
|
-
end
|
599
|
-
end
|
600
|
-
|
601
599
|
def list_conditions(conditions, values)
|
602
600
|
conditions ||= ['']
|
603
601
|
|
@@ -31,7 +31,7 @@ module SimpleDrilldown
|
|
31
31
|
if result[:rows]
|
32
32
|
sub_headers = headers + [{
|
33
33
|
value: result[:value],
|
34
|
-
display_row_count: result[:nodes] + result[:row_count] * (@search.list ? 1 : 0)
|
34
|
+
display_row_count: result[:nodes] + (result[:row_count] * (@search.list ? 1 : 0))
|
35
35
|
}]
|
36
36
|
significant_rows = result[:rows].reject { |r| r[:row_count].zero? }
|
37
37
|
significant_rows.each_with_index do |r, i|
|
@@ -62,7 +62,7 @@ module SimpleDrilldown
|
|
62
62
|
else
|
63
63
|
headers + [{
|
64
64
|
value: result[:value],
|
65
|
-
display_row_count: result[:nodes] + result[:row_count] * (@search.list ? 1 : 0)
|
65
|
+
display_row_count: result[:nodes] + (result[:row_count] * (@search.list ? 1 : 0))
|
66
66
|
}]
|
67
67
|
end
|
68
68
|
else
|
@@ -89,9 +89,13 @@ module SimpleDrilldown
|
|
89
89
|
private
|
90
90
|
|
91
91
|
def caption_txt
|
92
|
-
|
93
|
-
|
94
|
-
|
92
|
+
class_txt = controller.c_target_class &&
|
93
|
+
I18n.t(controller.c_target_class.name.pluralize.to_sym,
|
94
|
+
default: [controller.c_target_class.name.downcase.pluralize.to_sym,
|
95
|
+
controller.c_target_class.name.pluralize])
|
96
|
+
value_txt = I18n.t(@search.select_value.downcase, default: @search.select_value.to_s.titleize)
|
97
|
+
dimensions_txt = " by #{@dimensions.map { |d| d[:pretty_name] }.join(' and ')}" if @dimensions&.any?
|
98
|
+
"#{class_txt} #{value_txt}#{dimensions_txt}"
|
95
99
|
end
|
96
100
|
end
|
97
101
|
end
|
@@ -47,7 +47,7 @@ module SimpleDrilldown
|
|
47
47
|
attributes = attributes_or_search
|
48
48
|
@default_fields = default_fields
|
49
49
|
@default_select_value = default_select_value
|
50
|
-
@dimensions = attributes && attributes[:dimensions] || []
|
50
|
+
@dimensions = (attributes && attributes[:dimensions]) || []
|
51
51
|
@dimensions.delete_if(&:empty?)
|
52
52
|
@filter = attributes && attributes[:filter] ? attributes[:filter] : {}
|
53
53
|
@filter.keys.dup.each { |k| @filter[k] = Array(@filter[k]) }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_drilldown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Uwe Kubosch
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: caxlsx_rails
|
@@ -137,10 +137,12 @@ files:
|
|
137
137
|
- config/locales/en.yml
|
138
138
|
- config/locales/nb.yml
|
139
139
|
- config/routes.rb
|
140
|
-
- lib/generators/
|
141
|
-
- lib/generators/
|
142
|
-
- lib/generators/
|
143
|
-
- lib/generators/
|
140
|
+
- lib/generators/simple_drilldown/controller/USAGE
|
141
|
+
- lib/generators/simple_drilldown/controller/controller_generator.rb
|
142
|
+
- lib/generators/simple_drilldown/controller/templates/drilldown_controller.rb.erb
|
143
|
+
- lib/generators/simple_drilldown/controller/templates/drilldown_controller_test.rb.erb
|
144
|
+
- lib/generators/simple_drilldown/views/USAGE
|
145
|
+
- lib/generators/simple_drilldown/views/views_generator.rb
|
144
146
|
- lib/simple_drilldown.rb
|
145
147
|
- lib/simple_drilldown/changes.rb
|
146
148
|
- lib/simple_drilldown/controller.rb
|
@@ -170,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
170
172
|
- !ruby/object:Gem::Version
|
171
173
|
version: '0'
|
172
174
|
requirements: []
|
173
|
-
rubygems_version: 3.
|
175
|
+
rubygems_version: 3.3.3
|
174
176
|
signing_key:
|
175
177
|
specification_version: 4
|
176
178
|
summary: Simple data warehouse and drilldown.
|
@@ -1,11 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class DrilldownControllerGenerator < Rails::Generators::NamedBase
|
4
|
-
source_root File.expand_path('templates', __dir__)
|
5
|
-
|
6
|
-
def copy_drilldown_controller_file
|
7
|
-
template 'drilldown_controller.rb.erb', "app/controllers/#{file_name}_drilldown_controller.rb"
|
8
|
-
template 'drilldown_controller_test.rb.erb', "test/controllers/#{file_name}_drilldown_controller_test.rb"
|
9
|
-
route "draw_drilldown :#{singular_name}"
|
10
|
-
end
|
11
|
-
end
|