simple_drilldown 0.7.4 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/views/drilldown/_chart.html.erb +29 -28
- data/app/views/drilldown/_excel_record_list_xlsx.xlsx.axlsx +45 -0
- data/app/views/drilldown/_excel_summary_row_xlsx.xlsx.axlsx +31 -0
- data/app/views/drilldown/_excel_summary_total_row_xlsx.xlsx.axlsx +7 -0
- data/app/views/drilldown/_export_links.html.erb +1 -0
- data/app/views/drilldown/_field.html.erb +12 -20
- data/app/views/drilldown/_record_list.html.erb +6 -4
- data/app/views/drilldown/_summary_table.html.erb +13 -11
- data/app/views/drilldown/excel_export_xlsx.xlsx.axlsx +86 -0
- data/app/views/layouts/simple_drilldown/application.html.erb +9 -12
- data/lib/simple_drilldown.rb +1 -0
- data/lib/simple_drilldown/changes.rb +25 -0
- data/lib/simple_drilldown/controller.rb +34 -26
- data/lib/simple_drilldown/engine.rb +1 -1
- data/lib/simple_drilldown/helper.rb +2 -2
- data/lib/simple_drilldown/routing.rb +4 -1
- data/lib/simple_drilldown/search.rb +9 -9
- data/lib/simple_drilldown/version.rb +1 -1
- metadata +24 -7
- data/app/views/drilldown/_excel_record_list.builder +0 -10
- data/app/views/drilldown/_excel_row.builder +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1987db21273c12d879bae55633fd337003d7ced07c152a09e9269f88d8928af
|
4
|
+
data.tar.gz: b6e9149cbcc597a3e407aac96936c77956c9489d87a4ec14beb225d6e993cbc4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7325de41bc98a3b712d8a9cdf4c52099208d35e1026cff81a4c75be4bd2e33933000bc6f0df9aceb36ce0979826cc3819fac444f0f1ec1626c964aa31aca167
|
7
|
+
data.tar.gz: a2185eeda0623ece2aca690e92419cdc7821f18321e4c4cd6a7d02b4159ba0e970739f5df1928661d244d84b9eaacd1371974dbaeb84d618718fb4b4a2b7e71d
|
@@ -1,19 +1,19 @@
|
|
1
1
|
<%
|
2
2
|
data =
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
-
when 3
|
3
|
+
case @dimensions.size
|
4
|
+
when 0
|
5
|
+
{ @result[:value] => @result[:count] }
|
6
|
+
when 1
|
7
|
+
@result[:rows].map { |r| [@dimensions[0][:label_method] ? @dimensions[0][:label_method].call(r[:value]) : r[:value], r[:count]] }
|
8
|
+
when 2
|
9
|
+
@result[:rows].map do |r|
|
10
|
+
{
|
11
|
+
name: r[:value],
|
12
|
+
data: r[:rows].map { |r2| [r2[:value], r2[:count]] }
|
13
|
+
}
|
16
14
|
end
|
15
|
+
when 3
|
16
|
+
end
|
17
17
|
%>
|
18
18
|
|
19
19
|
<%
|
@@ -37,10 +37,10 @@
|
|
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| [controller.c_dimension_defs[name][:pretty_name], 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}" } %>
|
44
44
|
<% end %>
|
45
45
|
|
46
46
|
<br/>
|
@@ -62,21 +62,22 @@
|
|
62
62
|
</div>
|
63
63
|
|
64
64
|
<% if @dimensions.size == 1 &&
|
65
|
-
|
65
|
+
[SimpleDrilldown::Search::DisplayType::BAR, SimpleDrilldown::Search::DisplayType::PIE].include?(@search.display_type) %>
|
66
66
|
<%= javascript_tag do %>
|
67
67
|
$('#chart-1').on('click', function(e){
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
68
|
+
chart = Chartkick.charts['chart-1'].getChartObject();
|
69
|
+
firstPoint = chart.getElementsAtEvent(e)[0]
|
70
|
+
if (firstPoint) {
|
71
|
+
label = chart.data.labels[firstPoint._index];
|
72
|
+
value = chart.data.datasets[firstPoint._datasetIndex].data[firstPoint._index];
|
73
|
+
console.log("Label: " + label + ", Value: " + value);
|
74
|
+
new_location = window.location.toString();
|
75
|
+
new_location = new_location.replace(/#.*$/, '');
|
76
|
+
new_location = new_location.replace("&search[dimensions][]=<%= @dimensions[0][:url_param_name] %>", '');
|
77
|
+
new_location = new_location + '&search[filter][<%= @dimensions[0][:url_param_name] %>][]=' + label;
|
78
|
+
console.log(new_location);
|
79
|
+
window.location = new_location;
|
80
|
+
}
|
80
81
|
});
|
81
82
|
<% end %>
|
82
83
|
<% end %>
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
def excel_header_row(sheet)
|
4
|
+
padding_cells = @dimensions.empty? ? 1 : @dimensions.size
|
5
|
+
row = (1...(padding_cells - 1)).map { |_n| nil }
|
6
|
+
@search.fields.each_with_index { |field, _i| row << t(field) }
|
7
|
+
sheet.add_row row
|
8
|
+
end
|
9
|
+
|
10
|
+
def excel_row(sheet, wrap, transaction:)
|
11
|
+
padding_cells = @dimensions.empty? ? 1 : @dimensions.size
|
12
|
+
row = (1..(padding_cells - 1)).map { |_n| nil }
|
13
|
+
|
14
|
+
@search.fields.each_with_index do |field, _i|
|
15
|
+
value = if field == 'time'
|
16
|
+
(transaction.respond_to?(:completed_at) ? transaction.completed_at : transaction.created_at)
|
17
|
+
.localtime.strftime('%Y-%m-%d %H:%M')
|
18
|
+
elsif controller.c_fields[field.to_sym][:attr_method]
|
19
|
+
controller.c_fields[field.to_sym][:attr_method].call(transaction)
|
20
|
+
else
|
21
|
+
transaction.send(field)
|
22
|
+
end
|
23
|
+
|
24
|
+
field_def = controller.c_fields[field.to_sym]
|
25
|
+
if @search.list_change_times && field_def[:list_change_times] && transaction.assignment.try(:order)
|
26
|
+
changes = transaction.assignment.order.send("#{field}_changes")
|
27
|
+
.map do |al|
|
28
|
+
[al.created_at.localtime.strftime('%H:%M'),
|
29
|
+
PacMan.yaml_load(al.new_values).symbolize_keys[field.to_sym]]
|
30
|
+
end
|
31
|
+
last_change = changes.last.try(:[], 1)
|
32
|
+
value =
|
33
|
+
"#{changes.map { |al| al.join(' ') }.join("\n")}#{"\nActual: #{value}" if value != last_change}"
|
34
|
+
end
|
35
|
+
|
36
|
+
row << value
|
37
|
+
end
|
38
|
+
sheet.add_row row, style: wrap
|
39
|
+
end
|
40
|
+
|
41
|
+
sheet.add_row []
|
42
|
+
excel_header_row(sheet)
|
43
|
+
wrap = sheet.styles.add_style alignment: { wrap_text: true }
|
44
|
+
result[:records].each { |t| excel_row(sheet, wrap, transaction: t) }
|
45
|
+
sheet.add_row []
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
row = [
|
4
|
+
*(1..(dimension - headers.size - 1)).map { |_n| '' },
|
5
|
+
*headers.map.with_index { |h, i| value_label(@dimensions.size - headers.size + i - 1, h[:value]) },
|
6
|
+
*(value_label(dimension - 1, result[:value]) if dimension.positive?),
|
7
|
+
*((result[:count].to_f / parent_result[:count]).inspect if parent_result && @search.percent),
|
8
|
+
result[:count].inspect,
|
9
|
+
*(if parent_result && @search.percent
|
10
|
+
(
|
11
|
+
if parent_result[controller.c_summary_fields[0]].positive?
|
12
|
+
result[controller.c_summary_fields[0]].to_f / parent_result[controller.c_summary_fields[0]]
|
13
|
+
else
|
14
|
+
0
|
15
|
+
end).inspect
|
16
|
+
end),
|
17
|
+
result[controller.c_summary_fields[0]].inspect
|
18
|
+
]
|
19
|
+
if controller.c_summary_fields.size > 1
|
20
|
+
if parent_result && @search.percent
|
21
|
+
percent =
|
22
|
+
if parent_result[controller.c_summary_fields[1]].positive?
|
23
|
+
result[controller.c_summary_fields[1]].to_f / parent_result[controller.c_summary_fields[1]]
|
24
|
+
else
|
25
|
+
0
|
26
|
+
end
|
27
|
+
row << percent.inspect
|
28
|
+
end
|
29
|
+
row << result[controller.c_summary_fields[1]].inspect
|
30
|
+
end
|
31
|
+
sheet.add_row row
|
@@ -1,2 +1,3 @@
|
|
1
1
|
<%= link_to 'Excel', @search.url_options.merge(action: :excel_export) %> |
|
2
|
+
<%= link_to 'Excel (XML)', @search.url_options.merge(action: :excel_export, format: :xml) %> |
|
2
3
|
<%= link_to 'HTML', @search.url_options.merge(action: :html_export), data_popup: ['Elections', 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes, width=1024px'] %>
|
@@ -1,25 +1,17 @@
|
|
1
1
|
<% if dimension_name == 'calendar_date' %>
|
2
2
|
<% dates = [*@search.filter[dimension_name]] %>
|
3
|
-
<
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
<%= form.label "filter[calendar_date]", t(:to_date) %>
|
16
|
-
</td>
|
17
|
-
<td>
|
18
|
-
<input type="text" data-behaviour="datepicker" id="search_filter[to_<%= dimension_name %>]"
|
19
|
-
name="search[filter][<%= dimension_name %>][]" class="form-control" value="<%= dates[1] || dates[0] %>"
|
20
|
-
placeholder="yyyy-mm-dd"/>
|
21
|
-
</td>
|
22
|
-
</tr>
|
3
|
+
<div class="form-group">
|
4
|
+
<%= form.label "filter[calendar_date]", t(:from_date) %>
|
5
|
+
<input type="text" data-behaviour="datepicker" id="search_filter[from_<%= dimension_name %>]"
|
6
|
+
name="search[filter][<%= dimension_name %>][]" class="form-control date" value="<%= dates[0] %>"
|
7
|
+
placeholder="yyyy-mm-dd"/>
|
8
|
+
</div>
|
9
|
+
<div class="form-group">
|
10
|
+
<%= form.label "filter[calendar_date]", t(:to_date) %>
|
11
|
+
<input type="text" data-behaviour="datepicker" id="search_filter[to_<%= dimension_name %>]"
|
12
|
+
name="search[filter][<%= dimension_name %>][]" class="form-control date" value="<%= dates[1] %>"
|
13
|
+
placeholder="yyyy-mm-dd"/>
|
14
|
+
</div>
|
23
15
|
<% else %>
|
24
16
|
<div class="form-group">
|
25
17
|
<%= form.label "filter[#{dimension_name}]", t(dimension_name, default: :"activerecord.models.#{dimension_name}") %>
|
@@ -1,11 +1,13 @@
|
|
1
1
|
<% unless result[:records].empty? %>
|
2
2
|
<tr>
|
3
3
|
<td colspan="<%= controller.c_summary_fields.size + 1 %>">
|
4
|
-
<table class="table table-condensed table-bordered" style="padding-bottom: 10px;">
|
4
|
+
<table id="drilldown-records-<%= result[:value] %>" class="table table-condensed table-bordered" style="padding-bottom: 10px;">
|
5
5
|
<%= render :partial => '/drilldown/row_header' %>
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
<tbody>
|
7
|
+
<% result[:records].each do |t| %>
|
8
|
+
<%= render :partial => '/drilldown/row', :locals => { :transaction => t, :previous_transaction => nil, :errors => [], :error_row => false, :meter1_errors => false } %>
|
9
|
+
<% end %>
|
10
|
+
</tbody>
|
9
11
|
</table>
|
10
12
|
</td>
|
11
13
|
</tr>
|
@@ -1,12 +1,14 @@
|
|
1
|
-
<table class="table table-condensed table-bordered">
|
2
|
-
<
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
</
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
<table id="drilldown-summary-table" class="table table-condensed table-bordered">
|
2
|
+
<thead>
|
3
|
+
<tr>
|
4
|
+
<% @dimensions.each do |d| %>
|
5
|
+
<th><%= h d[:pretty_name] %></th>
|
6
|
+
<% end %>
|
7
|
+
<th><%= t controller.c_target_class.table_name.capitalize %></th>
|
8
|
+
<%= controller.c_summary_fields.map { |l| "<th>#{t(l)}</th>" }.join("\n").html_safe %>
|
9
|
+
</tr>
|
10
|
+
</thead>
|
11
|
+
<tbody>
|
12
|
+
<%= summary_row(@result) %>
|
13
|
+
</tbody>
|
12
14
|
</table>
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
def excel_summary_row_xlsx(sheet, result, parent_result = nil, dimension = 0, headers = [])
|
4
|
+
if result[:rows]
|
5
|
+
significant_rows = result[:rows].reject { |r| r[:row_count].zero? }
|
6
|
+
significant_rows.each_with_index do |r, i|
|
7
|
+
sub_headers =
|
8
|
+
if i.zero?
|
9
|
+
if dimension.zero?
|
10
|
+
headers
|
11
|
+
else
|
12
|
+
headers + [{
|
13
|
+
value: result[:value],
|
14
|
+
display_row_count: result[:nodes] + result[:row_count] * (@search.list ? 1 : 0)
|
15
|
+
}]
|
16
|
+
end
|
17
|
+
else
|
18
|
+
[] # [{:value => result[:value], :row_count => result[:row_count]}]
|
19
|
+
end
|
20
|
+
excel_summary_row_xlsx(sheet, r, result, dimension + 1, sub_headers)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
render(partial: '/drilldown/excel_summary_row_xlsx',
|
24
|
+
locals: { sheet: sheet, result: result, parent_result: parent_result, headers: headers.dup,
|
25
|
+
dimension: dimension })
|
26
|
+
|
27
|
+
if @search.list
|
28
|
+
render(partial: '/drilldown/excel_record_list_xlsx',
|
29
|
+
locals: { sheet: sheet, result: result })
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
return if dimension >= @dimensions.size
|
34
|
+
|
35
|
+
render(partial: '/drilldown/excel_summary_total_row_xlsx', locals: {
|
36
|
+
sheet: sheet, result: result, headers: headers.dup, dimension: dimension
|
37
|
+
})
|
38
|
+
end
|
39
|
+
|
40
|
+
xlsx_package.use_shared_strings = true
|
41
|
+
wb = xlsx_package.workbook
|
42
|
+
wb.add_worksheet(name: 'Transaction Summary') do |sheet|
|
43
|
+
# header_colspan =
|
44
|
+
# if @search.list
|
45
|
+
# [@dimensions.size - 1, 0].max + @search.fields.size - 1
|
46
|
+
# else
|
47
|
+
# @dimensions.size - 1 + (controller.c_summary_fields.size + 1) * (@search.percent ? 2 : 1)
|
48
|
+
# end
|
49
|
+
|
50
|
+
# xml.Row 'ss:Height' => '18.75' do
|
51
|
+
# xml.Cell 'ss:MergeAcross' => header_colspan, 'ss:StyleID' => 'MainTitle' do
|
52
|
+
# xml.Data caption, 'ss:Type' => 'String'
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
sheet.add_row [caption]
|
56
|
+
|
57
|
+
# xml.Row 'ss:Height' => '15.75' do
|
58
|
+
# xml.Cell 'ss:MergeAcross' => header_colspan, 'ss:StyleID' => 'SubTitle' do
|
59
|
+
# xml.Data subcaption, 'ss:Type' => 'String'
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
sheet.add_row [subcaption]
|
63
|
+
|
64
|
+
# xml.Row do
|
65
|
+
# @dimensions.each do |d|
|
66
|
+
# xml.Cell 'ss:StyleID' => 'DimensionHeading' do
|
67
|
+
# xml.Data (h d[:pretty_name]).to_s, 'ss:Type' => 'String'
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
# xml.Cell 'ss:StyleID' => 'Heading', 'ss:MergeAcross' => @search.percent ? 1 : 0 do
|
71
|
+
# xml.Data t(controller.c_target_class.table_name), 'ss:Type' => 'String'
|
72
|
+
# end
|
73
|
+
# controller.c_summary_fields.each do |f|
|
74
|
+
# xml.Cell 'ss:StyleID' => 'Heading', 'ss:MergeAcross' => @search.percent ? 1 : 0 do
|
75
|
+
# xml.Data f, 'ss:Type' => 'String'
|
76
|
+
# end
|
77
|
+
# end
|
78
|
+
# end
|
79
|
+
sheet.add_row [
|
80
|
+
*@dimensions.map { |d| (h d[:pretty_name]).to_s },
|
81
|
+
t(controller.c_target_class.table_name),
|
82
|
+
*controller.c_summary_fields.map { |f| f }
|
83
|
+
]
|
84
|
+
|
85
|
+
excel_summary_row_xlsx(sheet, @result)
|
86
|
+
end
|
@@ -1,15 +1,12 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
|
-
<head>
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
<%= yield %>
|
13
|
-
|
14
|
-
</body>
|
3
|
+
<head>
|
4
|
+
<title>Simple drilldown</title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
<%= stylesheet_link_tag "simple_drilldown/application", media: "all" %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
<%= yield %>
|
11
|
+
</body>
|
15
12
|
</html>
|
data/lib/simple_drilldown.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleDrilldown
|
4
|
+
# Allow tracking changes for a field
|
5
|
+
module Changes
|
6
|
+
def self.included(clazz)
|
7
|
+
clazz.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
# Class methods for Changes
|
11
|
+
module ClassMethods
|
12
|
+
def changes_for(*fields)
|
13
|
+
fields.each do |field|
|
14
|
+
condition_proc = lambda do
|
15
|
+
in_join = is_a?(ActiveRecord::Associations::JoinDependency::JoinAssociation)
|
16
|
+
table_alias = in_join ? aliased_table_name : AuditLog.table_name
|
17
|
+
"#{table_alias}.new_values LIKE '%#{field}%'"
|
18
|
+
end
|
19
|
+
has_many :"#{field}_changes", -> { where(condition_proc.call).order(:created_at) },
|
20
|
+
class_name: :AuditLog, foreign_key: :record_id
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -15,7 +15,7 @@ module SimpleDrilldown
|
|
15
15
|
class_attribute :c_default_fields, default: []
|
16
16
|
class_attribute :c_default_select_value, default: SimpleDrilldown::Search::SelectValue::COUNT
|
17
17
|
class_attribute :c_dimension_defs
|
18
|
-
class_attribute :c_fields
|
18
|
+
class_attribute :c_fields, default: []
|
19
19
|
class_attribute :c_list_includes, default: []
|
20
20
|
class_attribute :c_list_order
|
21
21
|
class_attribute :c_select, default: 'count(*) as count'
|
@@ -45,8 +45,8 @@ module SimpleDrilldown
|
|
45
45
|
self.c_base_condition = base_condition
|
46
46
|
end
|
47
47
|
|
48
|
-
def base_includes(base_includes)
|
49
|
-
self.c_base_includes = base_includes
|
48
|
+
def base_includes(*base_includes)
|
49
|
+
self.c_base_includes = base_includes.flatten
|
50
50
|
end
|
51
51
|
|
52
52
|
def base_group(base_group)
|
@@ -69,7 +69,7 @@ module SimpleDrilldown
|
|
69
69
|
self.c_select = select
|
70
70
|
end
|
71
71
|
|
72
|
-
def list_includes(list_includes)
|
72
|
+
def list_includes(*list_includes)
|
73
73
|
self.c_list_includes = list_includes.flatten
|
74
74
|
end
|
75
75
|
|
@@ -128,7 +128,7 @@ module SimpleDrilldown
|
|
128
128
|
interval: interval,
|
129
129
|
label_method: label_method,
|
130
130
|
legal_values: legal_values,
|
131
|
-
pretty_name: I18n.t(name, default: :"activerecord.models.#{name}"),
|
131
|
+
pretty_name: I18n.t(name, default: [:"activerecord.models.#{name}", name.to_s.titleize]),
|
132
132
|
queries: queries,
|
133
133
|
reverse: reverse,
|
134
134
|
select_expression:
|
@@ -153,14 +153,15 @@ module SimpleDrilldown
|
|
153
153
|
end
|
154
154
|
includes.uniq!
|
155
155
|
end
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
156
|
+
rows_query = c_target_class.unscoped.where(c_base_condition)
|
157
|
+
.select("#{query[:select]} AS value")
|
158
|
+
.joins(make_join([], c_target_class.name.underscore.to_sym, includes))
|
159
|
+
.order('value')
|
160
|
+
.group(:value)
|
161
|
+
# rows_query = rows_query.without_deleted if c_target_class.try :paranoid?
|
162
|
+
rows_query = rows_query.where(filter_conditions) if filter_conditions
|
163
|
+
rows_query = rows_query.where(query[:where]) if query[:where]
|
164
|
+
rows = rows_query.to_a
|
164
165
|
filter_fields = search.filter[field.to_s]
|
165
166
|
filter_fields&.each do |selected_value|
|
166
167
|
next if rows.find { |r| r[:value].to_s == selected_value }
|
@@ -191,9 +192,6 @@ module SimpleDrilldown
|
|
191
192
|
|
192
193
|
values = Array(values)
|
193
194
|
if dimension_def[:interval]
|
194
|
-
values *= 2 if values.size == 1
|
195
|
-
raise "Need 2 values for interval filter: #{values.inspect}" if values.size != 2
|
196
|
-
|
197
195
|
if values[0].present? && values[1].present?
|
198
196
|
condition_strings << "#{dimension_def[:select_expression]} BETWEEN ? AND ?"
|
199
197
|
condition_values += values
|
@@ -262,7 +260,9 @@ module SimpleDrilldown
|
|
262
260
|
include_alias = include.to_s.pluralize
|
263
261
|
case ass.macro
|
264
262
|
when :belongs_to
|
265
|
-
|
263
|
+
pk_col = ass.association_primary_key
|
264
|
+
fk_col = ass.options[:foreign_key] || "#{include}_id"
|
265
|
+
"LEFT JOIN #{include_table} #{include_alias} ON #{include_alias}.#{pk_col} = #{model_table}.#{fk_col}"
|
266
266
|
when :has_one, :has_many
|
267
267
|
fk_col = ass.options[:foreign_key] || "#{model}_id"
|
268
268
|
sql = +"LEFT JOIN #{include_table} #{include_alias} ON #{include_alias}.#{fk_col} = #{model_table}.id"
|
@@ -274,7 +274,8 @@ module SimpleDrilldown
|
|
274
274
|
ass_order_prefixed.gsub!(/\b#{cname}\b/, "#{include_alias}.#{cname}")
|
275
275
|
end
|
276
276
|
paranoid_clause = 'AND t2.deleted_at IS NULL' if ass.klass.paranoid?
|
277
|
-
# FIXME(uwe): Should we add "where" from the ScopeHolder here as well?
|
277
|
+
# FIXME(uwe): Should we add "where" from the ScopeHolder here as well?
|
278
|
+
# Ref: SimpleDrilldown::Changes#changes_for
|
278
279
|
min_query = <<~SQL
|
279
280
|
SELECT MIN(#{ass_order}) FROM #{include_table} t2 WHERE t2.#{fk_col} = #{model_table}.id #{paranoid_clause}
|
280
281
|
SQL
|
@@ -347,8 +348,8 @@ module SimpleDrilldown
|
|
347
348
|
rows = c_target_class.unscoped.where(c_base_condition).select(select).where(conditions)
|
348
349
|
.joins(joins)
|
349
350
|
.group(group)
|
350
|
-
.order(order)
|
351
|
-
|
351
|
+
.order(order)
|
352
|
+
.to_a
|
352
353
|
if rows.empty?
|
353
354
|
@result = { value: 'All', count: 0, row_count: 0, nodes: 0, rows: [] }
|
354
355
|
c_summary_fields.each { |f| @result[f] = 0 }
|
@@ -395,12 +396,19 @@ module SimpleDrilldown
|
|
395
396
|
|
396
397
|
def excel_export
|
397
398
|
index(false)
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
399
|
+
respond_to do |format|
|
400
|
+
format.xlsx do
|
401
|
+
render xlsx: c_target_class.table_name, template: 'drilldown/excel_export_xlsx'
|
402
|
+
end
|
403
|
+
format.xml do
|
404
|
+
set_excel_headers
|
405
|
+
if params.dig(:search, :list) == '1'
|
406
|
+
@records = get_records(@result)
|
407
|
+
render template: 'drilldown/excel_export_records', layout: false
|
408
|
+
else
|
409
|
+
render template: 'drilldown/excel_export', layout: false
|
410
|
+
end
|
411
|
+
end
|
404
412
|
end
|
405
413
|
end
|
406
414
|
|
@@ -9,7 +9,7 @@ module SimpleDrilldown
|
|
9
9
|
config.autoload_paths << File.dirname(__dir__)
|
10
10
|
|
11
11
|
initializer 'simple_drilldown.assets.precompile' do |app|
|
12
|
-
app.config.assets.precompile += %w[chartkick.js]
|
12
|
+
app.config.assets.precompile += %w[simple_drilldown/application.css chartkick.js]
|
13
13
|
end
|
14
14
|
|
15
15
|
ActionDispatch::Routing::Mapper.include SimpleDrilldown::Routing
|
@@ -38,7 +38,7 @@ module SimpleDrilldown
|
|
38
38
|
html << summary_row(r, result, dimension + 1, sub_headers, i.positive?)
|
39
39
|
end
|
40
40
|
elsif @search.list
|
41
|
-
html << render(partial: '/drilldown/record_list', locals: { result: result })
|
41
|
+
html << render(partial: '/drilldown/record_list', locals: { result: result, dimension: dimension })
|
42
42
|
end
|
43
43
|
if dimension < @dimensions.size
|
44
44
|
html << render(partial: '/drilldown/summary_total_row',
|
@@ -89,7 +89,7 @@ module SimpleDrilldown
|
|
89
89
|
private
|
90
90
|
|
91
91
|
def caption_txt
|
92
|
-
"#{controller.c_target_class} #{t(@search.select_value.downcase)}" +
|
92
|
+
"#{controller.c_target_class} #{I18n.t(@search.select_value.downcase)}" +
|
93
93
|
(@dimensions && @dimensions.any? ? " by #{@dimensions.map { |d| d[:pretty_name] }.join(' and ')}" : '')
|
94
94
|
end
|
95
95
|
end
|
@@ -8,8 +8,11 @@ module SimpleDrilldown
|
|
8
8
|
controller ||= path
|
9
9
|
get "#{path}(.:format)" => "#{controller}#index", as: path
|
10
10
|
scope path, controller: controller, as: path do
|
11
|
-
|
11
|
+
{ excel_export: :xlsx, excel_export_records: :xlsx, html_export: :html }.each do |action, format|
|
12
|
+
get action, defaults: { format: format }
|
13
|
+
end
|
12
14
|
get 'choices/:dimension_name', action: :choices, as: :choices
|
15
|
+
yield if block_given?
|
13
16
|
end
|
14
17
|
end
|
15
18
|
end
|
@@ -81,15 +81,15 @@ module SimpleDrilldown
|
|
81
81
|
|
82
82
|
def url_options
|
83
83
|
o = {
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
84
|
+
search: {
|
85
|
+
title: title,
|
86
|
+
list: list ? '1' : '0',
|
87
|
+
percent: percent ? '1' : '0',
|
88
|
+
list_change_times: list_change_times ? '1' : '0',
|
89
|
+
filter: filter,
|
90
|
+
dimensions: dimensions,
|
91
|
+
display_type: display_type,
|
92
|
+
},
|
93
93
|
}
|
94
94
|
o[:search][:fields] = fields unless fields == @default_fields
|
95
95
|
o
|
metadata
CHANGED
@@ -1,29 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_drilldown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.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: 2021-
|
11
|
+
date: 2021-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: caxlsx_rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.6'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: chartkick
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
31
|
- - "~>"
|
18
32
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
33
|
+
version: '4.0'
|
20
34
|
type: :runtime
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
40
|
+
version: '4.0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rails
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -91,12 +105,13 @@ files:
|
|
91
105
|
- app/mailers/simple_drilldown/application_mailer.rb
|
92
106
|
- app/models/simple_drilldown/application_record.rb
|
93
107
|
- app/views/drilldown/_chart.html.erb
|
94
|
-
- app/views/drilldown/
|
95
|
-
- app/views/drilldown/_excel_row.builder
|
108
|
+
- app/views/drilldown/_excel_record_list_xlsx.xlsx.axlsx
|
96
109
|
- app/views/drilldown/_excel_row_header.builder
|
97
110
|
- app/views/drilldown/_excel_styles.builder
|
98
111
|
- app/views/drilldown/_excel_summary_row.builder
|
112
|
+
- app/views/drilldown/_excel_summary_row_xlsx.xlsx.axlsx
|
99
113
|
- app/views/drilldown/_excel_summary_total_row.builder
|
114
|
+
- app/views/drilldown/_excel_summary_total_row_xlsx.xlsx.axlsx
|
100
115
|
- app/views/drilldown/_export_links.html.erb
|
101
116
|
- app/views/drilldown/_field.html.erb
|
102
117
|
- app/views/drilldown/_fields.html.erb
|
@@ -114,6 +129,7 @@ files:
|
|
114
129
|
- app/views/drilldown/data_3.builder
|
115
130
|
- app/views/drilldown/excel_export.builder
|
116
131
|
- app/views/drilldown/excel_export_records.builder
|
132
|
+
- app/views/drilldown/excel_export_xlsx.xlsx.axlsx
|
117
133
|
- app/views/drilldown/html_export.html.erb
|
118
134
|
- app/views/drilldown/index.html.erb
|
119
135
|
- app/views/drilldown/print.html.erb
|
@@ -126,6 +142,7 @@ files:
|
|
126
142
|
- lib/generators/drilldown_controller/templates/drilldown_controller.rb.erb
|
127
143
|
- lib/generators/drilldown_controller/templates/drilldown_controller_test.rb.erb
|
128
144
|
- lib/simple_drilldown.rb
|
145
|
+
- lib/simple_drilldown/changes.rb
|
129
146
|
- lib/simple_drilldown/controller.rb
|
130
147
|
- lib/simple_drilldown/engine.rb
|
131
148
|
- lib/simple_drilldown/helper.rb
|
@@ -153,7 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
153
170
|
- !ruby/object:Gem::Version
|
154
171
|
version: '0'
|
155
172
|
requirements: []
|
156
|
-
rubygems_version: 3.2.
|
173
|
+
rubygems_version: 3.2.15
|
157
174
|
signing_key:
|
158
175
|
specification_version: 4
|
159
176
|
summary: Simple data warehouse and drilldown.
|
@@ -1,10 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
xml << render(partial: '/drilldown/excel_row_header')
|
4
|
-
|
5
|
-
result[:records].each do |t|
|
6
|
-
xml << render(
|
7
|
-
partial: '/drilldown/excel_row',
|
8
|
-
locals: { transaction: t, previous_transaction: nil, errors: [], error_row: false, meter1_errors: false }
|
9
|
-
)
|
10
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
xml.Row do
|
4
|
-
padding_cells = @dimensions.empty? ? 1 : @dimensions.size
|
5
|
-
1.upto(padding_cells - 1) { |_n| xml.Cell('ss:StyleID' => 'Outer') }
|
6
|
-
|
7
|
-
@search.fields.each_with_index do |field, i|
|
8
|
-
if field == 'time'
|
9
|
-
value = ((
|
10
|
-
if transaction.respond_to?(:completed_at)
|
11
|
-
transaction.completed_at
|
12
|
-
else
|
13
|
-
transaction.created_at
|
14
|
-
end)).localtime.strftime('%Y-%m-%d %H:%M')
|
15
|
-
else
|
16
|
-
value = if @transaction_fields_map[field.to_sym][:attr_method]
|
17
|
-
@transaction_fields_map[field.to_sym][:attr_method].call(transaction)
|
18
|
-
else
|
19
|
-
transaction.send(field)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
field_def = @transaction_fields_map[field.to_sym]
|
24
|
-
|
25
|
-
xml.Cell('ss:Index' => (padding_cells + i).to_s) do
|
26
|
-
xml.Data value, 'ss:Type' => 'String'
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|