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