hqmf2js 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -8,9 +8,7 @@ group :assets do
8
8
  gem 'uglifier'
9
9
  end
10
10
 
11
- gem "hquery-patient-api", '~> 1.0.0'
12
- gem 'hqmf-parser', '~> 1.1.0'
13
- gem "health-data-standards", '~> 2.2.0'
11
+ gemspec
14
12
 
15
13
  gem 'nokogiri'
16
14
  gem 'sprockets', '~> 2.2.2'
data/Gemfile.lock CHANGED
@@ -1,3 +1,14 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ hqmf2js (1.2.0)
5
+ coffee-script (~> 2.2.0)
6
+ health-data-standards (~> 3.0.1)
7
+ hquery-patient-api (~> 1.0.1)
8
+ nokogiri (~> 1.5.5)
9
+ sprockets (~> 2.2.2)
10
+ tilt (~> 1.3.3)
11
+
1
12
  GEM
2
13
  remote: http://rubygems.org/
3
14
  specs:
@@ -49,28 +60,28 @@ GEM
49
60
  erubis (2.7.0)
50
61
  execjs (1.4.0)
51
62
  multi_json (~> 1.0)
52
- faraday (0.8.4)
63
+ faraday (0.8.6)
53
64
  multipart-post (~> 1.1)
54
65
  google-spreadsheet-ruby (0.1.8)
55
66
  nokogiri (>= 1.4.3.1)
56
67
  oauth (>= 0.3.6)
57
68
  oauth2 (>= 0.5.0)
58
69
  hashie (1.2.0)
59
- health-data-standards (2.2.0)
70
+ health-data-standards (3.0.1)
60
71
  activesupport (~> 3.2.9)
61
72
  builder (~> 3.0.0)
62
73
  erubis (~> 2.7.0)
63
- mongoid (~> 3.0.14)
74
+ google-spreadsheet-ruby (= 0.1.8)
75
+ log4r (~> 1.1.10)
76
+ mongoid (~> 3.1.0)
64
77
  nokogiri (~> 1.5.5)
65
78
  rest-client (~> 1.6.7)
66
- uuid (~> 2.3.5)
67
- hike (1.2.1)
68
- hqmf-parser (1.1.0)
69
- google-spreadsheet-ruby (= 0.1.8)
70
79
  roo (= 1.10.1)
71
80
  rubyzip
72
81
  spreadsheet (= 0.6.8)
73
- hquery-patient-api (1.0.0)
82
+ uuid (~> 2.3.5)
83
+ hike (1.2.1)
84
+ hquery-patient-api (1.0.1)
74
85
  httpauth (0.2.0)
75
86
  i18n (0.6.1)
76
87
  journey (1.0.4)
@@ -86,23 +97,25 @@ GEM
86
97
  mime-types (~> 1.16)
87
98
  treetop (~> 1.4.8)
88
99
  method_source (0.8.1)
89
- mime-types (1.19)
100
+ mime-types (1.20.1)
90
101
  minitest (4.3.2)
91
- mongoid (3.0.15)
92
- activemodel (~> 3.1)
93
- moped (~> 1.1)
102
+ mongoid (3.1.2)
103
+ activemodel (~> 3.2)
104
+ moped (~> 1.4.2)
94
105
  origin (~> 1.0)
95
106
  tzinfo (~> 0.3.22)
96
- moped (1.3.1)
97
- multi_json (1.4.0)
98
- multipart-post (1.1.5)
99
- nokogiri (1.5.5)
107
+ moped (1.4.2)
108
+ multi_json (1.5.0)
109
+ multi_xml (0.5.3)
110
+ multipart-post (1.2.0)
111
+ nokogiri (1.5.6)
100
112
  oauth (0.4.7)
101
- oauth2 (0.8.0)
113
+ oauth2 (0.9.1)
102
114
  faraday (~> 0.8)
103
115
  httpauth (~> 0.1)
104
116
  jwt (~> 0.1.4)
105
117
  multi_json (~> 1.0)
118
+ multi_xml (~> 0.5)
106
119
  rack (~> 1.2)
107
120
  origin (1.0.11)
108
121
  polyglot (0.3.3)
@@ -110,7 +123,7 @@ GEM
110
123
  coderay (~> 1.0.5)
111
124
  method_source (~> 0.8)
112
125
  slop (~> 3.3.1)
113
- rack (1.4.1)
126
+ rack (1.4.4)
114
127
  rack-cache (1.2)
115
128
  rack (>= 0.4)
116
129
  rack-ssl (1.3.2)
@@ -175,7 +188,7 @@ GEM
175
188
  uglifier (1.3.0)
176
189
  execjs (>= 0.3.0)
177
190
  multi_json (~> 1.0, >= 1.0.2)
178
- uuid (2.3.6)
191
+ uuid (2.3.7)
179
192
  macaddr (~> 1.0)
180
193
  yamler (0.1.0)
181
194
 
@@ -187,9 +200,7 @@ DEPENDENCIES
187
200
  coffee-rails
188
201
  coffee-script
189
202
  cover_me (~> 1.2.0)
190
- health-data-standards (~> 2.2.0)
191
- hqmf-parser (~> 1.1.0)
192
- hquery-patient-api (~> 1.0.0)
203
+ hqmf2js!
193
204
  minitest
194
205
  nokogiri
195
206
  pry
@@ -1,16 +1,74 @@
1
1
  @hqmf.CustomCalc = {}
2
2
 
3
+ @hqmf.CustomCalc.ADE_TTR_OBSERV = (patient, hqmfjs) ->
4
+ inrReadings = DURING(hqmfjs.LaboratoryTestResultInr(patient), hqmfjs.MeasurePeriod(patient));
5
+ inrReadings = new hqmf.CustomCalc.PercentTTREntries(inrReadings)
6
+ [inrReadings.calculatePercentTTR()]
7
+
3
8
  class @hqmf.CustomCalc.PercentTTREntries extends hQuery.CodedEntryList
4
9
 
5
10
  constructor: (events) ->
6
11
  super()
7
- events = events.sort(dateSortAscending)
8
- @push(event) for event in events
9
12
  @minInr = 2.0
10
13
  @maxInr = 3.0
11
- # sort entries ascending
12
- # filter duplicates to those closest to 2.5
13
- # remove duplicate results on entries
14
+ @minOutOfRange = 0.8
15
+ @maxOutOfRange = 10
16
+ @closestSetpoint = 2.5
17
+
18
+ clonedEvents = []
19
+ clonedEvents.push(new event.constructor(event.json)) for event in events
20
+
21
+ # remove entries < 0.8
22
+ # reset > 10 to be 10
23
+ # remove duplicate results on entries to those closest to 2.5
24
+ for entry in clonedEvents
25
+ currentClosestValue = null
26
+ for value in entry.values()
27
+ if value.scalar() > @maxOutOfRange
28
+ value.json['scalar'] = '10.0'
29
+ if value.scalar() >= @minOutOfRange && value.scalar() <= @maxOutOfRange
30
+ currentClosestValue = @closestValueToSetpoint(currentClosestValue, value)
31
+
32
+ passingValues = []
33
+ passingValues = [currentClosestValue.json] if currentClosestValue?
34
+ entry.json['values'] = passingValues
35
+
36
+ # filter duplicates to those closest to 2.5 on the same day
37
+ # remove any entries with no values (removed because the value was below 0.8, or no value on source data)
38
+ entriesByDay = {}
39
+ for entry in clonedEvents
40
+ date = entry.timeStamp()
41
+ key = "#{date.getUTCFullYear()}_#{date.getUTCMonth()}_#{date.getUTCDate()}"
42
+ entriesByDay[key] = [] unless entriesByDay[key]
43
+ entriesByDay[key].push(entry) if entry.values().length > 0
44
+
45
+ # keep the closest entry to 2.5 when there are multiple entries per day
46
+ finalEvents = []
47
+ for key in _.keys(entriesByDay)
48
+ if (entriesByDay[key].length > 1)
49
+ currentClosestValue = null
50
+ selectedEntry = null
51
+ for entry in entriesByDay[key]
52
+ currentClosestValue = @closestValueToSetpoint(currentClosestValue, entry.values()[0])
53
+ if currentClosestValue.scalar() == entry.values()[0].scalar()
54
+ selectedEntry = entry
55
+ finalEvents.push(selectedEntry)
56
+ else
57
+ finalEvents = finalEvents.concat(entriesByDay[key])
58
+
59
+ finalEvents = finalEvents.sort(dateSortAscending)
60
+
61
+ @push(event) for event in finalEvents
62
+
63
+
64
+ closestValueToSetpoint: (one, two) ->
65
+ return two if one == null
66
+ return one if two == null
67
+ if (Math.abs(one.scalar() - @closestSetpoint) > Math.abs(two.scalar() - @closestSetpoint))
68
+ return two
69
+ else
70
+ return one
71
+
14
72
 
15
73
  calculateDaysInRange: (firstInr, secondInr) ->
16
74
 
@@ -408,26 +408,26 @@ class ANYNonNull
408
408
  @ANYNonNull = ANYNonNull
409
409
 
410
410
  # Returns true if one or more of the supplied values is true
411
- atLeastOneTrue = (values...) ->
411
+ atLeastOneTrue = (precondition, values...) ->
412
412
  trueValues = (value for value in values when value && value.isTrue())
413
413
  trueValues.length>0
414
414
  hqmf.SpecificsManager.unionAll(new Boolean(trueValues.length>0), values)
415
415
  @atLeastOneTrue = atLeastOneTrue
416
416
 
417
417
  # Returns true if all of the supplied values are true
418
- allTrue = (values...) ->
418
+ allTrue = (precondition, values...) ->
419
419
  trueValues = (value for value in values when value && value.isTrue())
420
420
  hqmf.SpecificsManager.intersectAll(new Boolean(trueValues.length>0 && trueValues.length==values.length), values)
421
421
  @allTrue = allTrue
422
422
 
423
423
  # Returns true if one or more of the supplied values is false
424
- atLeastOneFalse = (values...) ->
424
+ atLeastOneFalse = (precondition, values...) ->
425
425
  falseValues = (value for value in values when value.isFalse())
426
426
  hqmf.SpecificsManager.intersectAll(new Boolean(falseValues.length>0), values, true)
427
427
  @atLeastOneFalse = atLeastOneFalse
428
428
 
429
429
  # Returns true if all of the supplied values are false
430
- allFalse = (values...) ->
430
+ allFalse = (precondition, values...) ->
431
431
  falseValues = (value for value in values when value.isFalse())
432
432
  hqmf.SpecificsManager.unionAll(new Boolean(falseValues.length>0 && falseValues.length==values.length), values, true)
433
433
  @allFalse = allFalse
@@ -514,6 +514,8 @@ class CrossProduct extends Array
514
514
  @eventLists.push eventList
515
515
  for event in eventList
516
516
  this.push(event)
517
+ listCount: -> @eventLists.length
518
+ childList: (index) -> @eventLists[index]
517
519
 
518
520
  # Create a CrossProduct of the supplied event lists.
519
521
  XPRODUCT = (eventLists...) ->
@@ -545,7 +547,7 @@ getIVL = (eventOrTimeStamp) ->
545
547
  ts.date = eventOrTimeStamp
546
548
  new IVL_TS(ts, ts)
547
549
  @getIVL = getIVL
548
-
550
+
549
551
  eventAccessor = {
550
552
  'DURING': 'low',
551
553
  'OVERLAP': 'low',
@@ -820,6 +822,37 @@ DATEDIFF = (events, range) ->
820
822
  hqmf.SpecificsManager.maintainSpecifics(new Boolean(withinRange('DATEDIFF', getIVL(events[0]), getIVL(events[1]), range)), events)
821
823
  @DATEDIFF = DATEDIFF
822
824
 
825
+ # Calculate the set of time differences in minutes between pairs of events
826
+ # events - a XPRODUCT of two event lists
827
+ # range - ignored
828
+ # initialSpecificContext - the specific context containing one row per permissible
829
+ # combination of events
830
+ TIMEDIFF = (events, range, initialSpecificContext) ->
831
+ if events.listCount() != 2
832
+ throw "TIMEDIFF can only process 2 lists of events"
833
+ eventList1 = events.childList(0)
834
+ eventList2 = events.childList(1)
835
+ eventIndex1 = hqmf.SpecificsManager.getColumnIndex(eventList1.specific_occurrence)
836
+ eventIndex2 = hqmf.SpecificsManager.getColumnIndex(eventList2.specific_occurrence)
837
+ eventMap1 = {}
838
+ eventMap2 = {}
839
+ for event in eventList1
840
+ eventMap1[event.id] = event
841
+ for event in eventList2
842
+ eventMap2[event.id] = event
843
+ results = []
844
+ for row in initialSpecificContext.rows
845
+ event1 = row.values[eventIndex1]
846
+ event2 = row.values[eventIndex2]
847
+ if event1 and event2 and event1 != hqmf.SpecificsManager.any and event2 != hqmf.SpecificsManager.any
848
+ # The maps contain the actual events we want to work with since these may contain
849
+ # time shifted clones of the events in the specificContext, e.g. via adjustBoundsForField
850
+ shiftedEvent1 = eventMap1[event1.id]
851
+ shiftedEvent2 = eventMap2[event2.id]
852
+ if shiftedEvent1 and shiftedEvent2
853
+ results.push(shiftedEvent1.asTS().difference(shiftedEvent2.asTS(), 'min'))
854
+ results
855
+ @TIMEDIFF = TIMEDIFF
823
856
 
824
857
  @OidDictionary = {};
825
858
 
@@ -1,7 +1,11 @@
1
1
  class @Logger
2
2
  @logger: []
3
+ @rationale: {}
3
4
  @info: (string) ->
4
5
  @logger.push("#{Logger.indent()}#{string}")
6
+ @record: (id, result) ->
7
+ if result? and typeof(result.isTrue) == 'function'
8
+ @rationale[id] = result.isTrue()
5
9
  @enabled: true
6
10
  @initialized: false
7
11
  @indentCount = 0
@@ -40,12 +44,17 @@ class @Logger
40
44
 
41
45
  @enableMeasureLogging = (hqmfjs) ->
42
46
  _.each(_.functions(hqmfjs), (method) ->
43
- hqmfjs[method] = _.wrap(hqmfjs[method], (func, patient) ->
47
+ hqmfjs[method] = _.wrap(hqmfjs[method], (func) ->
48
+
49
+ args = Array.prototype.slice.call(arguments,1)
50
+
44
51
  Logger.info("#{method}:")
45
52
  Logger.indentCount++
46
- result = func(patient)
53
+ result = func.apply(this, args)
54
+
47
55
  Logger.indentCount--
48
56
  Logger.info("#{method} -> #{Logger.asBoolean(result)}")
57
+ Logger.record(method,result)
49
58
  return result;
50
59
  );
51
60
  );
@@ -73,12 +82,6 @@ class @Logger
73
82
  );
74
83
 
75
84
  hQuery.CodedEntryList.prototype.match = _.wrap(hQuery.CodedEntryList.prototype.match, (func, codeSet, start, end) ->
76
-
77
- # if (codeSet)
78
- # Logger.info("matching: codeSets(#{_.keys(codeSet).join(",")}), #{start}, #{end}")
79
- # else
80
- # Logger.info("matching: WARNING: CODE SETS ARE NULL, #{start}, #{end}")
81
-
82
85
  func = _.bind(func, this, codeSet,start,end)
83
86
  result = func(codeSet,start,end)
84
87
  Logger.info("matched -> #{Logger.stringify(result)}")
@@ -98,6 +101,7 @@ class @Logger
98
101
  result = func.apply(this, args)
99
102
  Logger.indentCount--
100
103
  Logger.info("atLeastOneTrue -> #{result}")
104
+ Logger.record("precondition_#{args[0]}",result)
101
105
  result
102
106
  )
103
107
 
@@ -108,6 +112,7 @@ class @Logger
108
112
  result = func.apply(this, args)
109
113
  Logger.indentCount--
110
114
  Logger.info("allTrue -> #{result}")
115
+ Logger.record("precondition_#{args[0]}",result)
111
116
  result
112
117
  )
113
118
 
@@ -118,6 +123,7 @@ class @Logger
118
123
  result = func.apply(this, args)
119
124
  Logger.indentCount--
120
125
  Logger.info("allFalse -> #{result}")
126
+ Logger.record("precondition_#{args[0]}",result)
121
127
  result
122
128
  )
123
129
 
@@ -128,6 +134,7 @@ class @Logger
128
134
  result = func.apply(this, args)
129
135
  Logger.indentCount--
130
136
  Logger.info("atLeastOneFalse -> #{result}")
137
+ Logger.record("precondition_#{args[0]}",result)
131
138
  result
132
139
  )
133
140
 
@@ -13,6 +13,10 @@ hQuery.CodedEntry::asIVL_TS = ->
13
13
  tsHigh = new TS()
14
14
  tsHigh.date = this.endDate() || this.date() || null
15
15
  new IVL_TS(tsLow, tsHigh)
16
+ hQuery.CodedEntry::asTS = ->
17
+ ts = new TS()
18
+ ts.date = this.timeStamp()
19
+ ts
16
20
 
17
21
  hQuery.CodedEntry::respondTo = (functionName) ->
18
22
  typeof(@[functionName]) == "function"
data/hqmf2js.gemspec CHANGED
@@ -7,13 +7,15 @@ Gem::Specification.new do |s|
7
7
  s.email = "hquery-talk@googlegroups.com"
8
8
  s.homepage = "http://github.com/hquery/hqmf2js"
9
9
  s.authors = ["Marc Hadley", "Andre Quina", "Andy Gregorowicz"]
10
- s.version = '1.1.0'
10
+ s.version = '1.2.0'
11
11
 
12
12
  s.add_dependency 'nokogiri', '~> 1.5.5'
13
13
  s.add_dependency 'tilt', '~> 1.3.3'
14
14
  s.add_dependency 'coffee-script', '~> 2.2.0'
15
15
  s.add_dependency 'sprockets', '~> 2.2.2'
16
16
  s.add_development_dependency "awesome_print", "~> 1.1.0"
17
+ s.add_dependency 'health-data-standards', '~> 3.0.1'
18
+ s.add_dependency 'hquery-patient-api', '~> 1.0.1'
17
19
 
18
20
  s.files = s.files = `git ls-files`.split("\n")
19
21
  end
@@ -16,9 +16,9 @@ module HQMF2JS
16
16
  translation = {}
17
17
  value_sets.each do |value_set|
18
18
  code_sets = {}
19
- value_set["code_sets"].each do |code_set|
20
- code_sets[code_set["code_set"]] ||= []
21
- code_sets[code_set["code_set"]].concat(code_set["codes"])
19
+ value_set["concepts"].each do |code_set|
20
+ code_sets[code_set["code_system_name"]] ||= []
21
+ code_sets[code_set["code_system_name"]].concat(code_set["code"].to_a)
22
22
  end
23
23
 
24
24
  translation[value_set["oid"]] = code_sets
@@ -35,6 +35,7 @@ module HQMF2JS
35
35
  # <ValueSet id="2.16.840.1.113883.3.464.1.14" displayName="birth date">
36
36
  # <ConceptList xml:lang="en-US">
37
37
  # <Concept code="00110" codeSystemName="HL7" displayName="Date/Time of birth (TS)"
38
+
38
39
  # codeSystemVersion="3"/>
39
40
  # </ConceptList>
40
41
  # </ValueSet>
@@ -78,4 +79,4 @@ module HQMF2JS
78
79
 
79
80
  end
80
81
  end
81
- end
82
+ end
@@ -6,7 +6,7 @@ var <%= js_name(criteria) %> = <%= js_for_value(criteria.value) %>;
6
6
 
7
7
  // Data critera
8
8
  <%- all_criteria.select {|c| c.type != :variable}.each do |criteria| -%>
9
- hqmfjs.<%= js_name(criteria) %> = function(patient) {
9
+ hqmfjs.<%= js_name(criteria) %> = function(patient, initialSpecificContext) {
10
10
  <%- if criteria.type == :characteristic and !criteria.property.nil? -%>
11
11
  <%= js_for_characteristic(criteria) %>
12
12
  <%- else -%>
@@ -37,7 +37,7 @@ hqmfjs.<%= js_name(criteria) %> = function(patient) {
37
37
  <%- end -%>
38
38
  <%- if criteria.subset_operators -%>
39
39
  <%- 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 %>);
40
+ events = <%= subset_operator.type %>(events<%= ", #{js_for_bounds(subset_operator.value)}" if subset_operator.value %>, initialSpecificContext);
41
41
  <%- end # each operator -%>
42
42
  <%- end # subset operators -%>
43
43
  return events;
@@ -1,5 +1,5 @@
1
1
  var events = <%= criteria.derivation_operator %>(
2
2
  <%- criteria.children_criteria.each_with_index do |child, index| -%>
3
- <%= "hqmfjs.#{child}(patient)" %><%= "," if index<criteria.children_criteria.length-1 %>
3
+ <%= "hqmfjs.#{child}(patient, initialSpecificContext)" %><%= "," if index<criteria.children_criteria.length-1 %>
4
4
  <%- end -%>
5
5
  );
data/lib/generator/js.rb CHANGED
@@ -138,8 +138,8 @@ module HQMF2JS
138
138
  end
139
139
 
140
140
  # Returns the JavaScript generated for a HQMF::Precondition
141
- def js_for_precondition(precondition, indent)
142
- HQMF2JS::Generator.render_template('precondition', {'doc' => doc, 'precondition' => precondition, 'indent' => indent})
141
+ def js_for_precondition(precondition, indent, context=false)
142
+ HQMF2JS::Generator.render_template('precondition', {'doc' => doc, 'precondition' => precondition, 'indent' => indent, 'context' => context})
143
143
  end
144
144
 
145
145
  def patient_api_method(criteria)
@@ -202,6 +202,7 @@ module HQMF2JS
202
202
  #{js_for(population[HQMF::PopulationCriteria::DENEXCEP], HQMF::PopulationCriteria::DENEXCEP)}
203
203
  // CV
204
204
  #{js_for(population[HQMF::PopulationCriteria::MSRPOPL], HQMF::PopulationCriteria::MSRPOPL)}
205
+ #{js_for(population[HQMF::PopulationCriteria::OBSERV], HQMF::PopulationCriteria::OBSERV)}
205
206
  "
206
207
  end
207
208
 
@@ -224,7 +225,11 @@ module HQMF2JS
224
225
  type ||= criteria_code
225
226
  criteria = @doc.population_criteria(criteria_code)
226
227
  if criteria && criteria.preconditions && criteria.preconditions.length > 0
227
- HQMF2JS::Generator.render_template('population_criteria', {'doc' => @doc, 'criteria' => criteria, 'type'=>type})
228
+ if type==HQMF::PopulationCriteria::OBSERV
229
+ HQMF2JS::Generator.render_template('observation_criteria', {'doc' => @doc, 'criteria' => criteria, 'type'=>type})
230
+ else
231
+ HQMF2JS::Generator.render_template('population_criteria', {'doc' => @doc, 'criteria' => criteria, 'type'=>type})
232
+ end
228
233
  else
229
234
  "hqmfjs.#{type} = function(patient) { return new Boolean(#{when_not_found}); }"
230
235
  end
@@ -13,6 +13,7 @@ hqmfjs.MeasurePeriod = function(patient) {
13
13
  }
14
14
  if (typeof effective_date === 'number') {
15
15
  MeasurePeriod.high.date = new Date(1000*effective_date);
16
- MeasurePeriod.low.date = new Date(1000*effective_date);
16
+ // add one minute before pulling off the year. This turns 12-31-2012 23:59 into 1-1-2013 00:00 => 1-1-2012 00:00
17
+ MeasurePeriod.low.date = new Date(1000*(effective_date+60));
17
18
  MeasurePeriod.low.date.setFullYear(MeasurePeriod.low.date.getFullYear()-1);
18
19
  }
@@ -0,0 +1,4 @@
1
+ hqmfjs.<%= type %> = function(patient, initialSpecificContext) {
2
+ return <%= js_for_precondition(criteria.preconditions()[0], 0, true) -%>;
3
+ };
4
+
@@ -1,4 +1,4 @@
1
- hqmfjs.<%= type %> = function(patient) {
2
- return <%= js_for_precondition(criteria, 0) -%>;
1
+ hqmfjs.<%= type %> = function(patient, initialSpecificContext) {
2
+ return <%= js_for_precondition(criteria, 0, true) -%>;
3
3
  };
4
4
 
@@ -1,14 +1,16 @@
1
1
  <%- if precondition.conjunction? -%>
2
2
  <%- if indent>0 -%>
3
- <%= "\n#{' '*(indent+1)}#{conjunction_code_for(precondition)}(" -%>
3
+ <%= "\n#{' '*(indent+1)}#{conjunction_code_for(precondition)}('#{precondition.id}'," -%>
4
4
  <%- else -%>
5
- <%= "#{conjunction_code_for(precondition)}(" -%>
5
+ <%= "#{conjunction_code_for(precondition)}('#{precondition.id}'," -%>
6
6
  <%- end -%>
7
7
  <%- precondition.preconditions.each_with_index do |child, index| -%>
8
- <%= "#{' '*(indent+1)}#{js_for_precondition(child, indent+1)}" -%>
8
+ <%= "#{' '*(indent+1)}#{js_for_precondition(child, indent+1, context)}" -%>
9
9
  <%= "," if index < precondition.preconditions.length-1 -%>
10
10
  <%- end -%>
11
11
  <%= "\n#{' '*(indent+1)})" -%>
12
+ <%- elsif context -%>
13
+ <%= " hqmfjs.#{js_name(precondition.reference)}(patient, initialSpecificContext)" -%>
12
14
  <%- else -%>
13
15
  <%= "\n#{' '*(indent+1)}hqmfjs.#{js_name(precondition.reference)}(patient)" -%>
14
16
  <%- end -%>
@@ -29,15 +29,19 @@ namespace :hqmf do
29
29
  f.write("// #########################\n\n")
30
30
 
31
31
  f.write("// INITIAL PATIENT POPULATION\n")
32
- f.write(gen.js_for('IPP'))
33
- f.write("// DENOMINATOR\n")
34
- f.write(gen.js_for('DENOM'))
35
- f.write("// NUMERATOR\n")
36
- f.write(gen.js_for('NUMER'))
37
- f.write("// EXCLUSIONS\n")
38
- f.write(gen.js_for('EXCL'))
39
- f.write("// DENOMINATOR EXCEPTIONS\n")
40
- f.write(gen.js_for('DENEXCEP'))
32
+ f.write(gen.js_for(HQMF::PopulationCriteria::IPP))
33
+ f.write("\n// DENOMINATOR\n")
34
+ f.write(gen.js_for(HQMF::PopulationCriteria::DENOM))
35
+ f.write("\n// NUMERATOR\n")
36
+ f.write(gen.js_for(HQMF::PopulationCriteria::NUMER))
37
+ f.write("\n// EXCLUSIONS\n")
38
+ f.write(gen.js_for(HQMF::PopulationCriteria::DENEX))
39
+ f.write("\n// DENOMINATOR EXCEPTIONS\n")
40
+ f.write(gen.js_for(HQMF::PopulationCriteria::DENEXCEP))
41
+ f.write("\n// MSRPOPL\n")
42
+ f.write(gen.js_for(HQMF::PopulationCriteria::MSRPOPL))
43
+ f.write("\n// OBSERV\n")
44
+ f.write(gen.js_for(HQMF::PopulationCriteria::OBSERV))
41
45
  end
42
46
 
43
47
  puts "wrote javascript to: #{out_file}"
@@ -893,7 +893,7 @@
893
893
  {
894
894
  "type": "SUMMARY",
895
895
  "value": {
896
- "type": "IVL_INT",
896
+ "type": "IVL_PQ",
897
897
  "low": {
898
898
  "type": "PQ",
899
899
  "value": "2",
@@ -1118,7 +1118,7 @@
1118
1118
  {
1119
1119
  "type": "COUNT",
1120
1120
  "value": {
1121
- "type": "IVL_INT",
1121
+ "type": "IVL_PQ",
1122
1122
  "low": {
1123
1123
  "type": "PQ",
1124
1124
  "value": "3",
@@ -1145,7 +1145,7 @@
1145
1145
  {
1146
1146
  "type": "COUNT",
1147
1147
  "value": {
1148
- "type": "IVL_INT",
1148
+ "type": "IVL_PQ",
1149
1149
  "low": {
1150
1150
  "type": "PQ",
1151
1151
  "value": "5",
@@ -107,7 +107,7 @@
107
107
  "end_time": 1291166000,
108
108
  "code": {
109
109
  "code": "bar",
110
- "codeSystem": "SNOMED-CT"
110
+ "code_system": "SNOMED-CT"
111
111
  }
112
112
  },
113
113
  "end_time": 1291266000
@@ -34,6 +34,42 @@ class CustomCalculationsTest < Test::Unit::TestCase
34
34
  inr_b9 = new hQuery.CodedEntry({time:#{Time.gm(2010,10,31).to_i}, values:[{scalar:'1.8'}]})
35
35
 
36
36
  list2 = new hqmf.CustomCalc.PercentTTREntries([inr_b1,inr_b2,inr_b3,inr_b4,inr_b5,inr_b6,inr_b7,inr_b8,inr_b9])
37
+
38
+ inr_c1 = new hQuery.CodedEntry({time:#{Time.gm(2010,1,5).to_i}, values:[{scalar:'2.8'}]})
39
+ inr_c2 = new hQuery.CodedEntry({time:#{Time.gm(2010,1,18).to_i}, values:[{scalar:'3.5'}]})
40
+ inr_c3 = new hQuery.CodedEntry({time:#{Time.gm(2010,2,2).to_i}, values:[{scalar:'3.4'}]})
41
+ inr_c4 = new hQuery.CodedEntry({time:#{Time.gm(2010,2,15).to_i}, values:[{scalar:'3.9'}]})
42
+ inr_c5 = new hQuery.CodedEntry({time:#{Time.gm(2010,3,10).to_i}, values:[{scalar:'1.7'}]})
43
+ inr_c6 = new hQuery.CodedEntry({time:#{Time.gm(2010,3,24).to_i}, values:[{scalar:'2.3'}]})
44
+ inr_c7 = new hQuery.CodedEntry({time:#{Time.gm(2010,4,12).to_i}, values:[{scalar:'2.4'}]})
45
+ inr_c8 = new hQuery.CodedEntry({time:#{Time.gm(2010,5,13).to_i}, values:[{scalar:'3.2'}]})
46
+ inr_c9 = new hQuery.CodedEntry({time:#{Time.gm(2010,5,27).to_i}, values:[{scalar:'3.5'}]})
47
+ inr_c10 = new hQuery.CodedEntry({time:#{Time.gm(2010,6,10).to_i}, values:[{scalar:'3.5'}]})
48
+ inr_c11 = new hQuery.CodedEntry({time:#{Time.gm(2010,6,24).to_i}, values:[{scalar:'3.4'}]})
49
+ inr_c12 = new hQuery.CodedEntry({time:#{Time.gm(2010,7,8).to_i}, values:[{scalar:'2.1'}]})
50
+ inr_c13 = new hQuery.CodedEntry({time:#{Time.gm(2010,7,22).to_i}, values:[{scalar:'2.6'}]})
51
+
52
+ list3 = new hqmf.CustomCalc.PercentTTREntries([inr_c10,inr_c2,inr_c6,inr_c9,inr_c5,inr_c3,inr_c7,inr_c8,inr_c4,inr_c1,inr_c11,inr_c12,inr_c13])
53
+
54
+ inr_c1 = new hQuery.CodedEntry({time:#{Time.gm(2010,1,5).to_i}, values:[{scalar:'2.8'}]})
55
+ inr_c2 = new hQuery.CodedEntry({time:#{Time.gm(2010,1,18).to_i}, values:[{scalar:'3.5'}]})
56
+ inr_c3 = new hQuery.CodedEntry({time:#{Time.gm(2010,2,2).to_i}, values:[{scalar:'3.4'}]})
57
+ inr_c4 = new hQuery.CodedEntry({time:#{Time.gm(2010,2,2).to_i}, values:[{scalar:'2.1'}]})
58
+ inr_c5 = new hQuery.CodedEntry({time:#{Time.gm(2010,2,15).to_i}, values:[{scalar:'3.9'}]})
59
+ inr_c6 = new hQuery.CodedEntry({time:#{Time.gm(2010,3,10).to_i}, values:[{scalar:'1.7'}]})
60
+ inr_c7 = new hQuery.CodedEntry({time:#{Time.gm(2010,3,11).to_i}, values:[{scalar:'0.7'}]})
61
+ inr_c8 = new hQuery.CodedEntry({time:#{Time.gm(2010,3,24).to_i}, values:[{scalar:'2.9'},{scalar:'2.3'}]})
62
+ inr_c9 = new hQuery.CodedEntry({time:#{Time.gm(2010,4,12).to_i}, values:[{scalar:'2.4'}]})
63
+ inr_c10 = new hQuery.CodedEntry({time:#{Time.gm(2010,5,13).to_i}, values:[{scalar:'3.2'}]})
64
+ inr_c11 = new hQuery.CodedEntry({time:#{Time.gm(2010,5,13).to_i}, values:[{scalar:'3.6'}]})
65
+ inr_c12 = new hQuery.CodedEntry({time:#{Time.gm(2010,5,27).to_i}, values:[{scalar:'13.5'}]})
66
+ inr_c13 = new hQuery.CodedEntry({time:#{Time.gm(2010,6,10).to_i}, values:[{scalar:'3.5'}]})
67
+ inr_c14 = new hQuery.CodedEntry({time:#{Time.gm(2010,6,24).to_i}, values:[{scalar:'3.4'}]})
68
+ inr_c15 = new hQuery.CodedEntry({time:#{Time.gm(2010,7,8).to_i}, values:[{scalar:'12.1'}]})
69
+ inr_c16 = new hQuery.CodedEntry({time:#{Time.gm(2010,7,22).to_i}, values:[{scalar:'2.6'}, {scalar:'2.1'}]})
70
+
71
+ list4 = new hqmf.CustomCalc.PercentTTREntries([inr_c9,inr_c8,inr_c3,inr_c14,inr_c10,inr_c6,inr_c7,inr_c2,inr_c1,inr_c5,inr_c11,inr_c12,inr_c13,inr_c4,inr_c15,inr_c16])
72
+
37
73
  "
38
74
  @context.eval(test_initialize_js)
39
75
 
@@ -69,6 +105,36 @@ class CustomCalculationsTest < Test::Unit::TestCase
69
105
  def test_calculate_percent_ttr
70
106
  assert @context.eval("Math.abs(list.calculatePercentTTR() - 43.99552468) < .001")
71
107
  end
108
+
109
+ def test_calculate_ttr_testdeck_record
110
+ assert @context.eval("Math.abs(list2.calculateTTR() - 203.66666666) < .001")
111
+ end
112
+
113
+ def test_calculate_percent_ttr_testdeck_record
114
+ assert @context.eval("Math.abs(list2.calculatePercentTTR() - 95.6181533646322) < .001")
115
+ end
72
116
 
117
+ def test_calculate_ttr_out_of_order
118
+ assert @context.eval("Math.abs(list3.calculateTTR() - 87.11113886) < .001")
119
+ end
120
+
121
+ def test_calculate_percent_ttr_out_of_order
122
+ assert @context.eval("Math.abs(list3.calculatePercentTTR() - 43.99552468) < .001")
123
+ end
124
+
125
+ def test_cleanup_of_inr_values
126
+
127
+ cleaned_inrs = [2.8, 3.5, 2.1, 3.9, 1.7, 2.3, 2.4, 3.2, 10.0, 3.5, 3.4, 10.0, 2.6]
128
+
129
+ assert @context.eval("list4.length == #{cleaned_inrs.size}")
130
+ cleaned_inrs.each_with_index do |inr,index|
131
+ assert @context.eval("list4[#{index}].values()[0].scalar() == #{inr}")
132
+ end
133
+
134
+ assert @context.eval("Math.abs(list4.calculateTTR() - 80.3184450684) < .001")
135
+ assert @context.eval("Math.abs(list4.calculatePercentTTR() - 40.56487124668) < .001")
136
+
137
+ end
138
+
73
139
 
74
140
  end
@@ -17,31 +17,31 @@ class LibraryFunctionTest < Test::Unit::TestCase
17
17
  end
18
18
 
19
19
  def test_all_true
20
- @context.eval('allTrue(new Boolean(false),new Boolean(false),new Boolean(false)).isTrue()').must_equal false
21
- @context.eval('allTrue(new Boolean(false),new Boolean(true),new Boolean(false)).isTrue()').must_equal false
22
- @context.eval('allTrue(new Boolean(true),new Boolean(true),new Boolean(true)).isTrue()').must_equal true
23
- @context.eval('allTrue().isTrue()').must_equal false
20
+ @context.eval('allTrue(1,new Boolean(false),new Boolean(false),new Boolean(false)).isTrue()').must_equal false
21
+ @context.eval('allTrue(1,new Boolean(false),new Boolean(true),new Boolean(false)).isTrue()').must_equal false
22
+ @context.eval('allTrue(1,new Boolean(true),new Boolean(true),new Boolean(true)).isTrue()').must_equal true
23
+ @context.eval('allTrue(1).isTrue()').must_equal false
24
24
  end
25
25
 
26
26
  def test_at_least_one_true
27
- @context.eval('atLeastOneTrue(new Boolean(true),new Boolean(false),new Boolean(false)).isTrue()').must_equal true
28
- @context.eval('atLeastOneTrue(new Boolean(true),new Boolean(true),new Boolean(true)).isTrue()').must_equal true
29
- @context.eval('atLeastOneTrue(new Boolean(false),new Boolean(false),new Boolean(false)).isTrue()').must_equal false
30
- @context.eval('atLeastOneTrue().isTrue()').must_equal false
27
+ @context.eval('atLeastOneTrue(1,new Boolean(true),new Boolean(false),new Boolean(false)).isTrue()').must_equal true
28
+ @context.eval('atLeastOneTrue(1,new Boolean(true),new Boolean(true),new Boolean(true)).isTrue()').must_equal true
29
+ @context.eval('atLeastOneTrue(1,new Boolean(false),new Boolean(false),new Boolean(false)).isTrue()').must_equal false
30
+ @context.eval('atLeastOneTrue(1).isTrue()').must_equal false
31
31
  end
32
32
 
33
33
  def test_all_false
34
- @context.eval('allFalse(new Boolean(false),new Boolean(false),new Boolean(false)).isTrue()').must_equal true
35
- @context.eval('allFalse(new Boolean(false),new Boolean(true),new Boolean(false)).isTrue()').must_equal false
36
- @context.eval('allFalse(new Boolean(true),new Boolean(true),new Boolean(true)).isTrue()').must_equal false
37
- @context.eval('allFalse().isTrue()').must_equal false
34
+ @context.eval('allFalse(1,new Boolean(false),new Boolean(false),new Boolean(false)).isTrue()').must_equal true
35
+ @context.eval('allFalse(1,new Boolean(false),new Boolean(true),new Boolean(false)).isTrue()').must_equal false
36
+ @context.eval('allFalse(1,new Boolean(true),new Boolean(true),new Boolean(true)).isTrue()').must_equal false
37
+ @context.eval('allFalse(1).isTrue()').must_equal false
38
38
  end
39
39
 
40
40
  def test_at_least_one_false
41
- @context.eval('atLeastOneFalse(new Boolean(true),new Boolean(false),new Boolean(false)).isTrue()').must_equal true
42
- @context.eval('atLeastOneFalse(new Boolean(true),new Boolean(true),new Boolean(true)).isTrue()').must_equal false
43
- @context.eval('atLeastOneFalse(new Boolean(false),new Boolean(false),new Boolean(false)).isTrue()').must_equal true
44
- @context.eval('atLeastOneFalse().isTrue()').must_equal false
41
+ @context.eval('atLeastOneFalse(1,new Boolean(true),new Boolean(false),new Boolean(false)).isTrue()').must_equal true
42
+ @context.eval('atLeastOneFalse(1,new Boolean(true),new Boolean(true),new Boolean(true)).isTrue()').must_equal false
43
+ @context.eval('atLeastOneFalse(1,new Boolean(false),new Boolean(false),new Boolean(false)).isTrue()').must_equal true
44
+ @context.eval('atLeastOneFalse(1).isTrue()').must_equal false
45
45
  end
46
46
 
47
47
  def test_patient_extensions
@@ -202,6 +202,29 @@ class SpecificsTest < Test::Unit::TestCase
202
202
 
203
203
  end
204
204
 
205
+ def test_specifics_timediff
206
+ init_rows = "
207
+ var row1 = new Row('OccurrenceAEncounter',{'OccurrenceAEncounter':{'id':1},'OccurrenceBEncounter':{'id':20}});
208
+ var row2 = new Row('OccurrenceAEncounter',{'OccurrenceAEncounter':{'id':2},'OccurrenceBEncounter':{'id':20}});
209
+ var row3 = new Row('OccurrenceAEncounter',{'OccurrenceAEncounter':{'id':3},'OccurrenceBEncounter':{'id':30}});
210
+
211
+ var specific = new hqmf.SpecificOccurrence([row1,row2,row3]);
212
+
213
+ var ts1 = new TS('20100101100000');
214
+ var ts2 = new TS('20100101101000');
215
+
216
+ var events1 = [{'id': 1, 'asTS': function() {return ts1;}}];
217
+ events1.specific_occurrence = 'OccurrenceAEncounter';
218
+ var events2 = [{'id': 20, 'asTS': function() {return ts2;}},{'id': 30, 'asTS': function() {return ts2;}}];
219
+ events2.specific_occurrence = 'OccurrenceBEncounter';
220
+ var timediffs = TIMEDIFF(XPRODUCT(events1, events2), null, specific);
221
+ "
222
+
223
+ @context.eval(init_rows)
224
+ @context.eval("timediffs.length").must_equal 1
225
+ @context.eval("timediffs[0]").must_equal 10
226
+ end
227
+
205
228
  def test_specifics_event_counting
206
229
 
207
230
  init_rows = "
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hqmf2js
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-12-19 00:00:00.000000000 Z
14
+ date: 2013-02-27 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: nokogiri
@@ -93,6 +93,38 @@ dependencies:
93
93
  - - ~>
94
94
  - !ruby/object:Gem::Version
95
95
  version: 1.1.0
96
+ - !ruby/object:Gem::Dependency
97
+ name: health-data-standards
98
+ requirement: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 3.0.1
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ~>
110
+ - !ruby/object:Gem::Version
111
+ version: 3.0.1
112
+ - !ruby/object:Gem::Dependency
113
+ name: hquery-patient-api
114
+ requirement: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ~>
118
+ - !ruby/object:Gem::Version
119
+ version: 1.0.1
120
+ type: :runtime
121
+ prerelease: false
122
+ version_requirements: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ~>
126
+ - !ruby/object:Gem::Version
127
+ version: 1.0.1
96
128
  description: A library for converting HQMF files to executable JavaScript suitable
97
129
  for use with the hQuery Gateway
98
130
  email: hquery-talk@googlegroups.com
@@ -124,6 +156,7 @@ files:
124
156
  - lib/generator/derived_data.js.erb
125
157
  - lib/generator/js.rb
126
158
  - lib/generator/measure_period.js.erb
159
+ - lib/generator/observation_criteria.js.erb
127
160
  - lib/generator/patient_data.js.erb
128
161
  - lib/generator/population_criteria.js.erb
129
162
  - lib/generator/precondition.js.erb