hqmf2js 1.1.0 → 1.2.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.
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