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 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