simple_drilldown 0.7.4 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a36aab8c580f75685e00d36a6aea2bbb1b58b43c153db650ec9298cf0b5fff6
4
- data.tar.gz: 6c1a7cda78d09fdff2519c1db52dd4c5aa7f2c7c273bc7b9f6bc730cf02b6d80
3
+ metadata.gz: f1987db21273c12d879bae55633fd337003d7ced07c152a09e9269f88d8928af
4
+ data.tar.gz: b6e9149cbcc597a3e407aac96936c77956c9489d87a4ec14beb225d6e993cbc4
5
5
  SHA512:
6
- metadata.gz: 587b979884e3cdb8a85fee6438358bf5ba3878ff55b1355b52e987b1b896d686558fbcde181d9c1bc1a62b0ec7a6fbe591fb36c621d59d5688e0655c31170dff
7
- data.tar.gz: a4e90f4858363cea570a5544efb0ed45bb4ae541a4a2238511bf726755f6ced093e5f9dde0574f21f6e1b7621b9564788c9795f2ebd1e8c721014472b2f50d44
6
+ metadata.gz: b7325de41bc98a3b712d8a9cdf4c52099208d35e1026cff81a4c75be4bd2e33933000bc6f0df9aceb36ce0979826cc3819fac444f0f1ec1626c964aa31aca167
7
+ data.tar.gz: a2185eeda0623ece2aca690e92419cdc7821f18321e4c4cd6a7d02b4159ba0e970739f5df1928661d244d84b9eaacd1371974dbaeb84d618718fb4b4a2b7e71d
@@ -1,19 +1,19 @@
1
1
  <%
2
2
  data =
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
- }
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
- { onChange: 'form.submit()', name: 'search[dimensions][]', id: "search_dimensions_#{i}" } %>
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
- [SimpleDrilldown::Search::DisplayType::BAR, SimpleDrilldown::Search::DisplayType::PIE].include?(@search.display_type) %>
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
- 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("&search[dimensions][]=<%= @dimensions[0][:url_param_name] %>", '');
76
- new_location = new_location + '&search[filter][<%= @dimensions[0][:url_param_name] %>][]=' + label;
77
- console.log(new_location);
78
- window.location = new_location;
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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ row = (1..dimension).map { |_n| nil }
4
+
5
+ (@dimensions.size - dimension).times { |_i| row << nil }
6
+ row << result[:count]
7
+ sheet.add_row row
@@ -1,2 +1,3 @@
1
1
  <%= link_to 'Excel', @search.url_options.merge(action: :excel_export) %>&nbsp;|
2
+ <%= link_to 'Excel (XML)', @search.url_options.merge(action: :excel_export, format: :xml) %>&nbsp;|
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
- <tr>
4
- <td align="right" valign="top">
5
- <%= form.label "filter[calendar_date]", t(:from_date) %>
6
- </td>
7
- <td>
8
- <input type="text" data-behaviour="datepicker" id="search_filter[from_<%= dimension_name %>]"
9
- name="search[filter][<%= dimension_name %>][]" class="form-control" value="<%= dates[0] %>"
10
- placeholder="yyyy-mm-dd"/>
11
- </td>
12
- </tr>
13
- <tr>
14
- <td align="right" valign="top">
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
- <% result[:records].each do |t| %>
7
- <%= render :partial => '/drilldown/row', :locals => { :transaction => t, :previous_transaction => nil, :errors => [], :error_row => false, :meter1_errors => false } %>
8
- <% end %>
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
- <tr>
3
- <% @dimensions.each do |d| %>
4
- <th><%=h d[:pretty_name]%></th>
5
- <% end %>
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
- </tr>
9
-
10
- <%=summary_row(@result) %>
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
- <title>Simple drilldown</title>
5
- <%= csrf_meta_tags %>
6
- <%= csp_meta_tag %>
7
-
8
- <%= stylesheet_link_tag "simple_drilldown/application", media: "all" %>
9
- </head>
10
- <body>
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>
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'caxlsx_rails'
3
4
  require 'simple_drilldown/engine'
4
5
 
5
6
  module SimpleDrilldown
@@ -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
- rows = c_target_class.unscoped.where(c_base_condition)
157
- .select("#{query[:select]} AS value")
158
- .where(filter_conditions || '1=1')
159
- .where(query[:where] || '1=1')
160
- .joins(make_join([], c_target_class.name.underscore.to_sym, includes))
161
- .order('value')
162
- .group(:value)
163
- .to_a
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
- "LEFT JOIN #{include_table} #{include_alias} ON #{include_alias}.id = #{model_table}.#{include}_id"
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? Ref: DrilldownChanges#changes_for
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).to_a
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
- set_excel_headers
399
- if params.dig(:search, :list) == '1'
400
- @records = get_records(@result)
401
- render template: '/drilldown/excel_export_records', layout: false
402
- else
403
- render template: '/drilldown/excel_export', layout: false
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
- %i[excel_export excel_export_records html_export].each { |action| get action }
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
- 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
- },
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
@@ -1,3 +1,3 @@
1
1
  module SimpleDrilldown
2
- VERSION = '0.7.4'
2
+ VERSION = '0.9.0'
3
3
  end
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.7.4
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-03-02 00:00:00.000000000 Z
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: '3.3'
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: '3.3'
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/_excel_record_list.builder
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.3
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