spout 0.10.2 → 0.11.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -0
- data/README.md +3 -30
- data/lib/spout/commands/coverage.rb +2 -1
- data/lib/spout/commands/deploy.rb +82 -77
- data/lib/spout/commands/exporter.rb +2 -3
- data/lib/spout/commands/graphs.rb +68 -67
- data/lib/spout/commands/help.rb +155 -0
- data/lib/spout/helpers/array_statistics.rb +36 -30
- data/lib/spout/helpers/chart_types.rb +2 -2
- data/lib/spout/helpers/config_reader.rb +5 -5
- data/lib/spout/helpers/json_request.rb +1 -2
- data/lib/spout/helpers/json_request_generic.rb +87 -0
- data/lib/spout/helpers/quietly.rb +2 -4
- data/lib/spout/helpers/semantic.rb +7 -11
- data/lib/spout/helpers/send_file.rb +23 -25
- data/lib/spout/helpers/subject_loader.rb +41 -32
- data/lib/spout/helpers/table_formatting.rb +7 -6
- data/lib/spout/models/bucket.rb +5 -4
- data/lib/spout/models/coverage_result.rb +1 -1
- data/lib/spout/models/dictionary.rb +3 -1
- data/lib/spout/models/domain.rb +7 -6
- data/lib/spout/models/empty.rb +17 -0
- data/lib/spout/models/form.rb +8 -5
- data/lib/spout/models/graphables/default.rb +41 -18
- data/lib/spout/models/graphables/histogram.rb +6 -7
- data/lib/spout/models/graphables.rb +3 -5
- data/lib/spout/models/option.rb +6 -2
- data/lib/spout/models/outlier_result.rb +3 -3
- data/lib/spout/models/record.rb +21 -3
- data/lib/spout/models/subject.rb +4 -7
- data/lib/spout/models/tables/choices_vs_choices.rb +29 -17
- data/lib/spout/models/tables/choices_vs_numeric.rb +19 -12
- data/lib/spout/models/tables/default.rb +19 -32
- data/lib/spout/models/tables/numeric_vs_choices.rb +9 -13
- data/lib/spout/models/tables/numeric_vs_numeric.rb +9 -11
- data/lib/spout/models/tables.rb +4 -6
- data/lib/spout/models/variable.rb +51 -13
- data/lib/spout/tasks/engine.rake +1 -1
- data/lib/spout/templates/ruby-version +1 -1
- data/lib/spout/templates/travis.yml +1 -1
- data/lib/spout/tests/domain_format.rb +2 -2
- data/lib/spout/tests/domain_name_format.rb +15 -0
- data/lib/spout/tests/form_name_format.rb +14 -0
- data/lib/spout/tests/variable_name_format.rb +14 -0
- data/lib/spout/tests.rb +18 -13
- data/lib/spout/version.rb +3 -3
- data/lib/spout/views/index.html.erb +2 -2
- data/lib/spout/views/outliers.html.erb +1 -1
- data/lib/spout.rb +13 -58
- data/spout.gemspec +14 -15
- metadata +25 -25
- data/lib/spout/commands/images.rb +0 -199
- data/lib/spout/support/javascripts/data.js +0 -17
- data/lib/spout/support/javascripts/highcharts-convert.js +0 -583
- data/lib/spout/support/javascripts/highcharts-more.js +0 -50
- data/lib/spout/support/javascripts/highstock.js +0 -353
- data/lib/spout/support/javascripts/jquery.1.9.1.min.js +0 -5
@@ -5,7 +5,6 @@ module Spout
|
|
5
5
|
module Models
|
6
6
|
module Graphables
|
7
7
|
class Default
|
8
|
-
|
9
8
|
attr_reader :variable, :chart_variable, :stratification_variable, :subjects
|
10
9
|
|
11
10
|
def initialize(variable, chart_variable, stratification_variable, subjects)
|
@@ -13,27 +12,24 @@ module Spout
|
|
13
12
|
@chart_variable = chart_variable
|
14
13
|
@stratification_variable = stratification_variable
|
15
14
|
@subjects = subjects
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
begin
|
16
|
+
@values_unique = subjects.collect(&@variable.id.to_sym).reject { |a| a.is_a?(Spout::Models::Empty) }.uniq
|
17
|
+
rescue
|
18
|
+
@values_unique = []
|
19
|
+
end
|
20
20
|
@buckets = continuous_buckets
|
21
21
|
end
|
22
22
|
|
23
23
|
def to_hash
|
24
|
-
if valid?
|
25
|
-
{ title: title, subtitle: subtitle, categories: categories, units: units, series: series, stacking: stacking, x_axis_title: x_axis_title }
|
26
|
-
else
|
27
|
-
nil
|
28
|
-
end
|
24
|
+
{ title: title, subtitle: subtitle, categories: categories, units: units, series: series, stacking: stacking, x_axis_title: x_axis_title } if valid?
|
29
25
|
end
|
30
26
|
|
31
27
|
def valid?
|
32
|
-
if @variable
|
28
|
+
if @variable.nil? || @chart_variable.nil? || @values_unique == []
|
33
29
|
false
|
34
|
-
elsif @variable.type == 'choices'
|
30
|
+
elsif @variable.type == 'choices' && @variable.domain.options == []
|
35
31
|
false
|
36
|
-
elsif @chart_variable.type == 'choices'
|
32
|
+
elsif @chart_variable.type == 'choices' && @chart_variable.domain.options == []
|
37
33
|
false
|
38
34
|
else
|
39
35
|
true
|
@@ -45,7 +41,7 @@ module Spout
|
|
45
41
|
end
|
46
42
|
|
47
43
|
def subtitle
|
48
|
-
|
44
|
+
'By Visit'
|
49
45
|
end
|
50
46
|
|
51
47
|
def categories
|
@@ -71,11 +67,17 @@ module Spout
|
|
71
67
|
private
|
72
68
|
|
73
69
|
def continuous_buckets
|
74
|
-
values_numeric = @
|
70
|
+
values_numeric = @values_unique.select { |v| v.is_a? Numeric }
|
71
|
+
|
75
72
|
return [] if values_numeric.count == 0
|
76
73
|
minimum_bucket = values_numeric.min
|
77
74
|
maximum_bucket = values_numeric.max
|
78
75
|
max_buckets = 12
|
76
|
+
if all_integer?(values_numeric) && (maximum_bucket - minimum_bucket < max_buckets)
|
77
|
+
max_buckets = maximum_bucket - minimum_bucket
|
78
|
+
return discrete_buckets
|
79
|
+
end
|
80
|
+
|
79
81
|
bucket_size = ((maximum_bucket - minimum_bucket) / max_buckets.to_f)
|
80
82
|
precision = (bucket_size == 0 ? 0 : [-Math.log10(bucket_size).floor, 0].max)
|
81
83
|
|
@@ -88,8 +90,30 @@ module Spout
|
|
88
90
|
buckets
|
89
91
|
end
|
90
92
|
|
93
|
+
def discrete_buckets
|
94
|
+
values_numeric = @values_unique.select { |v| v.is_a? Numeric }
|
95
|
+
minimum_bucket = values_numeric.min
|
96
|
+
maximum_bucket = values_numeric.max
|
97
|
+
max_buckets = maximum_bucket - minimum_bucket + 1
|
98
|
+
bucket_size = 1
|
99
|
+
precision = 0
|
100
|
+
|
101
|
+
buckets = []
|
102
|
+
(0..(max_buckets-1)).to_a.each do |index|
|
103
|
+
start = (minimum_bucket + index * bucket_size)
|
104
|
+
stop = start
|
105
|
+
buckets << Spout::Models::Bucket.new(start.round(precision), stop.round(precision), discrete: true)
|
106
|
+
end
|
107
|
+
buckets
|
108
|
+
end
|
109
|
+
|
110
|
+
def all_integer?(values_numeric)
|
111
|
+
count = values_numeric.count { |v| Integer(format('%.0f', v)) == v }
|
112
|
+
count == values_numeric.size
|
113
|
+
end
|
114
|
+
|
91
115
|
def get_bucket(value)
|
92
|
-
return nil if @buckets.size == 0
|
116
|
+
return nil if @buckets.size == 0 || !value.is_a?(Numeric)
|
93
117
|
@buckets.each do |b|
|
94
118
|
return b.display_name if b.in_bucket?(value)
|
95
119
|
end
|
@@ -105,10 +129,9 @@ module Spout
|
|
105
129
|
# b) or are marked as missing codes but represented in the dataset
|
106
130
|
def filtered_domain_options(variable)
|
107
131
|
variable.domain.options.select do |o|
|
108
|
-
o.missing != true
|
132
|
+
o.missing != true || (o.missing == true && @values_unique.include?(o.value))
|
109
133
|
end
|
110
134
|
end
|
111
|
-
|
112
135
|
end
|
113
136
|
end
|
114
137
|
end
|
@@ -3,8 +3,8 @@ require 'spout/models/graphables/default'
|
|
3
3
|
module Spout
|
4
4
|
module Models
|
5
5
|
module Graphables
|
6
|
+
# Generates data for variable histograms
|
6
7
|
class Histogram < Spout::Models::Graphables::Default
|
7
|
-
|
8
8
|
def title
|
9
9
|
@variable.display_name
|
10
10
|
end
|
@@ -23,18 +23,18 @@ module Spout
|
|
23
23
|
|
24
24
|
def series
|
25
25
|
@chart_variable.domain.options.collect do |option|
|
26
|
-
visit_subjects = @subjects.select{ |s| s.send(@chart_variable.id) == option.value
|
27
|
-
visit_subject_values = visit_subjects.collect(&@variable.id.to_sym).sort rescue visit_subject_values = []
|
26
|
+
visit_subjects = @subjects.select{ |s| s.send(@chart_variable.id) == option.value && !s.send(@variable.id).nil? && !s.send(@variable.id).is_a?(Spout::Models::Empty) } rescue visit_subjects = []
|
27
|
+
visit_subject_values = visit_subjects.collect(&@variable.id.to_sym).sort # rescue visit_subject_values = []
|
28
28
|
next unless visit_subject_values.size > 0
|
29
29
|
|
30
30
|
data = []
|
31
31
|
|
32
32
|
if @variable.type == 'choices'
|
33
|
-
data = filtered_domain_options(@variable).collect do |
|
34
|
-
visit_subject_values.select{ |v| v ==
|
33
|
+
data = filtered_domain_options(@variable).collect do |o|
|
34
|
+
visit_subject_values.select { |v| v == o.value }.count
|
35
35
|
end
|
36
36
|
else
|
37
|
-
visit_subject_values.group_by{|v| get_bucket(v) }.each do |key, values|
|
37
|
+
visit_subject_values.group_by { |v| get_bucket(v) }.each do |key, values|
|
38
38
|
data[categories.index(key)] = values.count if categories.index(key)
|
39
39
|
end
|
40
40
|
end
|
@@ -46,7 +46,6 @@ module Spout
|
|
46
46
|
def x_axis_title
|
47
47
|
@variable.units
|
48
48
|
end
|
49
|
-
|
50
49
|
end
|
51
50
|
end
|
52
51
|
end
|
@@ -5,11 +5,9 @@ require 'spout/models/graphables/choices_vs_choices'
|
|
5
5
|
require 'spout/models/graphables/numeric_vs_numeric'
|
6
6
|
require 'spout/models/graphables/choices_vs_numeric'
|
7
7
|
|
8
|
-
|
9
8
|
module Spout
|
10
9
|
module Models
|
11
10
|
module Graphables
|
12
|
-
|
13
11
|
DEFAULT_CLASS = Spout::Models::Graphables::Default
|
14
12
|
GRAPHABLE_CLASSES = {
|
15
13
|
'histogram' => Spout::Models::Graphables::Histogram,
|
@@ -25,7 +23,7 @@ module Spout
|
|
25
23
|
end
|
26
24
|
|
27
25
|
def self.get_graph_type(variable, chart_variable, stratification_variable)
|
28
|
-
if stratification_variable
|
26
|
+
if stratification_variable.nil?
|
29
27
|
'histogram'
|
30
28
|
else
|
31
29
|
"#{variable_to_graph_type(variable)}_vs_#{variable_to_graph_type(chart_variable)}"
|
@@ -34,13 +32,13 @@ module Spout
|
|
34
32
|
|
35
33
|
def self.variable_to_graph_type(variable)
|
36
34
|
variable_type = (variable ? variable.type : nil)
|
37
|
-
case variable_type
|
35
|
+
case variable_type
|
36
|
+
when 'numeric', 'integer'
|
38
37
|
'numeric'
|
39
38
|
else
|
40
39
|
variable_type
|
41
40
|
end
|
42
41
|
end
|
43
|
-
|
44
42
|
end
|
45
43
|
end
|
46
44
|
end
|
data/lib/spout/models/option.rb
CHANGED
@@ -4,11 +4,15 @@ module Spout
|
|
4
4
|
attr_accessor :display_name, :value, :description, :missing
|
5
5
|
|
6
6
|
def initialize(option_hash)
|
7
|
-
%w(
|
8
|
-
instance_variable_set("@#{method}", (option_hash.
|
7
|
+
%w(display_name value description missing).each do |method|
|
8
|
+
instance_variable_set("@#{method}", (option_hash.is_a?(Hash) ? option_hash : {})[method])
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
+
def deploy_params
|
13
|
+
{ display_name: display_name, value: value, description: description,
|
14
|
+
missing: missing }
|
15
|
+
end
|
12
16
|
end
|
13
17
|
end
|
14
18
|
end
|
@@ -22,9 +22,9 @@ module Spout
|
|
22
22
|
2
|
23
23
|
end
|
24
24
|
variable = Spout::Helpers::JsonLoader::get_variable(method)
|
25
|
-
@units = (variable.
|
26
|
-
@display_name = (variable.
|
27
|
-
@variable_type = (variable.
|
25
|
+
@units = (variable.is_a?(Hash) ? variable['units'] : nil)
|
26
|
+
@display_name = (variable.is_a?(Hash) ? variable['display_name'] : nil)
|
27
|
+
@variable_type = (variable.is_a?(Hash) ? variable['type'] : nil)
|
28
28
|
@median = @values.median
|
29
29
|
end
|
30
30
|
|
data/lib/spout/models/record.rb
CHANGED
@@ -3,16 +3,34 @@ require 'fileutils'
|
|
3
3
|
|
4
4
|
module Spout
|
5
5
|
module Models
|
6
|
+
# Base class for spout variables, forms, and domains that are read from JSON
|
7
|
+
# files
|
6
8
|
class Record
|
7
|
-
|
8
9
|
class << self
|
9
10
|
# Only returns records with zero json errors, nil otherwise
|
10
11
|
def find_by_id(id)
|
11
|
-
|
12
|
-
file_name = Dir.glob(File.join(dictionary_root, "#{self.name.split("::").last.to_s.downcase}s", "**", "#{id.to_s.downcase}.json"), File::FNM_CASEFOLD).first
|
12
|
+
file_name = Dir.glob(expected_path(id), File::FNM_CASEFOLD).first
|
13
13
|
variable = new(file_name, dictionary_root)
|
14
14
|
(variable.errors.size > 0 ? nil : variable)
|
15
15
|
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def record_folder
|
20
|
+
"#{name.split('::').last.to_s.downcase}s"
|
21
|
+
end
|
22
|
+
|
23
|
+
def expected_filename(id)
|
24
|
+
"#{id.to_s.downcase}.json"
|
25
|
+
end
|
26
|
+
|
27
|
+
def expected_path(id)
|
28
|
+
File.join(dictionary_root, record_folder, '**', expected_filename(id))
|
29
|
+
end
|
30
|
+
|
31
|
+
def dictionary_root
|
32
|
+
FileUtils.pwd
|
33
|
+
end
|
16
34
|
end
|
17
35
|
end
|
18
36
|
end
|
data/lib/spout/models/subject.rb
CHANGED
@@ -1,17 +1,14 @@
|
|
1
|
-
# subject.rb
|
2
|
-
|
3
1
|
module Spout
|
4
2
|
module Models
|
5
|
-
|
3
|
+
# Subject encapsulates records for individuals specified by an identifier
|
6
4
|
class Subject
|
7
|
-
attr_accessor :_visit
|
5
|
+
attr_accessor :_visit, :_csv
|
8
6
|
|
9
|
-
def self.create
|
10
|
-
subject =
|
7
|
+
def self.create
|
8
|
+
subject = new
|
11
9
|
yield subject if block_given?
|
12
10
|
subject
|
13
11
|
end
|
14
|
-
|
15
12
|
end
|
16
13
|
end
|
17
14
|
end
|
@@ -3,49 +3,61 @@ require 'spout/models/tables/default'
|
|
3
3
|
module Spout
|
4
4
|
module Models
|
5
5
|
module Tables
|
6
|
+
# Generates a table of
|
6
7
|
class ChoicesVsChoices < Spout::Models::Tables::Default
|
7
|
-
|
8
8
|
def title
|
9
9
|
"#{@variable.display_name} vs #{@chart_variable.display_name}"
|
10
10
|
end
|
11
11
|
|
12
12
|
def headers
|
13
|
-
|
13
|
+
header_row = [''] + filtered_domain_options(@chart_variable).collect(&:display_name)
|
14
|
+
if @totals
|
15
|
+
header_row += ['Total']
|
16
|
+
end
|
17
|
+
[header_row]
|
14
18
|
end
|
15
19
|
|
16
20
|
def footers
|
17
21
|
total_values = filtered_domain_options(@chart_variable).collect do |option|
|
18
|
-
total_count = @filtered_subjects.
|
19
|
-
{ text: (
|
22
|
+
total_count = @filtered_subjects.count { |s| s.send(@chart_variable.id) == option.value }
|
23
|
+
{ text: (Spout::Helpers::TableFormatting.format_number(total_count, :count)), style: 'font-weight:bold' }
|
24
|
+
end
|
25
|
+
footer_row = [{ text: 'Total', style: 'font-weight:bold' }] + total_values
|
26
|
+
if @totals
|
27
|
+
footer_row += [{ text: Spout::Helpers::TableFormatting.format_number(@filtered_subjects.count, :count), style: 'font-weight:bold' }]
|
20
28
|
end
|
21
|
-
[
|
22
|
-
[{ text: "Total", style: "font-weight:bold" }] + total_values + [{ text: Spout::Helpers::TableFormatting::format_number(@filtered_subjects.count, :count), style: 'font-weight:bold'}]
|
23
|
-
]
|
29
|
+
[footer_row]
|
24
30
|
end
|
25
31
|
|
26
32
|
def rows
|
27
|
-
rows_result = []
|
28
33
|
rows_result = filtered_domain_options(@variable).collect do |option|
|
29
|
-
row_subjects = @filtered_subjects.select{ |s| s.send(@variable.id) == option.value }
|
34
|
+
row_subjects = @filtered_subjects.select { |s| s.send(@variable.id) == option.value }
|
30
35
|
row_cells = filtered_domain_options(@chart_variable).collect do |chart_option|
|
31
|
-
count = row_subjects.
|
32
|
-
count > 0 ? Spout::Helpers::TableFormatting
|
36
|
+
count = row_subjects.count { |s| s.send(@chart_variable.id) == chart_option.value }
|
37
|
+
count > 0 ? Spout::Helpers::TableFormatting.format_number(count, :count) : { text: '-', class: 'text-muted' }
|
33
38
|
end
|
34
39
|
|
35
|
-
|
40
|
+
row = [option.display_name] + row_cells
|
36
41
|
|
37
|
-
|
42
|
+
if @totals
|
43
|
+
total = row_subjects.count
|
44
|
+
row += [total == 0 ? { text: '-', class: 'text-muted' } : { text: Spout::Helpers::TableFormatting.format_number(total, :count), style: 'font-weight:bold' }]
|
45
|
+
end
|
46
|
+
row
|
38
47
|
end
|
39
48
|
|
40
|
-
if @filtered_subjects.
|
49
|
+
if @filtered_subjects.count { |s| s.send(@variable.id).is_a?(Spout::Models::Empty) } > 0
|
41
50
|
unknown_values = filtered_domain_options(@chart_variable).collect do |chart_option|
|
42
|
-
{ text: Spout::Helpers::TableFormatting
|
51
|
+
{ text: Spout::Helpers::TableFormatting.format_number(@filtered_subjects.count { |s| s.send(@chart_variable.id) == chart_option.value && s.send(@variable.id).is_a?(Spout::Models::Empty) }, :count), class: 'text-muted' }
|
52
|
+
end
|
53
|
+
unknown_row = [{ text: 'Unknown', class: 'text-muted' }] + unknown_values
|
54
|
+
if @totals
|
55
|
+
unknown_row += [{ text: Spout::Helpers::TableFormatting.format_number(@filtered_subjects.count { |s| s.send(@variable.id).is_a?(Spout::Models::Empty) }, :count), style: 'font-weight:bold', class: 'text-muted' }]
|
43
56
|
end
|
44
|
-
rows_result <<
|
57
|
+
rows_result << unknown_row
|
45
58
|
end
|
46
59
|
rows_result
|
47
60
|
end
|
48
|
-
|
49
61
|
end
|
50
62
|
end
|
51
63
|
end
|
@@ -4,45 +4,52 @@ require 'spout/helpers/array_statistics'
|
|
4
4
|
module Spout
|
5
5
|
module Models
|
6
6
|
module Tables
|
7
|
+
# Generates a table that displays choices versus numeric values
|
7
8
|
class ChoicesVsNumeric < Spout::Models::Tables::Default
|
8
|
-
|
9
9
|
def title
|
10
10
|
"#{@variable.display_name} vs #{@chart_variable.display_name}"
|
11
11
|
end
|
12
12
|
|
13
13
|
def headers
|
14
14
|
categories = [:quartile_one, :quartile_two, :quartile_three, :quartile_four].collect do |quartile|
|
15
|
-
bucket = @
|
15
|
+
bucket = @filtered_subjects.send(quartile).collect(&@chart_variable.id.to_sym)
|
16
16
|
"#{bucket.min} to #{bucket.max} #{@chart_variable.units}"
|
17
17
|
end
|
18
18
|
|
19
|
-
[
|
19
|
+
[[''] + categories + ['Total']]
|
20
20
|
end
|
21
21
|
|
22
22
|
def footers
|
23
23
|
total_values = [:quartile_one, :quartile_two, :quartile_three, :quartile_four].collect do |quartile|
|
24
|
-
{ text: Spout::Helpers::TableFormatting
|
24
|
+
{ text: Spout::Helpers::TableFormatting.format_number(@filtered_subjects.send(quartile).count, :count), style: 'font-weight:bold' }
|
25
25
|
end
|
26
26
|
|
27
27
|
[
|
28
|
-
[{ text:
|
28
|
+
[{ text: 'Total', style: 'font-weight:bold' }] + total_values + [{ text: Spout::Helpers::TableFormatting.format_number(@filtered_subjects.count, :count), style: 'font-weight:bold' }]
|
29
29
|
]
|
30
30
|
end
|
31
31
|
|
32
32
|
def rows
|
33
|
-
|
34
|
-
row_subjects = @
|
33
|
+
rows_result = filtered_domain_options(@variable).collect do |option|
|
34
|
+
row_subjects = @filtered_subjects.select { |s| s.send(@variable.id) == option.value }
|
35
35
|
|
36
36
|
data = [:quartile_one, :quartile_two, :quartile_three, :quartile_four].collect do |quartile|
|
37
|
-
|
38
|
-
Spout::Helpers::TableFormatting
|
37
|
+
count = @filtered_subjects.send(quartile).count { |s| s.send(@variable.id) == option.value }
|
38
|
+
Spout::Helpers::TableFormatting.format_number(count, :count)
|
39
39
|
end
|
40
40
|
|
41
|
-
[option.display_name] + data + [{ text: Spout::Helpers::TableFormatting
|
41
|
+
[option.display_name] + data + [{ text: Spout::Helpers::TableFormatting.format_number(row_subjects.count, :count), style: 'font-weight:bold' }]
|
42
42
|
end
|
43
|
-
end
|
44
|
-
|
45
43
|
|
44
|
+
if @filtered_subjects.count { |s| s.send(@variable.id).is_a?(Spout::Models::Empty) } > 0
|
45
|
+
unknown_values = [:quartile_one, :quartile_two, :quartile_three, :quartile_four].collect do |quartile|
|
46
|
+
count = @filtered_subjects.send(quartile).count { |s| s.send(@variable.id).is_a?(Spout::Models::Empty) }
|
47
|
+
{ text: Spout::Helpers::TableFormatting.format_number(count, :count), class: 'text-muted' }
|
48
|
+
end
|
49
|
+
rows_result << [{ text: 'Unknown', class: 'text-muted'}] + unknown_values + [{ text: Spout::Helpers::TableFormatting.format_number(@filtered_subjects.count { |s| s.send(@variable.id).is_a?(Spout::Models::Empty) }, :count), style: 'font-weight:bold', class: 'text-muted' }]
|
50
|
+
end
|
51
|
+
rows_result
|
52
|
+
end
|
46
53
|
end
|
47
54
|
end
|
48
55
|
end
|
@@ -5,41 +5,36 @@ module Spout
|
|
5
5
|
module Models
|
6
6
|
module Tables
|
7
7
|
class Default
|
8
|
+
attr_reader :variable, :chart_variable, :subjects, :subtitle, :totals
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
def initialize(variable, chart_variable, subjects, subtitle)
|
10
|
+
def initialize(variable, chart_variable, subjects, subtitle, totals)
|
12
11
|
@variable = variable
|
13
12
|
@chart_variable = chart_variable
|
14
|
-
@subjects = subjects
|
15
13
|
@subtitle = subtitle
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
14
|
+
@totals = totals
|
15
|
+
begin
|
16
|
+
@filtered_subjects = subjects.reject { |s| s.send(@chart_variable.id).is_a?(Spout::Models::Empty) }.sort_by(&@chart_variable.id.to_sym)
|
17
|
+
rescue
|
18
|
+
@filtered_subjects = []
|
19
|
+
end
|
20
|
+
begin
|
21
|
+
@values_unique = @filtered_subjects.collect(&@variable.id.to_sym).uniq
|
22
|
+
rescue
|
23
|
+
@values_unique = []
|
24
|
+
end
|
26
25
|
end
|
27
26
|
|
28
27
|
def to_hash
|
29
|
-
if valid?
|
30
|
-
{ title: title, subtitle: @subtitle, headers: headers, footers: footers, rows: rows }
|
31
|
-
else
|
32
|
-
nil
|
33
|
-
end
|
28
|
+
{ title: title, subtitle: @subtitle, headers: headers, footers: footers, rows: rows } if valid?
|
34
29
|
end
|
35
30
|
|
36
31
|
# TODO: Same as graphables/default.rb REFACTOR
|
37
32
|
def valid?
|
38
|
-
if @variable
|
33
|
+
if @variable.nil? || @chart_variable.nil? || @values_unique == []
|
39
34
|
false
|
40
|
-
elsif @variable.type == 'choices'
|
35
|
+
elsif @variable.type == 'choices' && @variable.domain.options == []
|
41
36
|
false
|
42
|
-
elsif @chart_variable.type == 'choices'
|
37
|
+
elsif @chart_variable.type == 'choices' && @chart_variable.domain.options == []
|
43
38
|
false
|
44
39
|
else
|
45
40
|
true
|
@@ -47,7 +42,7 @@ module Spout
|
|
47
42
|
end
|
48
43
|
|
49
44
|
def title
|
50
|
-
|
45
|
+
''
|
51
46
|
end
|
52
47
|
|
53
48
|
def headers
|
@@ -62,7 +57,6 @@ module Spout
|
|
62
57
|
[]
|
63
58
|
end
|
64
59
|
|
65
|
-
|
66
60
|
private
|
67
61
|
|
68
62
|
# Returns variable options that are either:
|
@@ -70,16 +64,9 @@ module Spout
|
|
70
64
|
# b) or are marked as missing codes but represented in the dataset
|
71
65
|
def filtered_domain_options(variable)
|
72
66
|
variable.domain.options.select do |o|
|
73
|
-
o.missing != true
|
67
|
+
o.missing != true || (o.missing == true && @values_unique.include?(o.value))
|
74
68
|
end
|
75
69
|
end
|
76
|
-
|
77
|
-
def filtered_both_variables_domain_options(variable)
|
78
|
-
variable.domain.options.select do |o|
|
79
|
-
o.missing != true or (o.missing == true and @values_both_variables_unique.include?(o.value))
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
70
|
end
|
84
71
|
end
|
85
72
|
end
|
@@ -5,42 +5,38 @@ module Spout
|
|
5
5
|
module Models
|
6
6
|
module Tables
|
7
7
|
class NumericVsChoices < Spout::Models::Tables::Default
|
8
|
-
|
9
8
|
def title
|
10
9
|
"#{@chart_variable.display_name} vs #{@variable.display_name}"
|
11
10
|
end
|
12
11
|
|
13
12
|
def headers
|
14
|
-
[
|
15
|
-
[""] + Spout::Helpers::ArrayStatistics::calculations.collect{|calculation_label, calculation_method| calculation_label} + ["Total"]
|
16
|
-
]
|
13
|
+
[[''] + Spout::Helpers::ArrayStatistics.calculations.collect(&:first) + ['Total']]
|
17
14
|
end
|
18
15
|
|
19
16
|
def footers
|
20
|
-
|
17
|
+
return [] unless @totals
|
18
|
+
total_values = Spout::Helpers::ArrayStatistics.calculations.collect do |_calculation_label, calculation_method, calculation_type, calculation_format|
|
21
19
|
total_count = @filtered_subjects.collect(&@variable.id.to_sym).send(calculation_method)
|
22
|
-
{ text: Spout::Helpers::TableFormatting
|
20
|
+
{ text: Spout::Helpers::TableFormatting.format_number(total_count, calculation_type, calculation_format), style: 'font-weight:bold' }
|
23
21
|
end
|
24
22
|
|
25
23
|
[
|
26
|
-
[{ text:
|
24
|
+
[{ text: 'Total', style: 'font-weight:bold' }] + total_values + [{ text: Spout::Helpers::TableFormatting.format_number(@filtered_subjects.count, :count), style: 'font-weight:bold' }]
|
27
25
|
]
|
28
26
|
end
|
29
27
|
|
30
28
|
def rows
|
31
29
|
filtered_domain_options(@chart_variable).collect do |option|
|
32
|
-
row_subjects = @filtered_subjects.select{ |s| s.send(@chart_variable.id) == option.value }
|
30
|
+
row_subjects = @filtered_subjects.select { |s| s.send(@chart_variable.id) == option.value }
|
33
31
|
|
34
|
-
row_cells = Spout::Helpers::ArrayStatistics
|
32
|
+
row_cells = Spout::Helpers::ArrayStatistics.calculations.collect do |_calculation_label, calculation_method, calculation_type, calculation_format|
|
35
33
|
count = row_subjects.collect(&@variable.id.to_sym).send(calculation_method)
|
36
|
-
(count == 0 && calculation_method == :count) ? { text: '-', class: 'text-muted' } : Spout::Helpers::TableFormatting
|
34
|
+
(count == 0 && calculation_method == :count) ? { text: '-', class: 'text-muted' } : Spout::Helpers::TableFormatting.format_number(count, calculation_type, calculation_format)
|
37
35
|
end
|
38
36
|
|
39
|
-
[option.display_name] + row_cells + [{ text: Spout::Helpers::TableFormatting
|
37
|
+
[option.display_name] + row_cells + [{ text: Spout::Helpers::TableFormatting.format_number(row_subjects.count, :count), style: 'font-weight:bold' }]
|
40
38
|
end
|
41
39
|
end
|
42
|
-
|
43
|
-
|
44
40
|
end
|
45
41
|
end
|
46
42
|
end
|
@@ -5,37 +5,36 @@ module Spout
|
|
5
5
|
module Models
|
6
6
|
module Tables
|
7
7
|
class NumericVsNumeric < Spout::Models::Tables::Default
|
8
|
-
|
9
8
|
def title
|
10
9
|
"#{@chart_variable.display_name} vs #{@variable.display_name}"
|
11
10
|
end
|
12
11
|
|
13
12
|
def headers
|
14
|
-
[
|
13
|
+
[[''] + Spout::Helpers::ArrayStatistics.calculations.collect(&:first) + ['Total']]
|
15
14
|
end
|
16
15
|
|
17
16
|
def footers
|
18
|
-
total_values = Spout::Helpers::ArrayStatistics
|
19
|
-
total_count = @
|
20
|
-
{ text: Spout::Helpers::TableFormatting
|
17
|
+
total_values = Spout::Helpers::ArrayStatistics.calculations.collect do |_calculation_label, calculation_method, calculation_type, calculation_format|
|
18
|
+
total_count = @filtered_subjects.collect(&@variable.id.to_sym).send(calculation_method)
|
19
|
+
{ text: Spout::Helpers::TableFormatting.format_number(total_count, calculation_type, calculation_format), style: 'font-weight:bold' }
|
21
20
|
end
|
22
21
|
|
23
22
|
[
|
24
|
-
[{ text:
|
23
|
+
[{ text: 'Total', style: 'font-weight:bold' }] + total_values + [{ text: Spout::Helpers::TableFormatting.format_number(@filtered_subjects.count, :count), style: 'font-weight:bold' }]
|
25
24
|
]
|
26
25
|
end
|
27
26
|
|
28
27
|
def rows
|
29
28
|
[:quartile_one, :quartile_two, :quartile_three, :quartile_four].collect do |quartile|
|
30
|
-
bucket = @
|
29
|
+
bucket = @filtered_subjects.send(quartile)
|
31
30
|
row_subjects = bucket.collect(&@variable.id.to_sym)
|
32
|
-
data = Spout::Helpers::ArrayStatistics
|
33
|
-
Spout::Helpers::TableFormatting
|
31
|
+
data = Spout::Helpers::ArrayStatistics.calculations.collect do |_calculation_label, calculation_method, calculation_type, calculation_format|
|
32
|
+
Spout::Helpers::TableFormatting.format_number(row_subjects.send(calculation_method), calculation_type, calculation_format)
|
34
33
|
end
|
35
34
|
|
36
35
|
row_name = get_row_name(quartile, bucket, row_subjects)
|
37
36
|
|
38
|
-
[row_name] + data + [{ text: Spout::Helpers::TableFormatting
|
37
|
+
[row_name] + data + [{ text: Spout::Helpers::TableFormatting.format_number(row_subjects.count, :count), style: 'font-weight:bold' }]
|
39
38
|
end
|
40
39
|
end
|
41
40
|
|
@@ -48,7 +47,6 @@ module Spout
|
|
48
47
|
"#{bucket.collect(&@chart_variable.id.to_sym).min} to #{bucket.collect(&@chart_variable.id.to_sym).max} #{@chart_variable.units}"
|
49
48
|
end
|
50
49
|
end
|
51
|
-
|
52
50
|
end
|
53
51
|
end
|
54
52
|
end
|