spout 0.10.2 → 0.11.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|