hqmf2js 1.2.1 → 1.3.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 (39) hide show
  1. checksums.yaml +15 -0
  2. data/.travis.yml +2 -5
  3. data/Gemfile +6 -4
  4. data/Gemfile.lock +83 -86
  5. data/Rakefile +9 -5
  6. data/app/assets/javascripts/hqmf_util.js.coffee +98 -23
  7. data/app/assets/javascripts/logging_utils.js.coffee +25 -16
  8. data/app/assets/javascripts/patient_api_extension.js.coffee +85 -0
  9. data/app/assets/javascripts/specifics.js.coffee +60 -14
  10. data/hqmf2js.gemspec +8 -8
  11. data/lib/assets/javascripts/libraries/map_reduce_utils.js +130 -0
  12. data/lib/generator/characteristic.js.erb +28 -17
  13. data/lib/generator/codes_to_json.rb +5 -5
  14. data/lib/generator/data_criteria.js.erb +5 -5
  15. data/lib/generator/execution.rb +144 -0
  16. data/lib/generator/js.rb +46 -21
  17. data/lib/generator/observation_criteria.js.erb +1 -1
  18. data/lib/generator/patient_data.js.erb +22 -19
  19. data/lib/generator/population_criteria.js.erb +6 -1
  20. data/lib/generator/precondition.js.erb +5 -7
  21. data/lib/hqmf2js.rb +1 -0
  22. data/test/fixtures/codes/codes.json +54 -0
  23. data/test/fixtures/json/0495.json +1014 -0
  24. data/test/fixtures/json/59New.json +148 -0
  25. data/test/fixtures/json/data_criteria/specific_occurrence.json +27 -0
  26. data/test/fixtures/json/data_criteria/temporals_with_anynonnull.json +27 -0
  27. data/test/fixtures/patients/larry_vanderman.json +4 -4
  28. data/test/simplecov.rb +19 -0
  29. data/test/test_helper.rb +1 -1
  30. data/test/unit/cmd_test.rb +92 -0
  31. data/test/unit/codes_to_json_test.rb +32 -0
  32. data/test/unit/erb_context_test.rb +64 -0
  33. data/test/unit/hqmf_from_json_javascript_test.rb +37 -2
  34. data/test/unit/hqmf_javascript_test.rb +18 -0
  35. data/test/unit/js_object_test.rb +27 -0
  36. data/test/unit/library_function_test.rb +22 -21
  37. data/test/unit/specifics_test.rb +7 -2
  38. metadata +16 -121
  39. data/lib/tasks/cover_me.rake +0 -8
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YTFiZDJjMjNmZGViNTU3MGE1MzY2M2YwYzBhZDVmOTViMjRhNDk1Zg==
5
+ data.tar.gz: !binary |-
6
+ ZGRlMTYwNmRmODBlZjNkNmQwY2E1Yjg4NmY2YmMwZjIxMjNmYjNkZA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZWM3NDBhMzBmYzg5YjE0ODAyZmEzNDliMDgzODIyZmI0MGIxM2FlZDY3ZTBm
10
+ NDkwZjhhMjMyMGZkZWNjYzk1ZDI0OWFlYjdiYzFmMzU5YmU2MTM3YWMxMGQ5
11
+ ZDcyMmZiNTdlMTdmMGZhZWVjYmI0YWFiNmUwNzM2MzhlYzcyNjY=
12
+ data.tar.gz: !binary |-
13
+ YTg5ZDhiZjZlOTYxNzY5ZTlkNjI3NDI2NGQzM2M4YzVkZDEwMTAxZDJmYjQ5
14
+ ZjJkNWFkZmY3N2UyZDZhYWI1NWY5ZjJkOWU2OGMzZjA4NjhjOTM5ZGM3MjRj
15
+ MDYzODYwMDAyODU2ZWNhMjhmMWIzMDJjNDAxN2ZmNzNkZjcxZDQ=
data/.travis.yml CHANGED
@@ -1,17 +1,14 @@
1
1
  language: ruby
2
-
3
2
  rvm:
4
3
  - 1.9.3
5
-
6
- script: bundle exec rake
7
-
4
+ script: bundle exec rake test
8
5
  notifications:
9
6
  email:
10
7
  recipients:
11
8
  - healthcare-ci@googlegroups.com
12
9
  on_failure: change
13
10
 
14
- branches:
11
+ branches:
15
12
  only:
16
13
  - master
17
14
  - develop
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem 'rails', '3.2.9'
3
+ gem 'rails', '3.2.14'
4
4
 
5
5
  group :assets do
6
6
  gem 'sass-rails'
@@ -8,10 +8,11 @@ group :assets do
8
8
  gem 'uglifier'
9
9
  end
10
10
 
11
- gemspec
11
+ gem 'hquery-patient-api', '1.0.4'
12
+ gem 'health-data-standards', :git => 'https://github.com/projectcypress/health-data-standards.git', :branch => 'master'
12
13
 
13
14
  gem 'nokogiri'
14
- gem 'sprockets', '~> 2.2.2'
15
+ gem 'sprockets'
15
16
  gem 'coffee-script'
16
17
  gem 'uglifier'
17
18
  gem 'tilt'
@@ -19,9 +20,10 @@ gem 'rake'
19
20
  gem 'pry'
20
21
 
21
22
  group :test do
23
+ gem 'simplecov', :require => false
24
+
22
25
  gem 'minitest'
23
26
  gem 'turn', :require => false
24
- gem 'cover_me', '~> 1.2.0'
25
27
  gem 'awesome_print', :require => 'ap'
26
28
 
27
29
  platforms :ruby do
data/Gemfile.lock CHANGED
@@ -1,46 +1,53 @@
1
- PATH
2
- remote: .
1
+ GIT
2
+ remote: https://github.com/projectcypress/health-data-standards.git
3
+ revision: 7a5673f90ab59b1350e22a71aefc1de5b569d98b
4
+ branch: master
3
5
  specs:
4
- hqmf2js (1.2.1)
5
- coffee-script (~> 2.2.0)
6
- health-data-standards (~> 3.1.1)
7
- hquery-patient-api (~> 1.0.2)
8
- nokogiri (~> 1.5.5)
9
- sprockets (~> 2.2.2)
10
- tilt (~> 1.3.3)
6
+ health-data-standards (3.4.3)
7
+ activesupport (~> 3.2.14)
8
+ builder (~> 3.0.0)
9
+ erubis (~> 2.7.0)
10
+ log4r (~> 1.1.10)
11
+ memoist (~> 0.9.1)
12
+ mongoid (~> 3.1.4)
13
+ mongoid-tree (~> 1.0.4)
14
+ nokogiri (= 1.6.0)
15
+ rest-client (~> 1.6.7)
16
+ rubyzip (= 0.9.9)
17
+ uuid (~> 2.3.7)
11
18
 
12
19
  GEM
13
20
  remote: http://rubygems.org/
14
21
  specs:
15
- actionmailer (3.2.9)
16
- actionpack (= 3.2.9)
17
- mail (~> 2.4.4)
18
- actionpack (3.2.9)
19
- activemodel (= 3.2.9)
20
- activesupport (= 3.2.9)
22
+ actionmailer (3.2.14)
23
+ actionpack (= 3.2.14)
24
+ mail (~> 2.5.4)
25
+ actionpack (3.2.14)
26
+ activemodel (= 3.2.14)
27
+ activesupport (= 3.2.14)
21
28
  builder (~> 3.0.0)
22
29
  erubis (~> 2.7.0)
23
30
  journey (~> 1.0.4)
24
- rack (~> 1.4.0)
31
+ rack (~> 1.4.5)
25
32
  rack-cache (~> 1.2)
26
33
  rack-test (~> 0.6.1)
27
34
  sprockets (~> 2.2.1)
28
- activemodel (3.2.9)
29
- activesupport (= 3.2.9)
35
+ activemodel (3.2.14)
36
+ activesupport (= 3.2.14)
30
37
  builder (~> 3.0.0)
31
- activerecord (3.2.9)
32
- activemodel (= 3.2.9)
33
- activesupport (= 3.2.9)
38
+ activerecord (3.2.14)
39
+ activemodel (= 3.2.14)
40
+ activesupport (= 3.2.14)
34
41
  arel (~> 3.0.2)
35
42
  tzinfo (~> 0.3.29)
36
- activeresource (3.2.9)
37
- activemodel (= 3.2.9)
38
- activesupport (= 3.2.9)
39
- activesupport (3.2.9)
40
- i18n (~> 0.6)
43
+ activeresource (3.2.14)
44
+ activemodel (= 3.2.14)
45
+ activesupport (= 3.2.14)
46
+ activesupport (3.2.14)
47
+ i18n (~> 0.6, >= 0.6.4)
41
48
  multi_json (~> 1.0)
42
49
  ansi (1.4.3)
43
- arel (3.0.2)
50
+ arel (3.0.3)
44
51
  awesome_print (1.1.0)
45
52
  builder (3.0.4)
46
53
  coderay (1.0.8)
@@ -51,81 +58,67 @@ GEM
51
58
  coffee-script-source
52
59
  execjs
53
60
  coffee-script-source (1.4.0)
54
- configatron (2.9.1)
55
- yamler (>= 0.1.0)
56
- cover_me (1.2.0)
57
- configatron
58
- hashie
59
61
  erubis (2.7.0)
60
62
  execjs (1.4.0)
61
63
  multi_json (~> 1.0)
62
- hashie (1.2.0)
63
- health-data-standards (3.1.1)
64
- activesupport (~> 3.2.9)
65
- builder (~> 3.0.0)
66
- erubis (~> 2.7.0)
67
- log4r (~> 1.1.10)
68
- memoist (~> 0.9.0)
69
- mongoid (~> 3.1.0)
70
- nokogiri (~> 1.5.5)
71
- rest-client (~> 1.6.7)
72
- rubyzip
73
- uuid (~> 2.3.5)
74
- hike (1.2.1)
75
- hquery-patient-api (1.0.2)
76
- i18n (0.6.1)
64
+ hike (1.2.3)
65
+ hquery-patient-api (1.0.4)
66
+ i18n (0.6.9)
77
67
  journey (1.0.4)
78
- json (1.7.5)
68
+ json (1.8.1)
79
69
  libv8 (3.3.10.4)
80
70
  log4r (1.1.10)
81
- macaddr (1.6.1)
82
- systemu (~> 2.5.0)
83
- mail (2.4.4)
84
- i18n (>= 0.4.0)
71
+ macaddr (1.6.7)
72
+ systemu (~> 2.6.2)
73
+ mail (2.5.4)
85
74
  mime-types (~> 1.16)
86
75
  treetop (~> 1.4.8)
87
- memoist (0.9.0)
76
+ memoist (0.9.1)
88
77
  method_source (0.8.1)
89
- mime-types (1.20.1)
78
+ mime-types (1.25.1)
79
+ mini_portile (0.5.3)
90
80
  minitest (4.3.2)
91
- mongoid (3.1.3)
81
+ mongoid (3.1.6)
92
82
  activemodel (~> 3.2)
93
- moped (~> 1.4.2)
83
+ moped (~> 1.4)
94
84
  origin (~> 1.0)
95
- tzinfo (~> 0.3.22)
96
- moped (1.4.5)
97
- multi_json (1.5.0)
98
- nokogiri (1.5.6)
99
- origin (1.0.11)
100
- polyglot (0.3.3)
85
+ tzinfo (~> 0.3.29)
86
+ mongoid-tree (1.0.4)
87
+ mongoid (>= 3.0, <= 4.0)
88
+ moped (1.5.2)
89
+ multi_json (1.9.2)
90
+ nokogiri (1.6.0)
91
+ mini_portile (~> 0.5.0)
92
+ origin (1.1.0)
93
+ polyglot (0.3.4)
101
94
  pry (0.9.10)
102
95
  coderay (~> 1.0.5)
103
96
  method_source (~> 0.8)
104
97
  slop (~> 3.3.1)
105
- rack (1.4.4)
98
+ rack (1.4.5)
106
99
  rack-cache (1.2)
107
100
  rack (>= 0.4)
108
- rack-ssl (1.3.2)
101
+ rack-ssl (1.3.4)
109
102
  rack
110
103
  rack-test (0.6.2)
111
104
  rack (>= 1.0)
112
- rails (3.2.9)
113
- actionmailer (= 3.2.9)
114
- actionpack (= 3.2.9)
115
- activerecord (= 3.2.9)
116
- activeresource (= 3.2.9)
117
- activesupport (= 3.2.9)
105
+ rails (3.2.14)
106
+ actionmailer (= 3.2.14)
107
+ actionpack (= 3.2.14)
108
+ activerecord (= 3.2.14)
109
+ activeresource (= 3.2.14)
110
+ activesupport (= 3.2.14)
118
111
  bundler (~> 1.0)
119
- railties (= 3.2.9)
120
- railties (3.2.9)
121
- actionpack (= 3.2.9)
122
- activesupport (= 3.2.9)
112
+ railties (= 3.2.14)
113
+ railties (3.2.14)
114
+ actionpack (= 3.2.14)
115
+ activesupport (= 3.2.14)
123
116
  rack-ssl (~> 1.3.2)
124
117
  rake (>= 0.8.7)
125
118
  rdoc (~> 3.4)
126
119
  thor (>= 0.14.6, < 2.0)
127
- rake (10.0.2)
128
- rdoc (3.12)
120
+ rake (10.2.0)
121
+ rdoc (3.12.2)
129
122
  json (~> 1.4)
130
123
  rest-client (1.6.7)
131
124
  mime-types (>= 1.16)
@@ -135,29 +128,32 @@ GEM
135
128
  railties (~> 3.2.0)
136
129
  sass (>= 3.1.10)
137
130
  tilt (~> 1.3)
131
+ simplecov (0.7.1)
132
+ multi_json (~> 1.0)
133
+ simplecov-html (~> 0.7.1)
134
+ simplecov-html (0.7.1)
138
135
  slop (3.3.3)
139
136
  sprockets (2.2.2)
140
137
  hike (~> 1.2)
141
138
  multi_json (~> 1.0)
142
139
  rack (~> 1.0)
143
140
  tilt (~> 1.1, != 1.3.0)
144
- systemu (2.5.2)
141
+ systemu (2.6.4)
145
142
  therubyracer (0.10.2)
146
143
  libv8 (~> 3.3.10)
147
- thor (0.16.0)
148
- tilt (1.3.3)
149
- treetop (1.4.12)
144
+ thor (0.19.1)
145
+ tilt (1.4.1)
146
+ treetop (1.4.15)
150
147
  polyglot
151
148
  polyglot (>= 0.3.1)
152
149
  turn (0.9.6)
153
150
  ansi
154
- tzinfo (0.3.35)
151
+ tzinfo (0.3.39)
155
152
  uglifier (1.3.0)
156
153
  execjs (>= 0.3.0)
157
154
  multi_json (~> 1.0, >= 1.0.2)
158
155
  uuid (2.3.7)
159
156
  macaddr (~> 1.0)
160
- yamler (0.1.0)
161
157
 
162
158
  PLATFORMS
163
159
  ruby
@@ -166,15 +162,16 @@ DEPENDENCIES
166
162
  awesome_print
167
163
  coffee-rails
168
164
  coffee-script
169
- cover_me (~> 1.2.0)
170
- hqmf2js!
165
+ health-data-standards!
166
+ hquery-patient-api (= 1.0.4)
171
167
  minitest
172
168
  nokogiri
173
169
  pry
174
- rails (= 3.2.9)
170
+ rails (= 3.2.14)
175
171
  rake
176
172
  sass-rails
177
- sprockets (~> 2.2.2)
173
+ simplecov
174
+ sprockets
178
175
  therubyracer
179
176
  therubyrhino
180
177
  tilt
data/Rakefile CHANGED
@@ -11,12 +11,16 @@ Dir['lib/tasks/*.rake'].sort.each do |ext|
11
11
  load ext
12
12
  end
13
13
 
14
- $LOAD_PATH << File.expand_path("../test",__FILE__)
15
14
  desc "Run basic tests"
16
- Rake::TestTask.new("test_units") { |t|
17
- t.pattern = 'test/unit/*_test.rb'
15
+ Rake::TestTask.new(:test_unit) do |t|
16
+ t.libs << "test"
17
+ t.test_files = FileList['test/**/*_test.rb']
18
18
  t.verbose = true
19
19
  t.warning = false
20
- }
20
+ end
21
+
22
+ task :test => [:test_unit] do
23
+ system("open coverage/index.html")
24
+ end
21
25
 
22
- task :default => [:test_units,'cover_me:report']
26
+ task :default => [:test]
@@ -52,6 +52,7 @@ class TS
52
52
  else
53
53
  earlier = @date
54
54
  later = ts.asDate()
55
+ return Number.MAX_VALUE if !earlier? || !later?
55
56
  if granularity=="a"
56
57
  TS.yearsDifference(earlier,later)
57
58
  else if granularity=="mo"
@@ -91,6 +92,9 @@ class TS
91
92
  [a,b] = TS.dropSeconds(@date, other.date)
92
93
  a.getTime() > b.getTime()
93
94
 
95
+ equals: (other) ->
96
+ (@date==null && other.date==null) || (@date.getTime()==other.date.getTime())
97
+
94
98
  # Returns whether this TS is before or concurrent with the supplied TS ignoring seconds
95
99
  beforeOrConcurrent: (other) ->
96
100
  if @date==null || other.date==null
@@ -398,6 +402,10 @@ class IVL_TS
398
402
  @low.asDate() && other.high.asDate() && @low.withinSameMinute(other.high)
399
403
  else
400
404
  false
405
+
406
+ equals: (other) ->
407
+ (@low==null && other.low==null) || (@low.equals(other.low)) && (@high==null && other.high==null) || (@high.equals(other.high))
408
+
401
409
  @IVL_TS = IVL_TS
402
410
 
403
411
  # Used to represent a value that will match any other value that is not null.
@@ -408,29 +416,80 @@ class ANYNonNull
408
416
  val != null
409
417
  @ANYNonNull = ANYNonNull
410
418
 
419
+ invokeOne = (patient, initialSpecificContext, fn) ->
420
+ if typeof(fn.isTrue)=='function' || typeof(fn)=='boolean'
421
+ fn
422
+ else
423
+ fn(patient, initialSpecificContext)
424
+ @invokeOne = invokeOne
425
+
426
+ evalUnlessShortCircuit = (fn) ->
427
+ # if we are short circuiting then return the function uncalled, if we are not then call the function and
428
+ # evaluate the tree. If uncalled, from here the function will only be called if required
429
+ if (Logger.short_circuit) then fn else fn()
430
+ @evalUnlessShortCircuit = evalUnlessShortCircuit
431
+
432
+ invokeAll = (patient, initialSpecificContext, fns) ->
433
+ (invokeOne(patient, initialSpecificContext, fn) for fn in fns)
434
+ @invokeAll = invokeAll
435
+
411
436
  # Returns true if one or more of the supplied values is true
412
- atLeastOneTrue = (precondition, values...) ->
413
- trueValues = (value for value in values when value && value.isTrue())
414
- trueValues.length>0
415
- hqmf.SpecificsManager.unionAll(new Boolean(trueValues.length>0), values)
437
+ atLeastOneTrue = (precondition, patient, initialSpecificContext, valueFns...) ->
438
+ evalUnlessShortCircuit ->
439
+ values = invokeAll(patient, initialSpecificContext, valueFns)
440
+ trueValues = (value for value in values when value && value.isTrue())
441
+ hqmf.SpecificsManager.unionAll(new Boolean(trueValues.length>0), values)
416
442
  @atLeastOneTrue = atLeastOneTrue
417
-
443
+
418
444
  # Returns true if all of the supplied values are true
419
- allTrue = (precondition, values...) ->
420
- trueValues = (value for value in values when value && value.isTrue())
421
- hqmf.SpecificsManager.intersectAll(new Boolean(trueValues.length>0 && trueValues.length==values.length), values)
445
+ allTrue = (precondition, patient, initialSpecificContext, valueFns...) ->
446
+ evalUnlessShortCircuit ->
447
+ values = []
448
+ for valueFn in valueFns
449
+ value = invokeOne(patient, initialSpecificContext, valueFn)
450
+ # break if the we have a false value and we're short circuiting.
451
+ #If we're not short circuiting then we want to calculate everything
452
+ break if value.isFalse() && Logger.short_circuit
453
+ values.push(value)
454
+ trueValues = (value for value in values when value && value.isTrue())
455
+ if trueValues.length==valueFns.length
456
+ hqmf.SpecificsManager.intersectAll(new Boolean(trueValues.length>0), trueValues)
457
+ else
458
+ # only intersect on false if we are not short circuiting.
459
+ # if we are not short circuiting then we want to have the specifics context returned for rationale
460
+ if Logger.short_circuit
461
+ value = new Boolean(false)
462
+ value.specificContext = hqmf.SpecificsManager.empty()
463
+ value
464
+ else
465
+ hqmf.SpecificsManager.intersectAll(new Boolean(false), values)
466
+
467
+
422
468
  @allTrue = allTrue
423
469
 
424
470
  # Returns true if one or more of the supplied values is false
425
- atLeastOneFalse = (precondition, values...) ->
426
- falseValues = (value for value in values when value.isFalse())
427
- hqmf.SpecificsManager.intersectAll(new Boolean(falseValues.length>0), values, true)
471
+ atLeastOneFalse = (precondition, patient, initialSpecificContext, valueFns...) ->
472
+ # values = invokeAll(patient, initialSpecificContext, valueFns)
473
+ # falseValues = (value for value in values when value.isFalse())
474
+ # hqmf.SpecificsManager.intersectAll(new Boolean(falseValues.length>0), values, true)
475
+ evalUnlessShortCircuit ->
476
+ values = []
477
+ hasFalse = false
478
+ for valueFn in valueFns
479
+ value = invokeOne(patient, initialSpecificContext, valueFn)
480
+ values.push(value)
481
+ if value.isFalse()
482
+ hasFalse = true
483
+ break if Logger.short_circuit
484
+ hqmf.SpecificsManager.intersectAll(new Boolean(values.length>0 && hasFalse), values, true)
428
485
  @atLeastOneFalse = atLeastOneFalse
429
486
 
430
487
  # Returns true if all of the supplied values are false
431
- allFalse = (precondition, values...) ->
432
- falseValues = (value for value in values when value.isFalse())
433
- hqmf.SpecificsManager.unionAll(new Boolean(falseValues.length>0 && falseValues.length==values.length), values, true)
488
+ allFalse = (precondition, patient, initialSpecificContext, valueFns...) ->
489
+ evalUnlessShortCircuit ->
490
+ values = invokeAll(patient, initialSpecificContext, valueFns)
491
+ falseValues = (value for value in values when value.isFalse())
492
+ hqmf.SpecificsManager.unionAll(new Boolean(falseValues.length>0 && falseValues.length==values.length), values, true)
434
493
  @allFalse = allFalse
435
494
 
436
495
  # Return true if compareTo matches value
@@ -447,7 +506,7 @@ anyMatchingValue = (event, valueToMatch) ->
447
506
  # Return only those events whose value matches the supplied value
448
507
  filterEventsByValue = (events, value) ->
449
508
  matchingEvents = (event for event in events when (anyMatchingValue(event, value)))
450
- matchingEvents
509
+ hqmf.SpecificsManager.maintainSpecifics(matchingEvents, events)
451
510
  @filterEventsByValue = filterEventsByValue
452
511
 
453
512
  # Return only those events with a field that matches the supplied value
@@ -530,11 +589,14 @@ XPRODUCT = (eventLists...) ->
530
589
  # Create a new list containing all the events from the supplied event lists
531
590
  UNION = (eventLists...) ->
532
591
  union = []
533
- # keep track of the specific occurrences by encounter ID. This is used in eventsMatchBounds (specifically in buildRowsForMatching down the _.isObject path)
592
+ # keep track of the specific occurrences by encounter ID. This is used in
593
+ # eventsMatchBounds (specifically in buildRowsForMatching down the _.isObject path)
534
594
  specific_occurrence = {}
535
595
  for eventList in eventLists
536
596
  for event in eventList
537
- specific_occurrence[event.id] = eventList.specific_occurrence if eventList.specific_occurrence
597
+ if eventList.specific_occurrence
598
+ specific_occurrence[event.id] ||= []
599
+ specific_occurrence[event.id].push eventList.specific_occurrence
538
600
  union.push(event)
539
601
  union.specific_occurrence = specific_occurrence unless _.isEmpty(specific_occurrence)
540
602
  hqmf.SpecificsManager.unionAll(union, eventLists)
@@ -745,39 +807,52 @@ applySpecificOccurrenceSubset = (operator, result, range, calculateSpecifics) ->
745
807
  result.specificContext = result.specificContext[operator]()
746
808
  result
747
809
 
810
+ uniqueEvents = (events) ->
811
+ hash = {}
812
+ (hash[event.id] = event for event in events)
813
+ _.values(hash)
814
+ @uniqueEvents = uniqueEvents
815
+
816
+ # if we have multiple events at the same exact time and they happen to be the one selected by FIRST, RECENT, etc
817
+ # then we want to select all of these issues as the first, most recent, etc.
818
+ selectConcurrent = (target, events) ->
819
+ targetIVL = target.asIVL_TS()
820
+ uniqueEvents((result for result in events when result.asIVL_TS().equals(targetIVL)))
821
+ @selectConcurrent = selectConcurrent
822
+
748
823
  FIRST = (events) ->
749
824
  result = []
750
- result = [events.sort(dateSortAscending)[0]] if (events.length > 0)
825
+ result = selectConcurrent(events.sort(dateSortAscending)[0], events) if (events.length > 0)
751
826
  applySpecificOccurrenceSubset('FIRST',hqmf.SpecificsManager.maintainSpecifics(result, events))
752
827
  @FIRST = FIRST
753
828
 
754
829
  SECOND = (events) ->
755
830
  result = []
756
- result = [events.sort(dateSortAscending)[1]] if (events.length > 1)
831
+ result = selectConcurrent(events.sort(dateSortAscending)[1], events) if (events.length > 1)
757
832
  applySpecificOccurrenceSubset('SECOND',hqmf.SpecificsManager.maintainSpecifics(result, events))
758
833
  @SECOND = SECOND
759
834
 
760
835
  THIRD = (events) ->
761
836
  result = []
762
- result = [events.sort(dateSortAscending)[2]] if (events.length > 2)
837
+ result = selectConcurrent(events.sort(dateSortAscending)[2], events) if (events.length > 2)
763
838
  applySpecificOccurrenceSubset('THIRD',hqmf.SpecificsManager.maintainSpecifics(result, events))
764
839
  @THIRD = THIRD
765
840
 
766
841
  FOURTH = (events) ->
767
842
  result = []
768
- result = [events.sort(dateSortAscending)[3]] if (events.length > 3)
843
+ result = selectConcurrent(events.sort(dateSortAscending)[3], events) if (events.length > 3)
769
844
  applySpecificOccurrenceSubset('FOURTH',hqmf.SpecificsManager.maintainSpecifics(result, events))
770
845
  @FOURTH = FOURTH
771
846
 
772
847
  FIFTH = (events) ->
773
848
  result = []
774
- result = [events.sort(dateSortAscending)[4]] if (events.length > 4)
849
+ result = selectConcurrent(events.sort(dateSortAscending)[4], events) if (events.length > 4)
775
850
  applySpecificOccurrenceSubset('FIFTH',hqmf.SpecificsManager.maintainSpecifics(result, events))
776
851
  @FIFTH = FIFTH
777
852
 
778
853
  RECENT = (events) ->
779
854
  result = []
780
- result = [events.sort(dateSortDescending)[0]] if (events.length > 0)
855
+ result = selectConcurrent(events.sort(dateSortDescending)[0], events) if (events.length > 0)
781
856
  applySpecificOccurrenceSubset('RECENT',hqmf.SpecificsManager.maintainSpecifics(result, events))
782
857
  @RECENT = RECENT
783
858
 
@@ -4,11 +4,18 @@ class @Logger
4
4
  @info: (string) ->
5
5
  if @enable_logging
6
6
  @logger.push("#{Logger.indent()}#{string}")
7
+
7
8
  @record: (id, result) ->
8
9
  if @enable_rationale and result? and typeof(result.isTrue) == 'function'
9
- @rationale[id] = result.isTrue()
10
+ if result.isTrue() and result.length
11
+ json_results = _.map(result,(item) -> {id: item.id, json: item.json})
12
+ @rationale[id] = {results: json_results }
13
+ else
14
+ @rationale[id] = result.isTrue()
15
+
10
16
  @enable_logging: true
11
17
  @enable_rationale: true
18
+ @short_circuit: true
12
19
  @initialized: false
13
20
  @indentCount = 0
14
21
  @indent: ->
@@ -67,9 +74,10 @@ class @Logger
67
74
  Logger.indentCount--
68
75
 
69
76
 
70
- @injectLogger = (hqmfjs, enable_logging, enable_rationale) ->
77
+ @injectLogger = (hqmfjs, enable_logging, enable_rationale, short_circuit) ->
71
78
  Logger.enable_logging = enable_logging
72
79
  Logger.enable_rationale = enable_rationale
80
+ Logger.short_circuit = short_circuit
73
81
 
74
82
  # Wrap all of the data criteria functions generated from HQMF
75
83
  _.each(_.functions(hqmfjs), (method) ->
@@ -98,20 +106,21 @@ class @Logger
98
106
 
99
107
  # Wrap selected hQuery Patient API functions
100
108
  _.each(_.functions(hQuery.Patient.prototype), (method) ->
101
- if (hQuery.Patient.prototype[method].length == 0)
102
- hQuery.Patient.prototype[method] = _.wrap(hQuery.Patient.prototype[method], (func) ->
103
- Logger.info("called patient.#{method}():")
104
- func = _.bind(func, this)
105
- result = func()
106
- Logger.info("patient.#{method}() -> #{Logger.stringify(result)}")
107
- return result;);
108
- else
109
- hQuery.Patient.prototype[method] = _.wrap(hQuery.Patient.prototype[method], (func) ->
110
- args = Array.prototype.slice.call(arguments,1)
111
- Logger.info("called patient.#{method}(#{args}):")
112
- result = func.apply(this, args)
113
- Logger.info("patient.#{method}(#{args}) -> #{Logger.stringify(result)}")
114
- return result;);
109
+ if method != 'getEvents' && method != 'getAndCacheEvents'
110
+ if (hQuery.Patient.prototype[method].length == 0)
111
+ hQuery.Patient.prototype[method] = _.wrap(hQuery.Patient.prototype[method], (func) ->
112
+ Logger.info("called patient.#{method}():")
113
+ func = _.bind(func, this)
114
+ result = func()
115
+ Logger.info("patient.#{method}() -> #{Logger.stringify(result)}")
116
+ return result;);
117
+ else
118
+ hQuery.Patient.prototype[method] = _.wrap(hQuery.Patient.prototype[method], (func) ->
119
+ args = Array.prototype.slice.call(arguments,1)
120
+ Logger.info("called patient.#{method}(#{args}):")
121
+ result = func.apply(this, args)
122
+ Logger.info("patient.#{method}(#{args}) -> #{Logger.stringify(result)}")
123
+ return result;);
115
124
 
116
125
  );
117
126