hqmf2js 1.3.0 → 1.4.0

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.
Files changed (40) hide show
  1. checksums.yaml +5 -13
  2. data/.travis.yml +1 -1
  3. data/Gemfile +1 -25
  4. data/Gemfile.lock +170 -146
  5. data/app/assets/javascripts/crosswalk.js.coffee +17 -19
  6. data/app/assets/javascripts/custom_calculations.js.coffee +44 -17
  7. data/app/assets/javascripts/hqmf_util.js.coffee +559 -161
  8. data/app/assets/javascripts/logging_utils.js.coffee +6 -4
  9. data/app/assets/javascripts/patient_api_extension.js.coffee +41 -9
  10. data/app/assets/javascripts/specifics.js.coffee +163 -69
  11. data/hqmf2js.gemspec +7 -12
  12. data/lib/assets/javascripts/libraries/map_reduce_utils.js +151 -64
  13. data/lib/generator/characteristic.js.erb +23 -12
  14. data/lib/generator/codes_to_json.rb +1 -1
  15. data/lib/generator/data_criteria.js.erb +15 -3
  16. data/lib/generator/derived_data.js.erb +5 -0
  17. data/lib/generator/execution.rb +41 -11
  18. data/lib/generator/js.rb +74 -41
  19. data/lib/generator/patient_data.js.erb +1 -1
  20. data/lib/hqmf2js.rb +0 -1
  21. data/lib/hquery/engine.rb +3 -1
  22. data/lib/tasks/convert.rake +20 -12
  23. data/test/fixtures/NQF59New.json +1423 -0
  24. data/test/fixtures/fulfills.xml +917 -0
  25. data/test/fixtures/patients/larry_vanderman.json +573 -654
  26. data/test/{simplecov.rb → simplecov_init.rb} +0 -0
  27. data/test/test_helper.rb +2 -3
  28. data/test/unit/cmd_test.rb +145 -19
  29. data/test/unit/codes_to_json_test.rb +12 -12
  30. data/test/unit/custom_calculations_test.rb +2 -6
  31. data/test/unit/effective_date_test.rb +3 -4
  32. data/test/unit/erb_context_test.rb +12 -12
  33. data/test/unit/filter_by_reference_test.rb +39 -0
  34. data/test/unit/hqmf_from_json_javascript_test.rb +2 -1
  35. data/test/unit/hqmf_javascript_test.rb +12 -13
  36. data/test/unit/js_object_test.rb +2 -2
  37. data/test/unit/library_function_test.rb +210 -42
  38. data/test/unit/specifics_test.rb +402 -321
  39. metadata +57 -15
  40. data/config/warble.rb +0 -144
@@ -6,17 +6,12 @@ Gem::Specification.new do |s|
6
6
  s.description = "A library for converting HQMF files to executable JavaScript suitable for use with the hQuery Gateway"
7
7
  s.email = "hquery-talk@googlegroups.com"
8
8
  s.homepage = "http://github.com/hquery/hqmf2js"
9
- s.authors = ["Marc Hadley", "Andre Quina", "Andy Gregorowicz"]
10
- s.version = '1.3.0'
11
-
12
- # s.add_dependency 'nokogiri', '~> 1.5.5'
13
- # s.add_dependency 'tilt', '~> 1.3.3'
14
- # s.add_dependency 'coffee-script', '~> 2.2.0'
15
- # s.add_dependency 'sprockets', '~> 2.2.2'
16
- # s.add_development_dependency "awesome_print", "~> 1.1.0"
17
- # s.add_dependency 'health-data-standards', '~> 3.1.1'
18
- # s.add_dependency 'hquery-patient-api', '~> 1.0.2'
19
-
9
+ s.authors = ["The MITRE Corporation"]
10
+ s.version = '1.4.0'
11
+ s.license = 'Apache-2.0'
20
12
  s.files = s.files = `git ls-files`.split("\n")
21
- end
22
13
 
14
+ s.add_dependency "rails", "~> 4.2"
15
+ s.add_dependency 'health-data-standards', '~> 4.0'
16
+ s.add_dependency 'hquery-patient-api', '~> 1.1'
17
+ end
@@ -5,7 +5,12 @@
5
5
 
6
6
  var root = this;
7
7
 
8
- root.map = function(record, population, denominator, numerator, exclusion, denexcep, msrpopl, observ, occurrenceId, isContinuousVariable) {
8
+
9
+ root.emitResult = root.emitResult || function(value) {
10
+ if (value['IPP'] > 0) emit(ObjectId(), value);
11
+ }
12
+
13
+ root.map = function(record, population, denominator, numerator, exclusion, denexcep, msrpopl, msrpoplex, observ, occurrenceId, isContinuousVariable, stratification, variables, numex) {
9
14
  var value = {IPP: 0, patient_id: record._id,
10
15
  medical_record_id: record.medical_record_number,
11
16
  first: record.first, last: record.last, gender: record.gender,
@@ -14,22 +19,22 @@ root.map = function(record, population, denominator, numerator, exclusion, denex
14
19
  race: record.race, ethnicity: record.ethnicity, languages: record.languages,
15
20
  payer: extract_payer(record)};
16
21
 
22
+ value.measure_id = hqmfjs.hqmf_id
23
+ value.sub_id = hqmfjs.sub_id
24
+ value.nqf_id = hqmfjs.nqf_id
25
+ value.effective_date = hqmfjs.effective_date;
26
+ value.test_id = hqmfjs.test_id;
27
+
17
28
  if (isContinuousVariable) {
18
- value = calculateCV(record, population, msrpopl, observ, occurrenceId, value)
29
+ value = calculateCV(record, population, msrpopl, msrpoplex, observ, occurrenceId, value, stratification, variables)
19
30
  } else {
20
- value = calculate(record, population, denominator, numerator, exclusion, denexcep, occurrenceId, value)
31
+ value = calculate(record, population, denominator, numerator, exclusion, denexcep, occurrenceId, value, stratification, variables, numex)
21
32
  }
22
33
 
23
34
  if (typeof Logger != 'undefined') {
24
35
  value['logger'] = Logger.logger
25
36
  value['rationale'] = Logger.rationale
26
37
  }
27
-
28
- value.measure_id = hqmfjs.hqmf_id
29
- value.sub_id = hqmfjs.sub_id
30
- value.nqf_id = hqmfjs.nqf_id
31
- value.effective_date = hqmfjs.effective_date;
32
- value.test_id = hqmfjs.test_id;
33
38
 
34
39
  if (value.provider_performances) {
35
40
  var tmp = [];
@@ -44,8 +49,8 @@ root.map = function(record, population, denominator, numerator, exclusion, denex
44
49
  value.provider_performances = null;
45
50
  }
46
51
 
47
- //if (value['IPP'] > 0) emit(ObjectId(), value);
48
- emit(ObjectId(), value);
52
+ emitResult(value);
53
+ //emit(ObjectId(), value);
49
54
  };
50
55
 
51
56
  root.extract_payer = function(record) {
@@ -60,71 +65,153 @@ root.extract_payer = function(record) {
60
65
  return payer;
61
66
  };
62
67
 
63
- root.calculate = function(record, population, denominator, numerator, exclusion, denexcep, occurrenceId, value) {
64
-
68
+ root.calculate = function(record, population, denominator, numerator, exclusion, denexcep, occurrenceId, value, stratification, variables, numex) {
69
+
65
70
  finalSpecifics = {};
66
- value = _.extend(value, {DENOM: 0, NUMER: 0, DENEXCEP: 0, DENEX: 0, antinumerator: 0, finalSpecifics: finalSpecifics});
67
-
68
- var ipp = population();
69
- hqmf.SpecificsManager.storeFinal('IPP', ipp, finalSpecifics);
70
- if (hqmf.SpecificsManager.validate(ipp)) {
71
- value.IPP = hqmf.SpecificsManager.countUnique(occurrenceId, ipp);
72
- var denom = hqmf.SpecificsManager.intersectSpecifics(denominator(), ipp, occurrenceId);
73
- hqmf.SpecificsManager.storeFinal('DENOM', denom, finalSpecifics);
74
- if (hqmf.SpecificsManager.validate(denom)) {
75
-
76
- value.DENOM = hqmf.SpecificsManager.countUnique(occurrenceId, denom);
77
- var exclusions = hqmf.SpecificsManager.intersectSpecifics(exclusion(), denom, occurrenceId);
78
- hqmf.SpecificsManager.storeFinal('DENEX', exclusions, finalSpecifics);
79
- if (hqmf.SpecificsManager.validate(exclusions)) {
80
- value.DENEX = hqmf.SpecificsManager.countUnique(occurrenceId, exclusions);
81
- denom = hqmf.SpecificsManager.exclude(occurrenceId, denom, exclusions);
82
- }
71
+ value = _.extend(value, {IPP: 0, DENOM: 0, NUMER: 0, DENEXCEP: 0, DENEX: 0, NUMEX: 0, antinumerator: 0});
83
72
 
73
+ // For the moment, we are doing this to allow for the disabling of storage of finalSpecifics in Mongo.
74
+ // At some point, it may make more sense to move this to its own variable, rather than using enable_rationale
75
+ // For example, if Cypress starts running into finalSpecifics size issues.
76
+ if (Logger.enable_rationale) {
77
+ value = _.extend(value, {finalSpecifics: finalSpecifics});
78
+ }
79
+
80
+ var strat;
81
+ var ipp;
82
+ var validStrat = false;
83
+ if(stratification) {
84
+ strat = stratification()
85
+ hqmf.SpecificsManager.storeFinal('STRAT', strat, finalSpecifics);
86
+ value.STRAT = hqmf.SpecificsManager.countUnique(occurrenceId, strat);
87
+ if (hqmf.SpecificsManager.validate(strat)) {
88
+ ipp = hqmf.SpecificsManager.intersectSpecifics(population(), strat, occurrenceId);
89
+ validStrat = true;
84
90
  }
85
- // DENEX is a subset of the denominator, so we should set the specifics before the exclusion
86
- // hqmf.SpecificsManager.storeFinal('DENOM', denom, finalSpecifics);
87
-
88
- // we need to check the denominator again to make sure we still have viable candidates after
89
- // exclusions have been removed
90
- if (hqmf.SpecificsManager.validate(denom)) {
91
- var numer = hqmf.SpecificsManager.intersectSpecifics(numerator(), denom, occurrenceId);
92
- hqmf.SpecificsManager.storeFinal('NUMER', numer, finalSpecifics);
93
- if (hqmf.SpecificsManager.validate(numer)) {
94
- value.NUMER = hqmf.SpecificsManager.countUnique(occurrenceId, numer);
91
+ } else {
92
+ ipp = population();
93
+ }
94
+
95
+ if (ipp) {
96
+ hqmf.SpecificsManager.storeFinal('IPP', ipp, finalSpecifics);
97
+ if (hqmf.SpecificsManager.validate(ipp)) {
98
+ value.IPP = hqmf.SpecificsManager.countUnique(occurrenceId, ipp);
99
+ // if we have a stratification that does not reference an episode of care then we need to take the IPP count
100
+ if (validStrat && value.STRAT < value.IPP) value.STRAT = value.IPP;
101
+ var denom = hqmf.SpecificsManager.intersectSpecifics(denominator(), ipp, occurrenceId);
102
+ hqmf.SpecificsManager.storeFinal('DENOM', denom, finalSpecifics);
103
+ if (hqmf.SpecificsManager.validate(denom)) {
104
+
105
+ value.DENOM = hqmf.SpecificsManager.countUnique(occurrenceId, denom);
106
+ var exclusions = hqmf.SpecificsManager.intersectSpecifics(exclusion(), denom, occurrenceId);
107
+ hqmf.SpecificsManager.storeFinal('DENEX', exclusions, finalSpecifics);
108
+ if (hqmf.SpecificsManager.validate(exclusions)) {
109
+ value.DENEX = hqmf.SpecificsManager.countUnique(occurrenceId, exclusions);
110
+ denom = hqmf.SpecificsManager.exclude(occurrenceId, denom, exclusions);
111
+ }
112
+
95
113
  }
96
-
97
- var excep = hqmf.SpecificsManager.intersectSpecifics(denexcep(), denom, occurrenceId);
98
- hqmf.SpecificsManager.storeFinal('DENEXCEP', excep, finalSpecifics);
99
- if (hqmf.SpecificsManager.validate(excep)) {
100
- excep = hqmf.SpecificsManager.exclude(occurrenceId, excep, numer);
101
- value.DENEXCEP = hqmf.SpecificsManager.countUnique(occurrenceId, excep);
102
- denom = hqmf.SpecificsManager.exclude(occurrenceId, denom, excep);
114
+ // DENEX is a subset of the denominator, so we should set the specifics before the exclusion
115
+ // hqmf.SpecificsManager.storeFinal('DENOM', denom, finalSpecifics);
116
+
117
+ // we need to check the denominator again to make sure we still have viable candidates after
118
+ // exclusions have been removed
119
+ if (hqmf.SpecificsManager.validate(denom)) {
120
+ var numer = hqmf.SpecificsManager.intersectSpecifics(numerator(), denom, occurrenceId);
121
+ hqmf.SpecificsManager.storeFinal('NUMER', numer, finalSpecifics);
122
+ if (hqmf.SpecificsManager.validate(numer)) {
123
+ value.NUMER = hqmf.SpecificsManager.countUnique(occurrenceId, numer);
124
+ var numeratorExclusions = hqmf.SpecificsManager.intersectSpecifics(numex(), numer, occurrenceId);
125
+ hqmf.SpecificsManager.storeFinal('NUMEX', numeratorExclusions, finalSpecifics);
126
+ if (hqmf.SpecificsManager.validate(numeratorExclusions)) {
127
+ value.NUMEX = hqmf.SpecificsManager.countUnique(occurrenceId, numeratorExclusions);
128
+ numer = hqmf.SpecificsManager.exclude(occurrenceId, numer, numeratorExclusions);
129
+ }
130
+ }
131
+
132
+ var excep = hqmf.SpecificsManager.intersectSpecifics(denexcep(), denom, occurrenceId);
133
+ hqmf.SpecificsManager.storeFinal('DENEXCEP', excep, finalSpecifics);
134
+ if (hqmf.SpecificsManager.validate(excep)) {
135
+ excep = hqmf.SpecificsManager.exclude(occurrenceId, excep, numer);
136
+ value.DENEXCEP = hqmf.SpecificsManager.countUnique(occurrenceId, excep);
137
+ denom = hqmf.SpecificsManager.exclude(occurrenceId, denom, excep);
138
+ }
139
+ value.antinumerator = value.DENOM-value.NUMER;
103
140
  }
104
- value.antinumerator = value.DENOM-value.NUMER;
141
+
105
142
  }
106
-
143
+ }
144
+
145
+ // Adding a separate set of computations for variables. This is because the
146
+ // logic view displays non-occurrenced variables. However, these are not always
147
+ // a part of the actual logic computation. This means that they don't get
148
+ // colored. By adding in a separate set of computations for variables, we
149
+ // guarantee that they'll be colored.
150
+ if(variables){
151
+ variables();
107
152
  }
108
153
  return value;
109
154
  };
110
155
 
111
- root.calculateCV = function(record, population, msrpopl, observ, occurrenceId, value) {
156
+ root.calculateCV = function(record, population, msrpopl, msrpoplex, observ, occurrenceId, value, stratification, variables) {
112
157
  finalSpecifics = {};
113
- value = _.extend(value, {MSRPOPL: 0, values: [], finalSpecifics: finalSpecifics});
114
-
115
- var ipp = population()
116
- hqmf.SpecificsManager.storeFinal('IPP', ipp, finalSpecifics);
117
- if (hqmf.SpecificsManager.validate(ipp)) {
118
- value.IPP = hqmf.SpecificsManager.countUnique(occurrenceId, ipp);
119
- var measurePopulation = hqmf.SpecificsManager.intersectSpecifics(msrpopl(), ipp, occurrenceId);
120
- hqmf.SpecificsManager.storeFinal('MSRPOPL', measurePopulation, finalSpecifics);
121
- if (hqmf.SpecificsManager.validate(measurePopulation)) {
122
- var observations = observ(measurePopulation.specificContext);
123
- value.MSRPOPL = hqmf.SpecificsManager.countUnique(occurrenceId, measurePopulation);
124
- value.values = observations;
158
+ value = _.extend(value, {MSRPOPL: 0, MSRPOPLEX: 0, values: []});
159
+
160
+ // For the moment, we are doing this to allow for the disabling of storage of finalSpecifics in Mongo.
161
+ // At some point, it may make more sense to move this to its own variable, rather than using enable_rationale
162
+ // For example, if Cypress starts running into finalSpecifics size issues.
163
+ if (Logger.enable_rationale) {
164
+ value = _.extend(value, {finalSpecifics: finalSpecifics});
165
+ }
166
+
167
+ var strat;
168
+ var ipp;
169
+ var validStrat = false;
170
+ if(stratification) {
171
+ strat = stratification()
172
+ hqmf.SpecificsManager.storeFinal('STRAT', strat, finalSpecifics);
173
+ value.STRAT = hqmf.SpecificsManager.countUnique(occurrenceId, strat);
174
+ if (hqmf.SpecificsManager.validate(strat)) {
175
+ ipp = hqmf.SpecificsManager.intersectSpecifics(population(), strat, occurrenceId);
176
+ validStrat = true;
177
+ }
178
+ } else {
179
+ ipp = population();
180
+ }
181
+
182
+
183
+ if (ipp) {
184
+ hqmf.SpecificsManager.storeFinal('IPP', ipp, finalSpecifics);
185
+ if (hqmf.SpecificsManager.validate(ipp)) {
186
+ value.IPP = hqmf.SpecificsManager.countUnique(occurrenceId, ipp);
187
+ // if we have a stratification that does not reference an episode of care then we need to take the IPP count
188
+ if (validStrat && value.STRAT < value.IPP) value.STRAT = value.IPP;
189
+ var measurePopulation = hqmf.SpecificsManager.intersectSpecifics(msrpopl(), ipp, occurrenceId);
190
+ hqmf.SpecificsManager.storeFinal('MSRPOPL', measurePopulation, finalSpecifics);
191
+ if (hqmf.SpecificsManager.validate(measurePopulation)) {
192
+ value.MSRPOPL = hqmf.SpecificsManager.countUnique(occurrenceId, measurePopulation);
193
+
194
+ var measurePopulationExclusions = hqmf.SpecificsManager.intersectSpecifics(msrpoplex(), measurePopulation, occurrenceId);
195
+ hqmf.SpecificsManager.storeFinal('MSRPOPLEX', measurePopulationExclusions, finalSpecifics);
196
+ if (hqmf.SpecificsManager.validate(measurePopulationExclusions)) {
197
+ value.MSRPOPLEX = hqmf.SpecificsManager.countUnique(occurrenceId, measurePopulationExclusions);
198
+ // Remove from measure population if in measure population exclusion.
199
+ measurePopulation = hqmf.SpecificsManager.exclude(occurrenceId, measurePopulation, measurePopulationExclusions);
200
+ }
201
+
202
+ var observations = observ(measurePopulation.specificContext);
203
+ value.values = observations;
204
+ }
125
205
  }
126
206
  }
207
+
208
+ // Adding a separate set of computations for variables. This is because the
209
+ // logic view displays non-occurrenced variables. However, these are not always
210
+ // a part of the actual logic computation. This means that they don't get
211
+ // colored. By adding in a separate set of computations for variables, we
212
+ // guarantee that they'll be colored.
213
+ if(variables){
214
+ variables();
215
+ }
127
216
  return value;
128
217
  };
129
-
130
-
@@ -1,30 +1,41 @@
1
1
  var value = patient.<%= criteria.property %>(<%= js_for_date_bound(criteria) if criteria.property == :age %>) || null;
2
2
  <%- if criteria.property == :birthtime -%>
3
- var events = [value];
3
+ var events = value ? [value] : [];
4
4
  <%- if criteria.temporal_references -%>
5
5
  <%- criteria.temporal_references.each do |temporal_reference| -%>
6
6
  events = <%= temporal_reference.type %>(events, hqmfjs.<%= temporal_reference.reference.id %>(patient)<%= ", #{js_for_bounds(temporal_reference.range)}" if temporal_reference.range %>);
7
7
  <%- end -%>
8
8
  <%- end -%>
9
- events.specificContext=hqmf.SpecificsManager.identity();
9
+ <%- if criteria.field_values.present? -%>
10
+ <%- criteria.field_values.keys.each do |field| -%>
11
+ events = matchingValue(value, <%= js_for_bounds(criteria.field_values[field]) %>).isTrue() ? events : [];
12
+ <%- end -%>
13
+ <%- end -%>
14
+ events.specificContext=events.specificContext||hqmf.SpecificsManager.identity();
10
15
  return events;
11
- <%- elsif criteria.property == :expired %>
12
- var return_value = matchingValue(value, 'true');
13
-
16
+ <%- elsif criteria.property == :expired -%>
17
+ var events = value ? [patient.deathdate()] : [];
14
18
  <%- if criteria.temporal_references -%>
15
- var events = [patient.deathdate()];
16
- <%- criteria.temporal_references.each do |temporal_reference| -%>
17
- return_value = <%= temporal_reference.type %>(events, hqmfjs.<%= temporal_reference.reference.id %>(patient)<%= ", #{js_for_bounds(temporal_reference.range)}" if temporal_reference.range %>);
18
- <%- end -%>
19
+ <%- criteria.temporal_references.each do |temporal_reference| -%>
20
+ events = <%= temporal_reference.type %>(events, hqmfjs.<%= temporal_reference.reference.id %>(patient)<%= ", #{js_for_bounds(temporal_reference.range)}" if temporal_reference.range %>);
19
21
  <%- end -%>
20
- return_value.specificContext=hqmf.SpecificsManager.identity();
21
- return return_value
22
+ <%- end -%>
23
+ events.specificContext=events.specificContext||hqmf.SpecificsManager.identity();
24
+ return events;
22
25
  <%- elsif criteria.property == :clinicalTrialParticipant -%>
23
26
  matching = matchingValue(value, 'true');
24
27
  matching.specificContext=hqmf.SpecificsManager.identity();
25
28
  return matching;
29
+ <%- elsif criteria.property == :race || criteria.property == :payer -%>
30
+ if (value === null) {
31
+ matching = new Boolean(false);
32
+ } else {
33
+ matching = new Boolean(value.includedIn(<%=criteria.inline_code_list.to_json%>));
34
+ }
35
+ matching.specificContext=hqmf.SpecificsManager.identity();
36
+ return matching;
26
37
  <%- else -%>
27
38
  matching = matchingValue(value, <%= js_for_bounds(criteria.value) %>);
28
39
  matching.specificContext=hqmf.SpecificsManager.identity();
29
40
  return matching;
30
- <%- end -%>
41
+ <%- end -%>
@@ -14,7 +14,7 @@ module HQMF2JS
14
14
  code_sets = {}
15
15
  value_set.concepts.each do |code_set|
16
16
  code_sets[code_set.code_system_name] ||= []
17
- code_sets[code_set.code_system_name].concat(code_set.code.to_a)
17
+ code_sets[code_set.code_system_name] << code_set.code
18
18
  end
19
19
 
20
20
  translation[value_set.oid] = code_sets
@@ -17,15 +17,27 @@ hqmfjs.<%= js_name(criteria) %> = function(patient, initialSpecificContext) {
17
17
  <%- end -%>
18
18
  <%- if criteria.value -%>
19
19
  events = filterEventsByValue(events, <%= js_for_bounds(criteria.value) %>);
20
+ <%- elsif is_result_criteria(criteria) && !criteria.negation -%>
21
+ // force a check of results for non-negated result types so that performed does not bleed into results
22
+ events = filterEventsByValue(events, new ANYNonNull());
20
23
  <%- end -%>
21
24
  <%- if criteria.field_values.present?
22
25
  criteria.field_values.keys.each do |field| -%>
23
- events = <%= field_library_method(field) %>(events, "<%= field_method(field) %>", <%= js_for_bounds(criteria.field_values[field]) %>);
26
+ events = <%= field_library_method(field, criteria.field_values[field]) %>(events, "<%= field_method(field) %>", <%= js_for_bounds(criteria.field_values[field]) %>);
24
27
  <%- end -%>
28
+ <%- if criteria.subset_operators -%>
29
+ fields = <%= criteria.field_values.keys.map {|field| field_method(field)} %>;
30
+ <%- end -%>
31
+ <%- end -%>
32
+ <%- if ['transfer_from','transfer_to'].include?(criteria.definition) -%>
33
+ events = denormalizeEventsByTransfer(events, '<%=criteria.definition.camelize(:lower)%>');
34
+ <%- end -%>
35
+ <%- if :communications == criteria.type-%>
36
+ events = filterEventsByCommunicationDirection(events, '<%=criteria.definition%>');
25
37
  <%- end -%>
26
38
  <%- if criteria.temporal_references and criteria.temporal_references.length > 0 -%>
27
39
  <%- criteria.temporal_references.each do |temporal_reference| -%>
28
- if (events.length > 0) events = <%= temporal_reference.type %>(events, hqmfjs.<%= temporal_reference.reference.id %>(patient)<%= ", #{js_for_bounds(temporal_reference.range)}" if temporal_reference.range %>);
40
+ if (events.length > 0 || !Logger.short_circuit) events = <%= temporal_reference.type %>(events, hqmfjs.<%= temporal_reference.reference.id %>(patient)<%= ", #{js_for_bounds(temporal_reference.range)}" if temporal_reference.range %>);
29
41
  <%- end -%>
30
42
  if (events.length == 0) events.specificContext=hqmf.SpecificsManager.empty();
31
43
  <%- else # no temporal refs -%>
@@ -37,7 +49,7 @@ hqmfjs.<%= js_name(criteria) %> = function(patient, initialSpecificContext) {
37
49
  <%- end -%>
38
50
  <%- if criteria.subset_operators -%>
39
51
  <%- criteria.subset_operators.select {|subset_operator| subset_operator.type}.each do |subset_operator| -%>
40
- events = <%= subset_operator.type %>(events<%= ", #{js_for_bounds(subset_operator.value)}" if subset_operator.value %>, initialSpecificContext);
52
+ events = <%= subset_operator.type %>(events<%= if subset_operator.value then ", #{js_for_bounds(subset_operator.value)}"; elsif subset_operator.type == 'DATETIMEDIFF' then ", null"; end %>, initialSpecificContext<%- if ['SUM','MEDIAN'].include?(subset_operator.type) -%>, fields<%- end -%>);
41
53
  <%- end # each operator -%>
42
54
  <%- end # subset operators -%>
43
55
  return events;
@@ -3,3 +3,8 @@
3
3
  <%= "hqmfjs.#{child}(patient, initialSpecificContext)" %><%= "," if index<criteria.children_criteria.length-1 %>
4
4
  <%- end -%>
5
5
  );
6
+ <%- if criteria.variable -%>
7
+ <%- if criteria.specific_occurrence -%>
8
+ events.specific_occurrence = '<%= criteria.source_data_criteria%>';
9
+ <%- end -%>
10
+ <%- end -%>
@@ -2,7 +2,7 @@ module HQMF2JS
2
2
  module Generator
3
3
  class Execution
4
4
 
5
-
5
+
6
6
  def self.quoted_string_array_or_null(arr)
7
7
  if arr
8
8
  quoted = arr.map {|e| "\"#{e}\""}
@@ -20,6 +20,7 @@ module HQMF2JS
20
20
  var effective_date = <%= effective_date %>;
21
21
  var enable_logging = <%= enable_logging %>;
22
22
  var enable_rationale = <%= enable_rationale %>;
23
+ var short_circuit = <%= short_circuit %>;
23
24
 
24
25
  <% if (!test_id.nil? && test_id.class==Moped::BSON::ObjectId) %>
25
26
  var test_id = new ObjectId(\"<%= test_id %>\");
@@ -32,7 +33,7 @@ module HQMF2JS
32
33
 
33
34
  hqmfjs.effective_date = effective_date;
34
35
  hqmfjs.test_id = test_id;
35
-
36
+
36
37
  #{logic(hqmf_document, population_index, options)}
37
38
  };
38
39
  "
@@ -57,19 +58,25 @@ module HQMF2JS
57
58
  crosswalk_instrument = "instrumentTrueCrosswalk(hqmfjs);"
58
59
  end
59
60
 
60
-
61
+
61
62
  "
62
63
  var patient_api = new hQuery.Patient(patient);
63
64
 
64
65
  #{gen.to_js(population_index, codes, force_sources)}
65
-
66
+
66
67
  var occurrenceId = #{quoted_string_array_or_null(episode_ids)};
67
68
 
68
69
  hqmfjs.initializeSpecifics(patient_api, hqmfjs)
69
-
70
+
70
71
  var population = function() {
71
72
  return executeIfAvailable(hqmfjs.#{HQMF::PopulationCriteria::IPP}, patient_api);
72
73
  }
74
+ var stratification = null;
75
+ if (hqmfjs.#{HQMF::PopulationCriteria::STRAT}) {
76
+ stratification = function() {
77
+ return hqmf.SpecificsManager.setIfNull(executeIfAvailable(hqmfjs.#{HQMF::PopulationCriteria::STRAT}, patient_api));
78
+ }
79
+ }
73
80
  var denominator = function() {
74
81
  return executeIfAvailable(hqmfjs.#{HQMF::PopulationCriteria::DENOM}, patient_api);
75
82
  }
@@ -82,13 +89,25 @@ module HQMF2JS
82
89
  var denexcep = function() {
83
90
  return executeIfAvailable(hqmfjs.#{HQMF::PopulationCriteria::DENEXCEP}, patient_api);
84
91
  }
92
+ var numex = function() {
93
+ return executeIfAvailable(hqmfjs.#{HQMF::PopulationCriteria::NUMEX}, patient_api);
94
+ }
85
95
  var msrpopl = function() {
86
- return executeIfAvailable(hqmfjs.#{HQMF::PopulationCriteria::MSRPOPL}, patient_api);
96
+ #{msrpopl_function(custom_functions, population_index)}
97
+ }
98
+ var msrpoplex = function() {
99
+ return executeIfAvailable(hqmfjs.#{HQMF::PopulationCriteria::MSRPOPLEX}, patient_api);
87
100
  }
88
101
  var observ = function(specific_context) {
89
102
  #{observation_function(custom_functions, population_index)}
90
103
  }
91
-
104
+
105
+ var variables = function() {
106
+ if (Logger.enable_rationale) {
107
+ return executeIfAvailable(hqmfjs.VARIABLES, patient_api);
108
+ }
109
+ }
110
+
92
111
  var executeIfAvailable = function(optionalFunction, patient_api) {
93
112
  if (typeof(optionalFunction)==='function') {
94
113
  result = optionalFunction(patient_api);
@@ -105,15 +124,18 @@ module HQMF2JS
105
124
  Logger.logger = [];
106
125
  Logger.rationale={};
107
126
  if (typeof short_circuit == 'undefined') short_circuit = true;
108
-
127
+
109
128
  // turn on logging if it is enabled
110
129
  if (enable_logging || enable_rationale) {
111
130
  injectLogger(hqmfjs, enable_logging, enable_rationale, short_circuit);
112
- }
131
+ } else {
132
+ Logger.enable_rationale = false;
133
+ Logger.short_circuit = short_circuit;
134
+ }
113
135
  }
114
136
 
115
137
  try {
116
- map(patient, population, denominator, numerator, exclusion, denexcep, msrpopl, observ, occurrenceId,#{continuous_variable});
138
+ map(patient, population, denominator, numerator, exclusion, denexcep, msrpopl, msrpoplex, observ, occurrenceId,#{continuous_variable},stratification, variables, numex);
117
139
  } catch(err) {
118
140
  print(err.stack);
119
141
  throw err;
@@ -139,6 +161,14 @@ module HQMF2JS
139
161
 
140
162
  end
141
163
 
164
+ def self.msrpopl_function(custom_functions, population_index)
165
+ if (custom_functions && custom_functions[HQMF::PopulationCriteria::MSRPOPL])
166
+ "return #{custom_functions[HQMF::PopulationCriteria::MSRPOPL]}(patient_api, hqmfjs)"
167
+ else
168
+ "return executeIfAvailable(hqmfjs.#{HQMF::PopulationCriteria::MSRPOPL}, patient_api);"
169
+ end
170
+ end
171
+
142
172
  end
143
173
  end
144
- end
174
+ end