hqmf2js 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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