spout 0.10.0.beta9 → 0.10.0.beta10

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
  SHA1:
3
- metadata.gz: 1510bf903f7dada97ac4f63538881fe5cddb0825
4
- data.tar.gz: 8678041008439bd0eaea5e163eb6d4fc6337469b
3
+ metadata.gz: cb6c2e9b16149cba7a217e1e5dfec5764ac33e8a
4
+ data.tar.gz: e1a7fbd00c9c5678089d8d6266e791121fc2d468
5
5
  SHA512:
6
- metadata.gz: 390ca815b08364e3d3f8e068dd1104d784bdc48eb85bd2da9fc6e3d44562aec5ac75e22524d0580c1f0dba8ffec341021e463ac9b57d627ac7ae6b8e85d846f4
7
- data.tar.gz: fa7bf7eb06e667c972354bd7b81f4d32e7f0c37de0a7115c06f484361943a7a9b737020b9434c4b7d132403035aa9c9f1caf89fccdfd3cf87e8b0357163fe935
6
+ metadata.gz: b93e28aa28e19ef8d3bec0fbc5107e8bae5b835eaca307bac36dec4c151ee3c7c7e27ba0ff8f050d81d1538256490b8cb786936b4d61acb1c71980da7860b662
7
+ data.tar.gz: 9e7d49b85dc352e0289de396ef75bfdebec45064a05ef992c2efe872519c61c901cc4811f60ea48c123dfa4b67c86aa634270e54c8f7a4dd1bec08f6281d143c
data/CHANGELOG.md CHANGED
@@ -15,6 +15,11 @@
15
15
  - **Testing Changes**
16
16
  - Tests now include checks to assure that variable display_name fields don't exceed 255 length requirement
17
17
  - `include Spout::Tests::VariableDisplayNameLength`
18
+ - **Gem Changes**
19
+ - Use of Ruby 2.1.4 is now recommended
20
+
21
+ ### Refactoring
22
+ - Removing ChartTypes class in favor of a more modular Graph and Table class
18
23
 
19
24
  ## 0.9.1 (October 14, 2014)
20
25
 
@@ -7,6 +7,9 @@ require 'colorize'
7
7
 
8
8
  require 'spout/helpers/subject_loader'
9
9
  require 'spout/helpers/chart_types'
10
+ require 'spout/models/variable'
11
+ require 'spout/models/graphables'
12
+ require 'spout/models/tables'
10
13
  require 'spout/helpers/config_reader'
11
14
  require 'spout/helpers/send_file'
12
15
  require 'spout/version'
@@ -27,7 +30,9 @@ module Spout
27
30
 
28
31
  @config = Spout::Helpers::ConfigReader.new
29
32
 
30
- if Spout::Helpers::ChartTypes::get_json(@config.visit, 'variable') == nil
33
+ @stratification_variable = Spout::Models::Variable.find_by_id @config.visit
34
+
35
+ if @stratification_variable == nil
31
36
  if @config.visit == ''
32
37
  puts "The visit variable in .spout.yml can't be blank."
33
38
  else
@@ -36,7 +41,7 @@ module Spout
36
41
  return self
37
42
  end
38
43
 
39
- missing_variables = @config.charts.select{|c| Spout::Helpers::ChartTypes::get_json(c['chart'], 'variable') == nil}
44
+ missing_variables = @config.charts.select{|c| Spout::Models::Variable.find_by_id(c['chart']) == nil}
40
45
  if missing_variables.count > 0
41
46
  puts "Could not find the following chart variable#{'s' unless missing_variables.size == 1}: #{missing_variables.join(', ')}"
42
47
  return self
@@ -54,7 +59,8 @@ module Spout
54
59
 
55
60
  @chart_variables = @config.charts.unshift( { "chart" => @config.visit, "title" => 'Histogram' } )
56
61
 
57
- @variable_files = Dir.glob('variables/**/*.json')
62
+ @dictionary_root = Dir.pwd
63
+ @variable_files = Dir.glob(File.join(@dictionary_root, 'variables', '**', '*.json'))
58
64
 
59
65
  t = Time.now
60
66
  @graphs_folder = File.join("graphs", @standard_version)
@@ -87,22 +93,23 @@ module Spout
87
93
 
88
94
  def compute_tables_and_charts
89
95
  variable_files_count = @variable_files.count
96
+
90
97
  @variable_files.each_with_index do |variable_file, file_index|
91
- json = JSON.parse(File.read(variable_file)) rescue json = nil
92
- next unless json
93
- next unless @valid_ids.include?(json["id"].to_s.downcase) or @valid_ids.size == 0
94
- next unless ["numeric", "integer", "choices"].include?(json["type"])
95
- variable_name = json['id'].to_s.downcase
96
- next unless Spout::Models::Subject.method_defined?(variable_name)
98
+ variable = Spout::Models::Variable.new(variable_file, @dictionary_root)
99
+
100
+ next unless variable.errors.size == 0
101
+ next unless @valid_ids.include?(variable.id) or @valid_ids.size == 0
102
+ next unless ["numeric", "integer", "choices"].include?(variable.type)
103
+ next unless Spout::Models::Subject.method_defined?(variable.id)
97
104
 
98
105
  if @deploy_mode
99
106
  print "\r Graph Generation: " + "#{"% 3d" % ((file_index+1)*100/variable_files_count)}% Uploaded".colorize(:white)
100
107
  else
101
- puts "#{file_index+1} of #{variable_files_count}: #{variable_file.gsub(/(^variables\/|\.json$)/, '').gsub('/', ' / ')}"
108
+ puts "#{file_index+1} of #{variable_files_count}: #{variable.folder}#{variable.id}"
102
109
  end
103
110
 
104
- @progress[variable_name] ||= {}
105
- next if (not @deploy_mode and @progress[variable_name]['generated'] == true) or (@deploy_mode and @progress[variable_name]['uploaded'] == true)
111
+ @progress[variable.id] ||= {}
112
+ next if (not @deploy_mode and @progress[variable.id]['generated'] == true) or (@deploy_mode and @progress[variable.id]['uploaded'] == true)
106
113
 
107
114
  stats = {
108
115
  charts: {},
@@ -112,38 +119,43 @@ module Spout
112
119
  @chart_variables.each do |chart_type_hash|
113
120
  chart_type = chart_type_hash["chart"]
114
121
  chart_title = chart_type_hash["title"].downcase.gsub(' ', '-')
122
+ chart_variable = Spout::Models::Variable.find_by_id(chart_type)
115
123
 
116
124
  if chart_type == @config.visit
117
- filtered_subjects = @subjects.select{ |s| s.send(chart_type) != nil } # and s.send(variable_name) != nil
125
+ filtered_subjects = @subjects.select{ |s| s.send(chart_type) != nil }
118
126
  if filtered_subjects.count > 0
119
- stats[:charts][chart_title] = Spout::Helpers::ChartTypes::chart_histogram(chart_type, filtered_subjects, json, variable_name)
120
- stats[:tables][chart_title] = Spout::Helpers::ChartTypes::table_arbitrary(chart_type, filtered_subjects, json, variable_name)
127
+ graph = Spout::Models::Graphables.for(variable, chart_variable, nil, filtered_subjects)
128
+ stats[:charts][chart_title] = graph.to_hash
129
+ table = Spout::Models::Tables.for(variable, chart_variable, filtered_subjects, nil)
130
+ stats[:tables][chart_title] = table.to_hash
121
131
  end
122
132
  else
123
- filtered_subjects = @subjects.select{ |s| s.send(chart_type) != nil } # and s.send(variable_name) != nil
124
- if filtered_subjects.collect(&variable_name.to_sym).compact.count > 0
125
- stats[:charts][chart_title] = Spout::Helpers::ChartTypes::chart_arbitrary(chart_type, filtered_subjects, json, variable_name, visits)
126
- stats[:tables][chart_title] = visits.collect do |visit_display_name, visit_value|
127
- visit_subjects = filtered_subjects.select{ |s| s._visit == visit_value }
128
- unknown_subjects = visit_subjects.select{ |s| s.send(variable_name) == nil }
129
- (visit_subjects.count > 0 && visit_subjects.count != unknown_subjects.count) ? Spout::Helpers::ChartTypes::table_arbitrary(chart_type, visit_subjects, json, variable_name, visit_display_name) : nil
133
+ filtered_subjects = @subjects.select{ |s| s.send(chart_type) != nil }
134
+ if filtered_subjects.collect(&variable.id.to_sym).compact.count > 0
135
+ graph = Spout::Models::Graphables.for(variable, chart_variable, @stratification_variable, filtered_subjects)
136
+ stats[:charts][chart_title] = graph.to_hash
137
+ stats[:tables][chart_title] = @stratification_variable.domain.options.collect do |option|
138
+ visit_subjects = filtered_subjects.select{ |s| s._visit == option.value }
139
+ unknown_subjects = visit_subjects.select{ |s| s.send(variable.id) == nil }
140
+ table = Spout::Models::Tables.for(variable, chart_variable, visit_subjects, option.display_name)
141
+ (visit_subjects.count > 0 && visit_subjects.count != unknown_subjects.count) ? table.to_hash : nil
130
142
  end.compact
131
143
  end
132
144
  end
133
145
  end
134
146
 
135
- chart_json_file = File.join(@graphs_folder, "#{json['id']}.json")
147
+ chart_json_file = File.join(@graphs_folder, "#{variable.id}.json")
136
148
  File.open(chart_json_file, 'w') { |file| file.write( JSON.pretty_generate(stats) + "\n" ) }
137
149
 
138
- @progress[variable_name]['generated'] = true
150
+ @progress[variable.id]['generated'] = true
139
151
 
140
- if @deploy_mode and not @progress[variable_name]['uploaded'] == true
152
+ if @deploy_mode and not @progress[variable.id]['uploaded'] == true
141
153
  response = send_to_server(chart_json_file)
142
154
  if response.kind_of?(Hash) and response['upload'] == 'success'
143
- @progress[variable_name]['uploaded'] = true
155
+ @progress[variable.id]['uploaded'] = true
144
156
  else
145
157
  puts "\nUPLOAD FAILED: ".colorize(:red) + File.basename(chart_json_file)
146
- @progress[variable_name]['uploaded'] = false
158
+ @progress[variable.id]['uploaded'] = false
147
159
  end
148
160
  end
149
161
 
@@ -156,13 +168,6 @@ module Spout
156
168
  response = Spout::Helpers::SendFile.post("#{@url}/datasets/#{@slug}/upload_graph.json", chart_json_file, @standard_version, @token)
157
169
  end
158
170
 
159
- # [["Visit 1", "1"], ["Visit 2", "2"], ["CVD Outcomes", "3"]]
160
- def visits
161
- @visits ||= begin
162
- Spout::Helpers::ChartTypes::domain_array(@config.visit)
163
- end
164
- end
165
-
166
171
  end
167
172
  end
168
173
  end
@@ -4,6 +4,8 @@ require 'rubygems'
4
4
  require 'json'
5
5
  require 'yaml'
6
6
 
7
+ require 'spout/models/variable'
8
+ require 'spout/models/graphables'
7
9
  require 'spout/helpers/subject_loader'
8
10
  require 'spout/helpers/chart_types'
9
11
  require 'spout/helpers/config_reader'
@@ -68,7 +70,9 @@ module Spout
68
70
  FileUtils.mkpath( options_folder )
69
71
  tmp_options_file = File.join( options_folder, 'options.json' )
70
72
 
73
+ chart_variable = Spout::Models::Variable.find_by_id(@config.visit)
71
74
  variable_files_count = @variable_files.count
75
+
72
76
  @variable_files.each_with_index do |variable_file, file_index|
73
77
  json = JSON.parse(File.read(variable_file)) rescue json = nil
74
78
  next unless json
@@ -90,9 +94,10 @@ module Spout
90
94
 
91
95
  filtered_subjects = @subjects.select{ |s| s.send(@config.visit) != nil }
92
96
 
93
- chart_json = Spout::Helpers::ChartTypes::chart_histogram(@config.visit, filtered_subjects, json, variable_name)
97
+ variable = Spout::Models::Variable.find_by_id variable_name
98
+ graph = Spout::Models::Graphables.for(variable, chart_variable, nil, filtered_subjects)
94
99
 
95
- if chart_json
100
+ if graph.valid?
96
101
  File.open(tmp_options_file, "w") do |outfile|
97
102
  outfile.puts <<-eos
98
103
  {
@@ -106,21 +111,21 @@ module Spout
106
111
  "text": ""
107
112
  },
108
113
  "xAxis": {
109
- "categories": #{chart_json[:categories].to_json}
114
+ "categories": #{graph.categories.to_json}
110
115
  },
111
116
  "yAxis": {
112
117
  "title": {
113
- "text": #{chart_json[:units].to_json}
118
+ "text": #{graph.units.to_json}
114
119
  }
115
120
  },
116
121
  "plotOptions": {
117
122
  "column": {
118
123
  "pointPadding": 0.2,
119
124
  "borderWidth": 0,
120
- "stacking": #{chart_json[:stacking].to_json}
125
+ "stacking": #{graph.stacking.to_json}
121
126
  }
122
127
  },
123
- "series": #{chart_json[:series].to_json}
128
+ "series": #{graph.series.to_json}
124
129
  }
125
130
  eos
126
131
  end
@@ -33,389 +33,6 @@ module Spout
33
33
  end
34
34
  buckets
35
35
  end
36
-
37
- def self.get_json(file_name, file_type)
38
- file = Dir.glob("#{file_type.to_s.downcase}s/**/#{file_name.to_s.downcase}.json", File::FNM_CASEFOLD).first
39
- json = JSON.parse(File.read(file)) rescue json = nil
40
- json
41
- end
42
-
43
- def self.get_variable(variable_name)
44
- get_json(variable_name, 'variable')
45
- end
46
-
47
- def self.get_domain(json)
48
- get_json(json['domain'], 'domain')
49
- end
50
-
51
- def self.domain_array(variable_name)
52
- variable_file = Dir.glob("variables/**/#{variable_name.to_s.downcase}.json", File::FNM_CASEFOLD).first
53
- json = JSON.parse(File.read(variable_file)) rescue json = nil
54
- if json
55
- domain_json = get_domain(json)
56
- domain_json ? domain_json.collect{|option_hash| [option_hash['display_name'], option_hash['value']]} : []
57
- else
58
- []
59
- end
60
- end
61
-
62
- def self.chart_arbitrary_choices_by_quartile(chart_type, subjects, json, method)
63
- # CHART TYPE IS THE QUARTILE VARIABLE
64
- return unless chart_variable_json = get_variable(chart_type)
65
- return unless domain_json = get_domain(json)
66
-
67
- title = "#{json['display_name']} by #{chart_variable_json['display_name']}"
68
- subtitle = "By Visit"
69
- # categories = ["Quartile One", "Quartile Two", "Quartile Three", "Quartile Four"]
70
- units = 'percent'
71
- series = []
72
-
73
- filtered_subjects = subjects.select{ |s| s.send(method) != nil and s.send(chart_type) != nil }.sort_by(&chart_type.to_sym)
74
-
75
- all_subject_values = filtered_subjects.collect(&method.to_sym).compact.sort
76
- domain_json = remove_unused_missing_codes_from_domain(domain_json, all_subject_values.uniq)
77
-
78
- categories = [:quartile_one, :quartile_two, :quartile_three, :quartile_four].collect do |quartile|
79
- bucket = filtered_subjects.send(quartile).collect(&chart_type.to_sym)
80
- "#{bucket.min} to #{bucket.max}"
81
- end
82
-
83
- domain_json.each do |option_hash|
84
- data = [:quartile_one, :quartile_two, :quartile_three, :quartile_four].collect do |quartile|
85
- filtered_subjects.send(quartile).select{ |s| s.send(method) == option_hash['value'] }.count
86
- end
87
-
88
- series << { name: option_hash['display_name'], data: data } unless filtered_subjects.size == 0
89
- end
90
-
91
- { title: title, subtitle: subtitle, categories: categories, units: units, series: series, stacking: 'percent' }
92
- end
93
-
94
- def self.chart_arbitrary_by_quartile(chart_type, subjects, json, method, visits)
95
- # CHART TYPE IS THE QUARTILE VARIABLE
96
- return unless chart_variable_json = get_variable(chart_type)
97
- return chart_arbitrary_choices_by_quartile(chart_type, subjects, json, method) if json['type'] == 'choices'
98
-
99
- title = "#{json['display_name']} by #{chart_variable_json['display_name']}"
100
- subtitle = "By Visit"
101
- categories = ["Quartile One", "Quartile Two", "Quartile Three", "Quartile Four"]
102
- units = json["units"]
103
- series = []
104
-
105
- series = []
106
-
107
- visits.each do |visit_display_name, visit_value|
108
- data = []
109
- filtered_subjects = subjects.select{ |s| s._visit == visit_value and s.send(method) != nil and s.send(chart_type) != nil }.sort_by(&chart_type.to_sym)
110
-
111
- [:quartile_one, :quartile_two, :quartile_three, :quartile_four].each do |quartile|
112
- array = filtered_subjects.send(quartile).collect(&method.to_sym)
113
- data << { y: (array.mean.round(1) rescue 0.0),
114
- stddev: ("%0.1f" % array.standard_deviation rescue ''),
115
- median: ("%0.1f" % array.median rescue ''),
116
- min: ("%0.1f" % array.min rescue ''),
117
- max: ("%0.1f" % array.max rescue ''),
118
- n: array.n }
119
- end
120
-
121
- series << { name: visit_display_name, data: data } unless filtered_subjects.size == 0
122
- end
123
-
124
- { title: title, subtitle: subtitle, categories: categories, units: units, series: series }
125
- end
126
-
127
- def self.table_arbitrary_by_quartile(chart_type, subjects, json, method, subtitle = nil)
128
- return table_arbitrary_choices_by_quartile(chart_type, subjects, json, method, subtitle) if json['type'] == 'choices'
129
- # CHART TYPE IS THE QUARTILE VARIABLE
130
- return unless chart_variable_json = get_variable(chart_type)
131
-
132
-
133
- headers = [
134
- [""] + Spout::Helpers::ArrayStatistics::calculations.collect{|calculation_label, calculation_method| calculation_label} + ["Total"]
135
- ]
136
-
137
- filtered_subjects = subjects.select{ |s| s.send(method) != nil and s.send(chart_type) != nil }.sort_by(&chart_type.to_sym)
138
-
139
- rows = [:quartile_one, :quartile_two, :quartile_three, :quartile_four].collect do |quartile|
140
- bucket = filtered_subjects.send(quartile)
141
- row_subjects = bucket.collect(&method.to_sym)
142
- data = Spout::Helpers::ArrayStatistics::calculations.collect do |calculation_label, calculation_method, calculation_type, calculation_format|
143
- TableFormatting::format_number(row_subjects.send(calculation_method), calculation_type, calculation_format)
144
- end
145
-
146
- row_name = if row_subjects.size == 0
147
- quartile.to_s.capitalize.gsub('_one', ' One').gsub('_two', ' Two').gsub('_three', ' Three').gsub('_four', ' Four')
148
- else
149
- "#{bucket.collect(&chart_type.to_sym).min} to #{bucket.collect(&chart_type.to_sym).max} #{chart_variable_json['units']}"
150
- end
151
-
152
- [row_name] + data + [{ text: TableFormatting::format_number(row_subjects.count, :count), style: 'font-weight:bold'}]
153
- end
154
-
155
- total_values = Spout::Helpers::ArrayStatistics::calculations.collect do |calculation_label, calculation_method, calculation_type, calculation_format|
156
- total_count = filtered_subjects.collect(&method.to_sym).send(calculation_method)
157
- { text: TableFormatting::format_number(total_count, calculation_type, calculation_format), style: "font-weight:bold" }
158
- end
159
-
160
- footers = [
161
- [{ text: "Total", style: "font-weight:bold" }] + total_values + [{ text: TableFormatting::format_number(filtered_subjects.count, :count), style: 'font-weight:bold'}]
162
- ]
163
-
164
- { title: "#{chart_variable_json['display_name']} vs #{json['display_name']}", subtitle: subtitle, headers: headers, footers: footers, rows: rows }
165
-
166
- end
167
-
168
- def self.table_arbitrary_choices_by_quartile(chart_type, subjects, json, method, subtitle)
169
- # CHART TYPE IS THE QUARTILE VARIABLE
170
- return unless chart_variable_json = get_variable(chart_type)
171
- return unless domain_json = get_domain(json)
172
-
173
- filtered_subjects = subjects.select{ |s| s.send(method) != nil and s.send(chart_type) != nil }.sort_by(&chart_type.to_sym)
174
- all_subject_values = filtered_subjects.collect(&method.to_sym).compact.sort
175
- domain_json = remove_unused_missing_codes_from_domain(domain_json, all_subject_values.uniq)
176
-
177
- categories = [:quartile_one, :quartile_two, :quartile_three, :quartile_four].collect do |quartile|
178
- bucket = filtered_subjects.send(quartile).collect(&chart_type.to_sym)
179
- "#{bucket.min} to #{bucket.max} #{chart_variable_json['units']}"
180
- end
181
-
182
- headers = [
183
- [""] + categories + ["Total"]
184
- ]
185
-
186
- rows = []
187
-
188
- rows = domain_json.collect do |option_hash|
189
- row_subjects = filtered_subjects.select{ |s| s.send(method) == option_hash['value'] }
190
-
191
- data = [:quartile_one, :quartile_two, :quartile_three, :quartile_four].collect do |quartile|
192
- bucket = filtered_subjects.send(quartile).select{ |s| s.send(method) == option_hash['value'] }
193
- TableFormatting::format_number(bucket.count, :count)
194
- end
195
-
196
- [option_hash['display_name']] + data + [{ text: TableFormatting::format_number(row_subjects.count, :count), style: 'font-weight:bold'}]
197
- end
198
-
199
-
200
- total_values = [:quartile_one, :quartile_two, :quartile_three, :quartile_four].collect do |quartile|
201
- { text: TableFormatting::format_number(filtered_subjects.send(quartile).count, :count), style: "font-weight:bold" }
202
- end
203
-
204
- footers = [
205
- [{ text: "Total", style: "font-weight:bold" }] + total_values + [{ text: TableFormatting::format_number(filtered_subjects.count, :count), style: 'font-weight:bold'}]
206
- ]
207
-
208
- { title: "#{json['display_name']} vs #{chart_variable_json['display_name']}", subtitle: subtitle, headers: headers, footers: footers, rows: rows }
209
- end
210
-
211
-
212
- def self.chart_arbitrary_choices(chart_type, subjects, json, method)
213
- return unless chart_variable_json = get_variable(chart_type)
214
- return unless chart_variable_domain = domain_array(chart_type)
215
- return unless domain_json = get_domain(json)
216
-
217
-
218
- title = "#{json['display_name']} by #{chart_variable_json['display_name']}"
219
- subtitle = "By Visit"
220
- categories = chart_variable_domain.collect{|a| a[0]}
221
- units = 'percent'
222
- series = []
223
-
224
- all_subject_values = subjects.collect(&method.to_sym).compact.sort
225
- domain_json = remove_unused_missing_codes_from_domain(domain_json, all_subject_values.uniq)
226
-
227
- domain_json.each do |option_hash|
228
- domain_values = subjects.select{ |s| s.send(method) == option_hash['value'] }
229
-
230
- data = chart_variable_domain.collect do |display_name, value|
231
- domain_values.select{ |s| s.send(chart_type) == value }.count
232
- end
233
- series << { name: option_hash['display_name'], data: data }
234
- end
235
-
236
- { title: title, subtitle: subtitle, categories: categories, units: units, series: series, stacking: 'percent' }
237
- end
238
-
239
- def self.chart_arbitrary(chart_type, subjects, json, method, visits)
240
- return unless chart_variable_json = get_variable(chart_type)
241
- return unless chart_variable_domain = domain_array(chart_type)
242
- return chart_arbitrary_by_quartile(chart_type, subjects, json, method, visits) if ['numeric', 'integer'].include?(chart_variable_json['type'])
243
-
244
- return chart_arbitrary_choices(chart_type, subjects, json, method) if json['type'] == 'choices'
245
-
246
- title = "#{json['display_name']} by #{chart_variable_json['display_name']}"
247
- subtitle = "By Visit"
248
- categories = []
249
- units = json["units"]
250
- series = []
251
-
252
- data = []
253
-
254
- visits.each do |visit_display_name, visit_value|
255
- visit_subjects = subjects.select{ |s| s._visit == visit_value and s.send(method) != nil }
256
- if visit_subjects.count > 0
257
- chart_variable_domain.each_with_index do |(display_name, value), index|
258
- values = visit_subjects.select{|s| s.send(chart_type) == value }.collect(&method.to_sym)
259
- data[index] ||= []
260
- data[index] << (values.mean.round(2) rescue 0.0)
261
- end
262
- categories << visit_display_name
263
- end
264
- end
265
-
266
- chart_variable_domain.each_with_index do |(display_name, value), index|
267
- series << { name: display_name, data: data[index] }
268
- end
269
-
270
- { title: title, subtitle: subtitle, categories: categories, units: units, series: series }
271
- end
272
-
273
-
274
- def self.table_arbitrary(chart_type, subjects, json, method, subtitle = nil)
275
- return unless chart_variable_json = get_variable(chart_type)
276
- return unless chart_variable_domain = domain_array(chart_type)
277
- return table_arbitrary_by_quartile(chart_type, subjects, json, method, subtitle) if ['numeric', 'integer'].include?(chart_variable_json['type'])
278
- return table_arbitrary_choices(chart_type, subjects, json, method, subtitle) if json['type'] == 'choices'
279
-
280
- headers = [
281
- [""] + Spout::Helpers::ArrayStatistics::calculations.collect{|calculation_label, calculation_method| calculation_label} + ["Total"]
282
- ]
283
-
284
- filtered_subjects = subjects.select{ |s| s.send(chart_type) != nil }
285
-
286
- rows = chart_variable_domain.collect do |display_name, value|
287
- row_subjects = filtered_subjects.select{ |s| s.send(chart_type) == value }
288
-
289
- row_cells = Spout::Helpers::ArrayStatistics::calculations.collect do |calculation_label, calculation_method, calculation_type, calculation_format|
290
- count = row_subjects.collect(&method.to_sym).send(calculation_method)
291
- (count == 0 && calculation_method == :count) ? { text: '-', class: 'text-muted' } : TableFormatting::format_number(count, calculation_type, calculation_format)
292
- end
293
-
294
- [display_name] + row_cells + [{ text: TableFormatting::format_number(row_subjects.count, :count), style: 'font-weight:bold'}]
295
- end
296
-
297
- total_values = Spout::Helpers::ArrayStatistics::calculations.collect do |calculation_label, calculation_method, calculation_type, calculation_format|
298
- total_count = filtered_subjects.collect(&method.to_sym).send(calculation_method)
299
- { text: TableFormatting::format_number(total_count, calculation_type, calculation_format), style: "font-weight:bold" }
300
- end
301
-
302
- footers = [
303
- [{ text: "Total", style: "font-weight:bold" }] + total_values + [{ text: TableFormatting::format_number(filtered_subjects.count, :count), style: 'font-weight:bold'}]
304
- ]
305
-
306
- { title: "#{chart_variable_json['display_name']} vs #{json['display_name']}", subtitle: subtitle, headers: headers, footers: footers, rows: rows }
307
-
308
- end
309
-
310
- def self.table_arbitrary_choices(chart_type, subjects, json, method, subtitle)
311
- return unless chart_variable_json = get_variable(chart_type)
312
- return unless chart_variable_domain = domain_array(chart_type)
313
- return unless domain_json = get_domain(json)
314
-
315
- headers = [
316
- [""] + chart_variable_domain.collect{|display_name, value| display_name} + ["Total"]
317
- ]
318
-
319
- filtered_subjects = subjects.select{ |s| s.send(chart_type) != nil }
320
-
321
- all_subject_values = filtered_subjects.collect(&method.to_sym).compact.sort
322
- domain_json = remove_unused_missing_codes_from_domain(domain_json, all_subject_values.uniq)
323
-
324
- rows = domain_json.collect do |option_hash|
325
- row_subjects = filtered_subjects.select{ |s| s.send(method) == option_hash['value'] }
326
- row_cells = chart_variable_domain.collect do |display_name, value|
327
- count = row_subjects.select{ |s| s.send(chart_type) == value }.count
328
- count > 0 ? TableFormatting::format_number(count, :count) : { text: '-', class: 'text-muted' }
329
- end
330
-
331
- total = row_subjects.count
332
-
333
- [option_hash['display_name']] + row_cells + [total == 0 ? { text: '-', class: 'text-muted' } : { text: TableFormatting::format_number(total, :count), style: 'font-weight:bold'}]
334
- end
335
-
336
- if filtered_subjects.select{|s| s.send(method) == nil }.count > 0
337
- unknown_values = chart_variable_domain.collect do |display_name, value|
338
- { text: TableFormatting::format_number(filtered_subjects.select{ |s| s.send(chart_type) == value and s.send(method) == nil }.count, :count), class: 'text-muted' }
339
- end
340
- rows << [{ text: 'Unknown', class: 'text-muted'}] + unknown_values + [ { text: TableFormatting::format_number(filtered_subjects.select{|s| s.send(method) == nil}.count, :count), style: 'font-weight:bold', class: 'text-muted' } ]
341
- end
342
-
343
-
344
-
345
- total_values = chart_variable_domain.collect do |display_name, value|
346
- total_count = filtered_subjects.select{|s| s.send(chart_type) == value }.count
347
- { text: (total_count == 0 ? "-" : TableFormatting::format_number(total_count, :count)), style: "font-weight:bold" }
348
- end
349
-
350
- footers = [
351
- [{ text: "Total", style: "font-weight:bold" }] + total_values + [{ text: TableFormatting::format_number(filtered_subjects.count, :count), style: 'font-weight:bold'}]
352
- ]
353
-
354
- { title: "#{json['display_name']} vs #{chart_variable_json['display_name']}", subtitle: subtitle, headers: headers, footers: footers, rows: rows }
355
- end
356
-
357
- def self.chart_histogram(chart_type, subjects, json, method)
358
- domain_json = get_domain(json)
359
- return if json['type'] == 'choices' and not domain_json
360
- return unless chart_variable_json = get_variable(chart_type)
361
- return unless chart_variable_domain = domain_array(chart_type)
362
-
363
- title = "#{json['display_name']}"
364
- subtitle = "By Visit"
365
- units = "Subjects"
366
- x_axis_units = json["units"]
367
- series = []
368
-
369
- all_subject_values = subjects.collect(&method.to_sym).compact.sort
370
- return nil if all_subject_values.count == 0
371
-
372
- domain_json = remove_unused_missing_codes_from_domain(domain_json, all_subject_values.uniq) if domain_json
373
-
374
- categories = pull_categories(json, method, all_subject_values, domain_json)
375
-
376
- buckets = continuous_buckets(all_subject_values)
377
-
378
- chart_variable_domain.each do |display_name, value|
379
- visit_subjects = subjects.select{ |s| s.send(chart_type) == value and s.send(method) != nil }.collect(&method.to_sym).sort
380
- next unless visit_subjects.size > 0
381
-
382
- data = pull_data(json, visit_subjects, buckets, categories, domain_json)
383
-
384
- series << { name: display_name, data: data }
385
- end
386
-
387
- { title: title, subtitle: subtitle, categories: categories, units: units, series: series, x_axis_title: x_axis_units }
388
- end
389
-
390
- def self.pull_categories(json, method, all_subject_values, domain_json)
391
- categories = []
392
- if json['type'] == 'choices'
393
- categories = domain_json.collect{|option_hash| option_hash['display_name']}
394
- else
395
- buckets = continuous_buckets(all_subject_values)
396
- categories = buckets.collect{|b| "#{b[0]} to #{b[1]}"}
397
- end
398
- categories
399
- end
400
-
401
- def self.pull_data(json, visit_subjects, buckets, categories, domain_json)
402
- data = []
403
- if json['type'] == 'choices'
404
- domain_json.each do |option_hash|
405
- data << visit_subjects.select{ |v| v == option_hash['value'] }.count
406
- end
407
- else
408
- visit_subjects.group_by{|v| get_bucket(buckets, v) }.each do |key, values|
409
- data[categories.index(key)] = values.count if categories.index(key)
410
- end
411
- end
412
- data
413
- end
414
-
415
- def self.remove_unused_missing_codes_from_domain(domain_json, unique_subject_values)
416
- domain_json.select{|option_hash| option_hash['missing'] != true or (option_hash['missing'] == true and unique_subject_values.include?(option_hash['value']))}
417
- end
418
-
419
36
  end
420
37
  end
421
38
  end
@@ -0,0 +1,22 @@
1
+ module Spout
2
+ module Models
3
+ class Bucket
4
+
5
+ attr_accessor :start, :stop
6
+
7
+ def initialize(start, stop)
8
+ @start = start
9
+ @stop = stop
10
+ end
11
+
12
+ def in_bucket?(value)
13
+ value >= @start and value <= @stop
14
+ end
15
+
16
+ def display_name
17
+ "#{@start} to #{@stop}"
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -1,11 +1,13 @@
1
1
  require 'json'
2
2
 
3
+ require 'spout/models/record'
3
4
  require 'spout/models/option'
4
5
 
5
6
  module Spout
6
7
  module Models
7
8
 
8
- class Domain
9
+ class Domain < Spout::Models::Record
10
+
9
11
  attr_accessor :id, :folder, :options
10
12
  attr_reader :errors
11
13