simple_drilldown 0.3.5 → 0.6.3
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 +9 -6
- data/app/views/drilldown/_chart.html.erb +1 -1
- data/app/views/drilldown/_field.html.erb +0 -1
- data/app/views/drilldown/_fields.html.erb +1 -1
- data/app/views/drilldown/_filter.html.erb +4 -14
- data/app/views/drilldown/_record_list.html.erb +3 -3
- data/app/views/drilldown/_row.html.erb +9 -9
- data/app/views/drilldown/_row_header.html.erb +1 -1
- data/app/views/drilldown/_summary_table.html.erb +2 -2
- data/app/views/drilldown/excel_export_transactions.builder +1 -1
- data/lib/generators/drilldown_controller/USAGE +1 -0
- data/lib/generators/drilldown_controller/drilldown_controller_generator.rb +2 -1
- data/lib/generators/drilldown_controller/templates/drilldown_controller.rb.erb +14 -5
- data/lib/generators/drilldown_controller/templates/drilldown_controller_test.rb.erb +15 -0
- data/lib/simple_drilldown/{drilldown_controller.rb → controller.rb} +147 -92
- data/lib/simple_drilldown/engine.rb +6 -0
- data/lib/simple_drilldown/{drilldown_helper.rb → helper.rb} +2 -2
- data/lib/simple_drilldown/routing.rb +15 -0
- data/lib/simple_drilldown/version.rb +1 -1
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32063b0fd8dec6ee846e38eb37753d795acb2c6f3cca5d98a45d6c6a9c9bd868
|
4
|
+
data.tar.gz: 4c95e97c4780f82a6227a0f5e2048c7501d29e5534e7c74953746be915f42b1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b78736b952b5c7727ea627d83a4c47a72cf0665fded104fd6f65bb0f0adfc8b09a6899c4b832ce3a05991e05be2be8496ba6935ac24bc618af4492f7b997833b
|
7
|
+
data.tar.gz: e4f976920fcd12bd3820c390b22dd0d36af9a13f6d3f01517acb82574879d0196f4fc6fe7417a91a5a916a064d3187bc115119bfa1f16ea8954d53e9ee61b159
|
data/README.md
CHANGED
@@ -4,9 +4,9 @@
|
|
4
4
|
<img align="right" src="https://travis-ci.org/DatekWireless/simple_drilldown.svg?branch=master" alt="Build Status">
|
5
5
|
</a>
|
6
6
|
|
7
|
-
simple_drilldown offers a simple way to define axis to filter and group records
|
7
|
+
`simple_drilldown` offers a simple way to define axis to filter and group records
|
8
8
|
for analysis. The result is a record count for the selected filter and
|
9
|
-
distribution and the option to list the actual records.
|
9
|
+
distribution and the option to list and export the actual records.
|
10
10
|
|
11
11
|
## Usage
|
12
12
|
|
@@ -16,6 +16,10 @@ For a given schema:
|
|
16
16
|
|
17
17
|
```ruby
|
18
18
|
ActiveRecord::Schema.define(version: 20141204155251) do
|
19
|
+
create_table "users" do |t|
|
20
|
+
t.string "name", limit: 16, null: false
|
21
|
+
end
|
22
|
+
|
19
23
|
create_table "posts" do |t|
|
20
24
|
t.string "title", null: false
|
21
25
|
t.text "body", null: false
|
@@ -25,12 +29,9 @@ ActiveRecord::Schema.define(version: 20141204155251) do
|
|
25
29
|
t.datetime "updated_at", null: false
|
26
30
|
end
|
27
31
|
|
28
|
-
create_table "users" do |t|
|
29
|
-
t.string "name", limit: 16, null: false
|
30
|
-
end
|
31
|
-
|
32
32
|
create_table "comments" do |t|
|
33
33
|
t.integer "post_id", null: false
|
34
|
+
t.integer "user_id", null: false
|
34
35
|
t.string "title", null: false
|
35
36
|
t.text "body", null: false
|
36
37
|
t.integer "rating", null: false
|
@@ -65,6 +66,8 @@ end
|
|
65
66
|
Create a new controller to focus on posts. Each drilldown controller focuses on
|
66
67
|
one main entity.
|
67
68
|
|
69
|
+
bin/rails g drilldown_controller User
|
70
|
+
|
68
71
|
```ruby
|
69
72
|
class PostsDrilldownController < DrilldownController
|
70
73
|
|
@@ -37,7 +37,7 @@
|
|
37
37
|
<% (0..2).each do |i|
|
38
38
|
options = [['', '']]
|
39
39
|
options << [@dimensions[i][:pretty_name], @dimensions[i][:url_param_name]] if @dimensions[i]
|
40
|
-
options += @remaining_dimensions.keys.map { |name| [
|
40
|
+
options += @remaining_dimensions.keys.map { |name| [controller.c_dimension_defs[name][:pretty_name], name] } %>
|
41
41
|
<%= t(i == 0 ? :group_by : :then_by) %>:
|
42
42
|
<%= form.select 'dimensions', options, { :selected => @search.dimensions && @search.dimensions[i] },
|
43
43
|
{ :onChange => 'form.submit()', :name => 'search[dimensions][]', :id => "search_dimensions_#{i}" } %>
|
@@ -6,7 +6,7 @@
|
|
6
6
|
</style>
|
7
7
|
|
8
8
|
<% @transaction_fields.each do | field | %>
|
9
|
-
<%=form.label "fields[#{field}]", t(field), :class => "field_label" %>
|
9
|
+
<%=form.label "fields[#{field}]", t(field, default: :"attributes.#{field}"), :class => "field_label" %>
|
10
10
|
<%=form.check_box :fields, :id => "search_fields[#{field}]", :name => "search[fields][#{field}]", :checked => @search.fields.include?(field) %>
|
11
11
|
<br/>
|
12
12
|
<% end %>
|
@@ -3,18 +3,8 @@
|
|
3
3
|
<%= form.text_field :title, class: 'form-control' %>
|
4
4
|
</div>
|
5
5
|
|
6
|
-
<%
|
7
|
-
<%
|
8
|
-
|
9
|
-
|
10
|
-
<% ActiveRecord::Base.connection_pool.with_connection do %>
|
11
|
-
<% choices[dimension_name] = [[t(:all), nil]] + (dimension[:legal_values] && dimension[:legal_values].call(@search).map { |o| o.is_a?(Array) ? [o[0].to_s, o[1].to_s] : o.to_s } || []) %>
|
12
|
-
<% end %>
|
13
|
-
<% end %>
|
14
|
-
<% next [t, dimension_name] %>
|
15
|
-
<% end %>
|
16
|
-
<% threads.each do |t, dimension_name| %>
|
17
|
-
<% t.join %>
|
18
|
-
<%= render partial: 'drilldown/field', locals: {choices: choices[dimension_name] || [],
|
19
|
-
form: form, dimension_name: dimension_name} %>
|
6
|
+
<% controller.c_dimension_defs.each do |dimension_name, dimension| %>
|
7
|
+
<% choices = [[t(:all), nil]] + (dimension[:legal_values] && dimension[:legal_values].call(@search).map { |o| o.is_a?(Array) ? [o[0].to_s, o[1].to_s] : o.to_s } || []) %>
|
8
|
+
<%= render partial: 'drilldown/field', locals: { choices: choices || [],
|
9
|
+
form: form, dimension_name: dimension_name } %>
|
20
10
|
<% end %>
|
@@ -1,10 +1,10 @@
|
|
1
1
|
<% unless result[:transactions].empty? %>
|
2
2
|
<tr>
|
3
|
-
<td colspan="
|
3
|
+
<td colspan="<%= controller.c_summary_fields.size + 1 %>">
|
4
4
|
<table class="table table-condensed table-bordered" style="padding-bottom: 10px;">
|
5
|
-
<%=render :partial => '/drilldown/row_header' %>
|
5
|
+
<%= render :partial => '/drilldown/row_header' %>
|
6
6
|
<% result[:transactions].each do |t| %>
|
7
|
-
<%=render :partial => '/drilldown/row', :locals => {:transaction => t, :previous_transaction => nil, :errors => [], :error_row => false, :meter1_errors => false} %>
|
7
|
+
<%= render :partial => '/drilldown/row', :locals => { :transaction => t, :previous_transaction => nil, :errors => [], :error_row => false, :meter1_errors => false } %>
|
8
8
|
<% end %>
|
9
9
|
</table>
|
10
10
|
</td>
|
@@ -1,13 +1,13 @@
|
|
1
1
|
<tr valign="top">
|
2
2
|
<% @search.fields.each do |field| %>
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
<td>
|
4
|
+
<% if field == 'time' %>
|
5
|
+
<%= (transaction.respond_to?(:completed_at) ? transaction.completed_at : transaction.created_at).localtime.strftime('%Y-%m-%d %H:%M') %>
|
6
|
+
<% else %>
|
7
|
+
<% field_def = controller.c_fields[field.to_sym] %>
|
8
|
+
<%= field_def[:attr_method] ? field_def[:attr_method].call(transaction) : transaction.send(field) %>
|
9
|
+
<% end %>
|
10
|
+
</td>
|
11
11
|
<% end %>
|
12
|
-
<td><%=
|
12
|
+
<td><%= link_to t(:show), transaction %></td>
|
13
13
|
</tr>
|
@@ -3,8 +3,8 @@
|
|
3
3
|
<% @dimensions.each do |d| %>
|
4
4
|
<th><%=h d[:pretty_name]%></th>
|
5
5
|
<% end %>
|
6
|
-
<th><%= t
|
7
|
-
<%=
|
6
|
+
<th><%= t controller.c_target_class.table_name.capitalize %></th>
|
7
|
+
<%= controller.c_summary_fields.map{|l| "<th>#{t(l)}</th>"}.join("\n").html_safe %>
|
8
8
|
</tr>
|
9
9
|
|
10
10
|
<%=summary_row(@result) %>
|
@@ -45,7 +45,7 @@ xml.Workbook(
|
|
45
45
|
@transactions.each do |transaction|
|
46
46
|
xml.Row do
|
47
47
|
@transaction_fields.each do |field|
|
48
|
-
field_map =
|
48
|
+
field_map = controller.c_fields[field.to_sym]
|
49
49
|
if field == 'time'
|
50
50
|
xml.Cell 'ss:StyleID' => 'DateOnlyFormat' do
|
51
51
|
xml.Data transaction.completed_at.gmtime.xmlschema, 'ss:Type' => 'DateTime'
|
@@ -5,6 +5,7 @@ class DrilldownControllerGenerator < Rails::Generators::NamedBase
|
|
5
5
|
|
6
6
|
def copy_drilldown_controller_file
|
7
7
|
template 'drilldown_controller.rb.erb', "app/controllers/#{file_name}_drilldown_controller.rb"
|
8
|
-
|
8
|
+
template 'drilldown_controller_test.rb.erb', "test/controllers/#{file_name}_drilldown_controller_test.rb"
|
9
|
+
route "draw_drilldown :#{singular_name}_drilldown"
|
9
10
|
end
|
10
11
|
end
|
@@ -1,14 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
# What fields should be displayed as default when listing actual <%= class_name %> records.
|
5
|
-
default_fields %w[created_at updated_at] # TODO(uwe): Read fields from schema?
|
3
|
+
require 'simple_drilldown/controller'
|
6
4
|
|
5
|
+
class <%= class_name %>DrilldownController < SimpleDrilldown::Controller
|
7
6
|
# The main focus of the drilldown
|
8
|
-
target_class <%= class_name %>
|
7
|
+
# target_class <%= class_name %>
|
8
|
+
|
9
|
+
# `where` clause for the base line
|
10
|
+
# base_condition '1=1'
|
9
11
|
|
10
12
|
# How should we count the reords?
|
11
|
-
select 'count(*) as count'
|
13
|
+
# select 'count(*) as count'
|
14
|
+
|
15
|
+
# When selecting records, what relations should be included for optimization?
|
16
|
+
# Other relations can be included for specific dimensions and fields.
|
17
|
+
# base_includes :user, :comments # TODO(uwe): Read relations from schema?
|
18
|
+
|
19
|
+
# What fields should be displayed as default when listing actual <%= class_name %> records.
|
20
|
+
default_fields %w[created_at updated_at] # TODO(uwe): Read fields from schema?
|
12
21
|
|
13
22
|
# When listing records, what relations should be included for optimization?
|
14
23
|
# list_includes :user, :comments # TODO(uwe): Read relations from schema?
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class <%= class_name %>DrilldownControllerTest < ActionDispatch::IntegrationTest
|
6
|
+
test 'should get index' do
|
7
|
+
get <%= singular_name %>_drilldown_url
|
8
|
+
assert_response :success
|
9
|
+
end
|
10
|
+
|
11
|
+
test 'should get index with list' do
|
12
|
+
get <%= singular_name %>_drilldown_url search: { list: 1 }
|
13
|
+
assert_response :success
|
14
|
+
end
|
15
|
+
end
|
@@ -1,54 +1,84 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'simple_drilldown/
|
3
|
+
require 'simple_drilldown/helper'
|
4
4
|
require 'simple_drilldown/search'
|
5
5
|
|
6
6
|
module SimpleDrilldown
|
7
|
-
class
|
8
|
-
helper
|
7
|
+
class Controller < ::ApplicationController
|
8
|
+
helper Helper
|
9
9
|
|
10
10
|
LIST_LIMIT = 10_000
|
11
11
|
|
12
|
+
class_attribute :c_base_condition, default: '1=1'
|
13
|
+
class_attribute :c_base_group, default: []
|
14
|
+
class_attribute :c_base_includes, default: []
|
15
|
+
class_attribute :c_default_fields, default: []
|
16
|
+
class_attribute :c_default_select_value, default: SimpleDrilldown::Search::SelectValue::COUNT
|
17
|
+
class_attribute :c_dimension_defs
|
18
|
+
class_attribute :c_fields
|
19
|
+
class_attribute :c_list_includes, default: []
|
20
|
+
class_attribute :c_list_order
|
21
|
+
class_attribute :c_select, default: 'count(*) as count'
|
22
|
+
class_attribute :c_summary_fields, default: []
|
23
|
+
class_attribute :c_target_class
|
24
|
+
|
12
25
|
class << self
|
26
|
+
def inherited(base)
|
27
|
+
super
|
28
|
+
base.c_dimension_defs = Concurrent::Hash.new
|
29
|
+
base.c_fields = {}
|
30
|
+
begin
|
31
|
+
base.c_target_class = base.name.chomp('DrilldownController').constantize
|
32
|
+
rescue NameError
|
33
|
+
begin
|
34
|
+
base.c_target_class = base.name.chomp('Controller').constantize
|
35
|
+
rescue NameError
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
13
40
|
def base_condition(base_condition)
|
14
|
-
|
41
|
+
self.c_base_condition = base_condition
|
15
42
|
end
|
16
43
|
|
17
44
|
def base_includes(base_includes)
|
18
|
-
|
45
|
+
self.c_base_includes = base_includes
|
19
46
|
end
|
20
47
|
|
21
48
|
def base_group(base_group)
|
22
|
-
|
49
|
+
self.c_base_group = base_group
|
50
|
+
end
|
51
|
+
|
52
|
+
def default_fields(default_fields)
|
53
|
+
self.c_default_fields = default_fields.flatten
|
23
54
|
end
|
24
55
|
|
25
|
-
def
|
26
|
-
|
56
|
+
def default_select_value(default_select_value)
|
57
|
+
self.c_default_select_value = default_select_value
|
27
58
|
end
|
28
59
|
|
29
60
|
def target_class(target_class)
|
30
|
-
|
61
|
+
self.c_target_class = target_class
|
31
62
|
end
|
32
63
|
|
33
64
|
def select(select)
|
34
|
-
|
65
|
+
self.c_select = select
|
35
66
|
end
|
36
67
|
|
37
68
|
def list_includes(list_includes)
|
38
|
-
|
69
|
+
self.c_list_includes = list_includes.flatten
|
39
70
|
end
|
40
71
|
|
41
72
|
def list_order(list_order)
|
42
|
-
|
73
|
+
self.c_list_order = list_order
|
43
74
|
end
|
44
75
|
|
45
76
|
def field(name, **options)
|
46
|
-
|
47
|
-
@@fields[name] = options
|
77
|
+
c_fields[name] = options
|
48
78
|
end
|
49
79
|
|
50
80
|
def summary_fields(*summary_fields)
|
51
|
-
|
81
|
+
self.c_summary_fields = summary_fields.flatten
|
52
82
|
end
|
53
83
|
|
54
84
|
def dimension(name, select_expression = name, options = {})
|
@@ -74,15 +104,20 @@ module SimpleDrilldown
|
|
74
104
|
raise "Unknown options: #{query_opts.keys.inspect}" unless (query_opts.keys - %i[select includes where]).empty?
|
75
105
|
end
|
76
106
|
|
77
|
-
|
78
|
-
|
79
|
-
@@dimension_defs[name.to_s] = {
|
80
|
-
includes: queries.inject([]) do |a, e|
|
107
|
+
c_dimension_defs[name.to_s] = {
|
108
|
+
includes: queries.inject(nil) do |a, e|
|
81
109
|
i = e[:includes]
|
82
110
|
next a unless i
|
83
|
-
next a if a
|
111
|
+
next a if a&.include?(i)
|
84
112
|
|
85
|
-
a
|
113
|
+
case a
|
114
|
+
when nil
|
115
|
+
i
|
116
|
+
when Symbol
|
117
|
+
[a, *i]
|
118
|
+
else
|
119
|
+
a.concat(*i)
|
120
|
+
end
|
86
121
|
end,
|
87
122
|
interval: interval,
|
88
123
|
label_method: label_method,
|
@@ -101,7 +136,7 @@ module SimpleDrilldown
|
|
101
136
|
my_filter = search.filter.dup
|
102
137
|
my_filter.delete(field.to_s) unless preserve_filter
|
103
138
|
filter_conditions, _t, includes = make_conditions(my_filter)
|
104
|
-
dimension_def =
|
139
|
+
dimension_def = c_dimension_defs[field.to_s]
|
105
140
|
result_sets = dimension_def[:queries].map do |query|
|
106
141
|
if query[:includes]
|
107
142
|
if query[:includes].is_a?(Array)
|
@@ -111,11 +146,11 @@ module SimpleDrilldown
|
|
111
146
|
end
|
112
147
|
includes.uniq!
|
113
148
|
end
|
114
|
-
rows =
|
149
|
+
rows = c_target_class.unscoped.where(c_base_condition)
|
115
150
|
.select("#{query[:select]} AS value")
|
116
|
-
.where(filter_conditions)
|
117
|
-
.where(query[:where])
|
118
|
-
.joins(make_join([],
|
151
|
+
.where(filter_conditions || '1=1')
|
152
|
+
.where(query[:where] || '1=1')
|
153
|
+
.joins(make_join([], c_target_class.name.underscore.to_sym, includes))
|
119
154
|
.order('value')
|
120
155
|
.group(:value)
|
121
156
|
.to_a
|
@@ -137,14 +172,14 @@ module SimpleDrilldown
|
|
137
172
|
end
|
138
173
|
|
139
174
|
def make_conditions(search_filter)
|
140
|
-
includes =
|
175
|
+
includes = c_base_includes.dup
|
141
176
|
if search_filter
|
142
177
|
condition_strings = []
|
143
178
|
condition_values = []
|
144
179
|
|
145
180
|
filter_texts = []
|
146
181
|
search_filter.each do |field, values|
|
147
|
-
dimension_def =
|
182
|
+
dimension_def = c_dimension_defs[field]
|
148
183
|
raise "Unknown filter field: #{field.inspect}" if dimension_def.nil?
|
149
184
|
|
150
185
|
values = [*values]
|
@@ -202,9 +237,9 @@ module SimpleDrilldown
|
|
202
237
|
when Hash
|
203
238
|
sql = +''
|
204
239
|
include.each do |parent, child|
|
205
|
-
sql << make_join(joins, model, parent)
|
240
|
+
sql << ' ' + make_join(joins, model, parent)
|
206
241
|
ass = model.to_s.camelize.constantize.reflect_on_association parent
|
207
|
-
sql << make_join(joins, parent, child, ass.class_name.constantize)
|
242
|
+
sql << ' ' + make_join(joins, parent, child, ass.class_name.constantize)
|
208
243
|
end
|
209
244
|
sql
|
210
245
|
when Symbol
|
@@ -254,24 +289,7 @@ module SimpleDrilldown
|
|
254
289
|
|
255
290
|
def initialize
|
256
291
|
super()
|
257
|
-
@
|
258
|
-
@default_fields = @@default_fields
|
259
|
-
@default_select_value = SimpleDrilldown::Search::SelectValue::COUNT
|
260
|
-
@target_class = @@target_class
|
261
|
-
@select = @@select
|
262
|
-
@@base_condition = '1 = 1' unless defined?(@@base_condition)
|
263
|
-
@base_condition = @@base_condition
|
264
|
-
@@base_includes = [] unless defined?(@@base_includes)
|
265
|
-
@base_includes = @@base_includes
|
266
|
-
@base_group = defined?(@@base_group) ? @@base_group : []
|
267
|
-
@@list_includes = [] unless defined?(@@list_includes)
|
268
|
-
@list_includes = @@list_includes
|
269
|
-
@list_order = @@list_order
|
270
|
-
@dimension_defs = @@dimension_defs
|
271
|
-
@@summary_fields = [] unless defined?(@@summary_fields)
|
272
|
-
@summary_fields = @@summary_fields
|
273
|
-
|
274
|
-
@history_fields = @fields.select { |_k, v| v[:list_change_times] }.map { |k, _v| k.to_s }
|
292
|
+
@history_fields = c_fields.select { |_k, v| v[:list_change_times] }.map { |k, _v| k.to_s }
|
275
293
|
end
|
276
294
|
|
277
295
|
# ?dimension[0]=supplier&dimension[1]=transaction_type&
|
@@ -279,18 +297,17 @@ module SimpleDrilldown
|
|
279
297
|
def index(do_render = true)
|
280
298
|
@search = new_search_object
|
281
299
|
|
282
|
-
@transaction_fields = (@search.fields + (
|
283
|
-
@transaction_fields_map = @fields
|
300
|
+
@transaction_fields = (@search.fields + (c_fields.keys.map(&:to_s) - @search.fields))
|
284
301
|
|
285
|
-
select =
|
286
|
-
includes =
|
302
|
+
select = c_select.dup
|
303
|
+
includes = c_base_includes.dup
|
287
304
|
|
288
305
|
@dimensions = []
|
289
|
-
select << ", 'All'::text as value0"
|
306
|
+
select << ", 'All'#{'::text' if c_target_class.connection.adapter_name == 'PostgreSQL'} as value0"
|
290
307
|
@dimensions += @search.dimensions.map do |dn|
|
291
|
-
raise "Unknown distribution field: #{dn.inspect}" if
|
308
|
+
raise "Unknown distribution field: #{dn.inspect}" if c_dimension_defs[dn].nil?
|
292
309
|
|
293
|
-
|
310
|
+
c_dimension_defs[dn]
|
294
311
|
end
|
295
312
|
@dimensions.each_with_index do |d, i|
|
296
313
|
select << ", #{d[:select_expression]} as value#{i + 1}"
|
@@ -315,18 +332,18 @@ module SimpleDrilldown
|
|
315
332
|
order = (1..@dimensions.size).map { |i| "value#{i}" }.join(',')
|
316
333
|
order = nil if order.empty?
|
317
334
|
end
|
318
|
-
group = (
|
335
|
+
group = (c_base_group + (1..@dimensions.size).map { |i| "value#{i}" }).join(',')
|
319
336
|
group = nil if group.empty?
|
320
337
|
|
321
|
-
joins = self.class.make_join([],
|
322
|
-
rows =
|
323
|
-
|
324
|
-
|
325
|
-
|
338
|
+
joins = self.class.make_join([], c_target_class.name.underscore.to_sym, includes)
|
339
|
+
rows = c_target_class.unscoped.where(c_base_condition).select(select).where(conditions)
|
340
|
+
.joins(joins)
|
341
|
+
.group(group)
|
342
|
+
.order(order).to_a
|
326
343
|
|
327
344
|
if rows.empty?
|
328
345
|
@result = { value: 'All', count: 0, row_count: 0, nodes: 0, rows: [] }
|
329
|
-
|
346
|
+
c_summary_fields.each { |f| @result[f] = 0 }
|
330
347
|
else
|
331
348
|
if do_render && @search.list && rows.inject(0) { |sum, r| sum + r[:count].to_i } > LIST_LIMIT
|
332
349
|
@search.list = false
|
@@ -335,9 +352,9 @@ module SimpleDrilldown
|
|
335
352
|
@result = result_from_rows(rows, 0, 0, ['All'])
|
336
353
|
end
|
337
354
|
|
338
|
-
remove_duplicates(@result) unless
|
355
|
+
remove_duplicates(@result) unless c_base_group.empty?
|
339
356
|
|
340
|
-
@remaining_dimensions =
|
357
|
+
@remaining_dimensions = c_dimension_defs.dup
|
341
358
|
@remaining_dimensions.each_key do |dim_name|
|
342
359
|
if (@search.filter[dim_name] && @search.filter[dim_name].size == 1) ||
|
343
360
|
(@dimensions.any? { |d| d[:url_param_name] == dim_name })
|
@@ -352,9 +369,9 @@ module SimpleDrilldown
|
|
352
369
|
def choices
|
353
370
|
@search = new_search_object
|
354
371
|
dimension_name = params[:dimension_name]
|
355
|
-
dimension =
|
372
|
+
dimension = c_dimension_defs[dimension_name]
|
356
373
|
selected = @search.filter[dimension_name] || []
|
357
|
-
raise "Unknown dimension #{dimension_name.inspect}: #{
|
374
|
+
raise "Unknown dimension #{dimension_name.inspect}: #{c_dimension_defs.keys.inspect}" unless dimension
|
358
375
|
|
359
376
|
choices = [[t(:all), nil]] +
|
360
377
|
(dimension[:legal_values]&.call(@search)&.map { |o| o.is_a?(Array) ? o[0..1].map(&:to_s) : o.to_s } || [])
|
@@ -398,7 +415,7 @@ module SimpleDrilldown
|
|
398
415
|
private
|
399
416
|
|
400
417
|
def new_search_object
|
401
|
-
SimpleDrilldown::Search.new(params[:search]&.to_unsafe_h,
|
418
|
+
SimpleDrilldown::Search.new(params[:search]&.to_unsafe_h, c_default_fields, c_default_select_value)
|
402
419
|
end
|
403
420
|
|
404
421
|
def remove_duplicates(result)
|
@@ -411,7 +428,7 @@ module SimpleDrilldown
|
|
411
428
|
if prev_row
|
412
429
|
if prev_row[:value] == r[:value]
|
413
430
|
prev_row[:count] += r[:count]
|
414
|
-
|
431
|
+
c_summary_fields.each do |f|
|
415
432
|
prev_row[f] += r[f]
|
416
433
|
end
|
417
434
|
prev_row[:row_count] = [prev_row[:row_count], r[:row_count]].max
|
@@ -449,7 +466,7 @@ module SimpleDrilldown
|
|
449
466
|
row_count: 0,
|
450
467
|
nodes: 0
|
451
468
|
}
|
452
|
-
|
469
|
+
c_summary_fields.each { |f| sub_result[f] = 0 }
|
453
470
|
sub_result[:rows] = add_zero_results([], dimension + 1) if dimension < @dimensions.size - 1
|
454
471
|
result_rows << sub_result
|
455
472
|
end
|
@@ -472,7 +489,7 @@ module SimpleDrilldown
|
|
472
489
|
row_count: 1,
|
473
490
|
nodes: @search.list ? 2 : 1
|
474
491
|
}
|
475
|
-
|
492
|
+
c_summary_fields.each { |f| result[f] = row[f].to_i }
|
476
493
|
return result
|
477
494
|
end
|
478
495
|
|
@@ -495,37 +512,75 @@ module SimpleDrilldown
|
|
495
512
|
nodes: result_rows.inject(0) { |t, r| t + r[:nodes] } + 1,
|
496
513
|
rows: result_rows
|
497
514
|
}
|
498
|
-
|
515
|
+
c_summary_fields.each { |f| result[f] = result_rows.inject(0) { |t, r| t + r[f] } }
|
499
516
|
result
|
500
517
|
end
|
501
518
|
|
502
519
|
def populate_list(conditions, includes, result, values)
|
503
520
|
if result[:rows]
|
504
|
-
result[:rows].each
|
505
|
-
|
521
|
+
return result[:rows].each { |r| populate_list(conditions, includes, r, values + [r[:value]]) }
|
522
|
+
end
|
523
|
+
list_includes = merge_includes(includes, c_list_includes)
|
524
|
+
@search.fields.each do |field|
|
525
|
+
field_def = c_fields[field.to_sym]
|
526
|
+
raise "Field definition missing for: #{field.inspect}" unless field_def
|
527
|
+
|
528
|
+
field_includes = field_def[:include]
|
529
|
+
if field_includes
|
530
|
+
list_includes = merge_includes(list_includes , field_includes)
|
506
531
|
end
|
507
|
-
|
508
|
-
|
509
|
-
@
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
field_includes = field_def[:include]
|
514
|
-
if field_includes
|
515
|
-
list_includes += field_includes.is_a?(Array) ? field_includes : [field_includes]
|
532
|
+
end
|
533
|
+
if @search.list_change_times
|
534
|
+
@history_fields.each do |f|
|
535
|
+
if @search.fields.include? f
|
536
|
+
list_includes = merge_includes(list_includes, assignment: { order: :"#{f}_changes" } )
|
516
537
|
end
|
517
538
|
end
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
539
|
+
end
|
540
|
+
joins = self.class.make_join([], c_target_class.name.underscore.to_sym, list_includes)
|
541
|
+
list_conditions = list_conditions(conditions, values)
|
542
|
+
base_query = c_target_class.unscoped.where(c_base_condition).joins(joins).order(c_list_order)
|
543
|
+
base_query = base_query.where(list_conditions) if list_conditions
|
544
|
+
result[:transactions] = base_query.to_a
|
545
|
+
end
|
546
|
+
|
547
|
+
def merge_includes(*args)
|
548
|
+
hash = hash_includes(*args)
|
549
|
+
result = hash.dup.map { |k, v|
|
550
|
+
if v.blank?
|
551
|
+
hash.delete(k)
|
552
|
+
k
|
553
|
+
end
|
554
|
+
}.compact
|
555
|
+
result << hash unless hash.blank?
|
556
|
+
case result.size
|
557
|
+
when 0
|
558
|
+
nil
|
559
|
+
when 1
|
560
|
+
result[0]
|
561
|
+
else
|
562
|
+
result
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
def hash_includes(*args)
|
567
|
+
args.inject({}) do |h, inc|
|
568
|
+
case inc
|
569
|
+
when Array
|
570
|
+
inc.each { |v|
|
571
|
+
h = hash_includes(h, v)
|
572
|
+
}
|
573
|
+
when Hash
|
574
|
+
inc.each { |k, v|
|
575
|
+
h[k] = merge_includes(h[k], v)
|
576
|
+
}
|
577
|
+
when NilClass, FalseClass
|
578
|
+
when String, Symbol
|
579
|
+
h[inc] ||= []
|
580
|
+
else
|
581
|
+
raise "Unknown include type: #{inc.inspect}"
|
523
582
|
end
|
524
|
-
|
525
|
-
list_conditions = list_conditions(conditions, values)
|
526
|
-
base_query = @target_class.unscoped.where(@base_condition).joins(joins).order(@list_order)
|
527
|
-
base_query = base_query.where(list_conditions) if list_conditions
|
528
|
-
result[:transactions] = base_query.to_a
|
583
|
+
h
|
529
584
|
end
|
530
585
|
end
|
531
586
|
|
@@ -1,11 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'chartkick'
|
4
|
+
require 'simple_drilldown/routing'
|
5
|
+
|
3
6
|
module SimpleDrilldown
|
4
7
|
class Engine < ::Rails::Engine
|
5
8
|
isolate_namespace SimpleDrilldown
|
9
|
+
config.autoload_paths << File.dirname(__dir__)
|
6
10
|
|
7
11
|
initializer 'simple_drilldown.assets.precompile' do |app|
|
8
12
|
app.config.assets.precompile += %w[chartkick.js]
|
9
13
|
end
|
14
|
+
|
15
|
+
ActionDispatch::Routing::Mapper.include SimpleDrilldown::Routing
|
10
16
|
end
|
11
17
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module SimpleDrilldown
|
4
4
|
# View helper for SimpleDrilldown
|
5
|
-
module
|
5
|
+
module Helper
|
6
6
|
def value_label(dimension_index, value)
|
7
7
|
dimension = @dimensions[dimension_index]
|
8
8
|
return nil if dimension.nil?
|
@@ -11,7 +11,7 @@ module SimpleDrilldown
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def caption
|
14
|
-
result = @search.title || "#{
|
14
|
+
result = @search.title || "#{controller.c_target_class} #{t(@search.select_value.downcase)}" +
|
15
15
|
(@dimensions && @dimensions.any? ? ' by ' + @dimensions.map { |d| d[:pretty_name] }.join(' and ') : '')
|
16
16
|
result.gsub('$date', [*@search.filter[:calendar_date]].uniq.join(' - '))
|
17
17
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleDrilldown
|
4
|
+
# Routing helper methods
|
5
|
+
module Routing
|
6
|
+
def draw_drilldown(path, controller = path)
|
7
|
+
get "#{path}(.:format)" => "#{controller}#index", as: path
|
8
|
+
scope path do
|
9
|
+
%i[choices excel_export html_export index].each do |action|
|
10
|
+
get "#{action}(/:id)(.:format)", controller: controller, action: action
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
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.3
|
4
|
+
version: 0.6.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Uwe Kubosch
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chartkick
|
@@ -123,10 +123,12 @@ files:
|
|
123
123
|
- lib/generators/drilldown_controller/USAGE
|
124
124
|
- lib/generators/drilldown_controller/drilldown_controller_generator.rb
|
125
125
|
- lib/generators/drilldown_controller/templates/drilldown_controller.rb.erb
|
126
|
+
- lib/generators/drilldown_controller/templates/drilldown_controller_test.rb.erb
|
126
127
|
- lib/simple_drilldown.rb
|
127
|
-
- lib/simple_drilldown/
|
128
|
-
- lib/simple_drilldown/drilldown_helper.rb
|
128
|
+
- lib/simple_drilldown/controller.rb
|
129
129
|
- lib/simple_drilldown/engine.rb
|
130
|
+
- lib/simple_drilldown/helper.rb
|
131
|
+
- lib/simple_drilldown/routing.rb
|
130
132
|
- lib/simple_drilldown/search.rb
|
131
133
|
- lib/simple_drilldown/version.rb
|
132
134
|
- lib/tasks/simple_drilldown_tasks.rake
|
@@ -135,7 +137,7 @@ licenses:
|
|
135
137
|
- MIT
|
136
138
|
metadata:
|
137
139
|
allowed_push_host: https://rubygems.org/
|
138
|
-
post_install_message:
|
140
|
+
post_install_message:
|
139
141
|
rdoc_options: []
|
140
142
|
require_paths:
|
141
143
|
- lib
|
@@ -151,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
151
153
|
version: '0'
|
152
154
|
requirements: []
|
153
155
|
rubygems_version: 3.1.2
|
154
|
-
signing_key:
|
156
|
+
signing_key:
|
155
157
|
specification_version: 4
|
156
158
|
summary: Simple data warehouse and drilldown.
|
157
159
|
test_files: []
|