hqmf2js 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -10
- data/Gemfile.lock +70 -68
- data/app/assets/javascripts/custom_calculations.js.coffee +72 -0
- data/app/assets/javascripts/hqmf_util.js.coffee +97 -46
- data/app/assets/javascripts/logging_utils.js.coffee +0 -11
- data/app/assets/javascripts/specifics.js.coffee +211 -185
- data/app/assets/javascripts/underscore.js +1200 -0
- data/hqmf2js.gemspec +5 -5
- data/lib/generator/characteristic.js.erb +3 -3
- data/lib/generator/converter.rb +5 -5
- data/lib/generator/data_criteria.js.erb +8 -8
- data/lib/generator/js.rb +25 -5
- data/test/fixtures/codes/codes.xml +6 -0
- data/test/fixtures/patients/larry_vanderman.json +11 -1
- data/test/test_helper.rb +2 -3
- data/test/unit/codes_to_json_test.rb +1 -1
- data/test/unit/custom_calculations_test.rb +74 -0
- data/test/unit/hqmf_from_json_javascript_test.rb +9 -9
- data/test/unit/hqmf_javascript_test.rb +36 -14
- data/test/unit/library_function_test.rb +29 -11
- data/test/unit/specifics_test.rb +200 -82
- metadata +13 -9
- data/test/fixtures/patient_api.js +0 -2823
data/Gemfile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
|
3
|
-
gem 'rails'
|
3
|
+
gem 'rails', '3.2.9'
|
4
4
|
|
5
5
|
group :assets do
|
6
6
|
gem 'sass-rails'
|
@@ -8,17 +8,12 @@ group :assets do
|
|
8
8
|
gem 'uglifier'
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
gem
|
14
|
-
#gem 'hqmf-parser', :git => 'https://github.com/pophealth/hqmf-parser.git', :branch => 'develop'
|
15
|
-
#gem 'hqmf-parser', :path => '../hqmf-parser'
|
16
|
-
gem 'hqmf-parser', '~> 1.0.4'
|
17
|
-
#gem "health-data-standards", :git => 'http://github.com/projectcypress/health-data-standards.git', :branch => 'develop'
|
18
|
-
gem "health-data-standards", '~> 2.1.3'
|
11
|
+
gem "hquery-patient-api", '~> 1.0.0'
|
12
|
+
gem 'hqmf-parser', '~> 1.1.0'
|
13
|
+
gem "health-data-standards", '~> 2.2.0'
|
19
14
|
|
20
15
|
gem 'nokogiri'
|
21
|
-
gem 'sprockets'
|
16
|
+
gem 'sprockets', '~> 2.2.2'
|
22
17
|
gem 'coffee-script'
|
23
18
|
gem 'uglifier'
|
24
19
|
gem 'tilt'
|
data/Gemfile.lock
CHANGED
@@ -1,53 +1,53 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
-
actionmailer (3.2.
|
5
|
-
actionpack (= 3.2.
|
4
|
+
actionmailer (3.2.9)
|
5
|
+
actionpack (= 3.2.9)
|
6
6
|
mail (~> 2.4.4)
|
7
|
-
actionpack (3.2.
|
8
|
-
activemodel (= 3.2.
|
9
|
-
activesupport (= 3.2.
|
7
|
+
actionpack (3.2.9)
|
8
|
+
activemodel (= 3.2.9)
|
9
|
+
activesupport (= 3.2.9)
|
10
10
|
builder (~> 3.0.0)
|
11
11
|
erubis (~> 2.7.0)
|
12
|
-
journey (~> 1.0.
|
12
|
+
journey (~> 1.0.4)
|
13
13
|
rack (~> 1.4.0)
|
14
14
|
rack-cache (~> 1.2)
|
15
15
|
rack-test (~> 0.6.1)
|
16
|
-
sprockets (~> 2.1
|
17
|
-
activemodel (3.2.
|
18
|
-
activesupport (= 3.2.
|
16
|
+
sprockets (~> 2.2.1)
|
17
|
+
activemodel (3.2.9)
|
18
|
+
activesupport (= 3.2.9)
|
19
19
|
builder (~> 3.0.0)
|
20
|
-
activerecord (3.2.
|
21
|
-
activemodel (= 3.2.
|
22
|
-
activesupport (= 3.2.
|
20
|
+
activerecord (3.2.9)
|
21
|
+
activemodel (= 3.2.9)
|
22
|
+
activesupport (= 3.2.9)
|
23
23
|
arel (~> 3.0.2)
|
24
24
|
tzinfo (~> 0.3.29)
|
25
|
-
activeresource (3.2.
|
26
|
-
activemodel (= 3.2.
|
27
|
-
activesupport (= 3.2.
|
28
|
-
activesupport (3.2.
|
25
|
+
activeresource (3.2.9)
|
26
|
+
activemodel (= 3.2.9)
|
27
|
+
activesupport (= 3.2.9)
|
28
|
+
activesupport (3.2.9)
|
29
29
|
i18n (~> 0.6)
|
30
30
|
multi_json (~> 1.0)
|
31
|
-
ansi (1.4.
|
31
|
+
ansi (1.4.3)
|
32
32
|
arel (3.0.2)
|
33
|
-
awesome_print (1.0
|
34
|
-
builder (3.0.
|
33
|
+
awesome_print (1.1.0)
|
34
|
+
builder (3.0.4)
|
35
35
|
choice (0.1.6)
|
36
|
-
coderay (1.0.
|
36
|
+
coderay (1.0.8)
|
37
37
|
coffee-rails (3.2.2)
|
38
38
|
coffee-script (>= 2.2.0)
|
39
39
|
railties (~> 3.2.0)
|
40
40
|
coffee-script (2.2.0)
|
41
41
|
coffee-script-source
|
42
42
|
execjs
|
43
|
-
coffee-script-source (1.
|
43
|
+
coffee-script-source (1.4.0)
|
44
44
|
configatron (2.9.1)
|
45
45
|
yamler (>= 0.1.0)
|
46
46
|
cover_me (1.2.0)
|
47
47
|
configatron
|
48
48
|
hashie
|
49
49
|
erubis (2.7.0)
|
50
|
-
execjs (1.
|
50
|
+
execjs (1.4.0)
|
51
51
|
multi_json (~> 1.0)
|
52
52
|
faraday (0.8.4)
|
53
53
|
multipart-post (~> 1.1)
|
@@ -56,24 +56,25 @@ GEM
|
|
56
56
|
oauth (>= 0.3.6)
|
57
57
|
oauth2 (>= 0.5.0)
|
58
58
|
hashie (1.2.0)
|
59
|
-
health-data-standards (2.
|
59
|
+
health-data-standards (2.2.0)
|
60
|
+
activesupport (~> 3.2.9)
|
60
61
|
builder (~> 3.0.0)
|
61
62
|
erubis (~> 2.7.0)
|
62
|
-
mongoid (~> 3.0.
|
63
|
+
mongoid (~> 3.0.14)
|
63
64
|
nokogiri (~> 1.5.5)
|
64
65
|
rest-client (~> 1.6.7)
|
65
66
|
uuid (~> 2.3.5)
|
66
67
|
hike (1.2.1)
|
67
|
-
hqmf-parser (1.0
|
68
|
+
hqmf-parser (1.1.0)
|
68
69
|
google-spreadsheet-ruby (= 0.1.8)
|
69
70
|
roo (= 1.10.1)
|
70
71
|
rubyzip
|
71
72
|
spreadsheet (= 0.6.8)
|
72
|
-
hquery-patient-api (0.
|
73
|
+
hquery-patient-api (1.0.0)
|
73
74
|
httpauth (0.2.0)
|
74
75
|
i18n (0.6.1)
|
75
|
-
journey (1.0.
|
76
|
-
json (1.7.
|
76
|
+
journey (1.0.4)
|
77
|
+
json (1.7.5)
|
77
78
|
jwt (0.1.5)
|
78
79
|
multi_json (>= 1.0)
|
79
80
|
libv8 (3.3.10.4)
|
@@ -84,16 +85,16 @@ GEM
|
|
84
85
|
i18n (>= 0.4.0)
|
85
86
|
mime-types (~> 1.16)
|
86
87
|
treetop (~> 1.4.8)
|
87
|
-
method_source (0.
|
88
|
-
mime-types (1.
|
89
|
-
minitest (
|
90
|
-
mongoid (3.0.
|
88
|
+
method_source (0.8.1)
|
89
|
+
mime-types (1.19)
|
90
|
+
minitest (4.3.2)
|
91
|
+
mongoid (3.0.15)
|
91
92
|
activemodel (~> 3.1)
|
92
93
|
moped (~> 1.1)
|
93
94
|
origin (~> 1.0)
|
94
95
|
tzinfo (~> 0.3.22)
|
95
|
-
moped (1.
|
96
|
-
multi_json (1.
|
96
|
+
moped (1.3.1)
|
97
|
+
multi_json (1.4.0)
|
97
98
|
multipart-post (1.1.5)
|
98
99
|
nokogiri (1.5.5)
|
99
100
|
oauth (0.4.7)
|
@@ -103,35 +104,35 @@ GEM
|
|
103
104
|
jwt (~> 0.1.4)
|
104
105
|
multi_json (~> 1.0)
|
105
106
|
rack (~> 1.2)
|
106
|
-
origin (1.0.
|
107
|
+
origin (1.0.11)
|
107
108
|
polyglot (0.3.3)
|
108
|
-
pry (0.9.
|
109
|
+
pry (0.9.10)
|
109
110
|
coderay (~> 1.0.5)
|
110
|
-
method_source (~> 0.
|
111
|
-
slop (
|
111
|
+
method_source (~> 0.8)
|
112
|
+
slop (~> 3.3.1)
|
112
113
|
rack (1.4.1)
|
113
114
|
rack-cache (1.2)
|
114
115
|
rack (>= 0.4)
|
115
116
|
rack-ssl (1.3.2)
|
116
117
|
rack
|
117
|
-
rack-test (0.6.
|
118
|
+
rack-test (0.6.2)
|
118
119
|
rack (>= 1.0)
|
119
|
-
rails (3.2.
|
120
|
-
actionmailer (= 3.2.
|
121
|
-
actionpack (= 3.2.
|
122
|
-
activerecord (= 3.2.
|
123
|
-
activeresource (= 3.2.
|
124
|
-
activesupport (= 3.2.
|
120
|
+
rails (3.2.9)
|
121
|
+
actionmailer (= 3.2.9)
|
122
|
+
actionpack (= 3.2.9)
|
123
|
+
activerecord (= 3.2.9)
|
124
|
+
activeresource (= 3.2.9)
|
125
|
+
activesupport (= 3.2.9)
|
125
126
|
bundler (~> 1.0)
|
126
|
-
railties (= 3.2.
|
127
|
-
railties (3.2.
|
128
|
-
actionpack (= 3.2.
|
129
|
-
activesupport (= 3.2.
|
127
|
+
railties (= 3.2.9)
|
128
|
+
railties (3.2.9)
|
129
|
+
actionpack (= 3.2.9)
|
130
|
+
activesupport (= 3.2.9)
|
130
131
|
rack-ssl (~> 1.3.2)
|
131
132
|
rake (>= 0.8.7)
|
132
133
|
rdoc (~> 3.4)
|
133
|
-
thor (
|
134
|
-
rake (0.
|
134
|
+
thor (>= 0.14.6, < 2.0)
|
135
|
+
rake (10.0.2)
|
135
136
|
rdoc (3.12)
|
136
137
|
json (~> 1.4)
|
137
138
|
rest-client (1.6.7)
|
@@ -143,37 +144,38 @@ GEM
|
|
143
144
|
rubyzip (>= 0.9.4)
|
144
145
|
spreadsheet (> 0.6.4)
|
145
146
|
todonotes (>= 0.1.0)
|
146
|
-
ruby-ole (1.2.11.
|
147
|
+
ruby-ole (1.2.11.6)
|
147
148
|
rubyzip (0.9.9)
|
148
|
-
sass (3.
|
149
|
+
sass (3.2.3)
|
149
150
|
sass-rails (3.2.5)
|
150
151
|
railties (~> 3.2.0)
|
151
152
|
sass (>= 3.1.10)
|
152
153
|
tilt (~> 1.3)
|
153
|
-
slop (
|
154
|
+
slop (3.3.3)
|
154
155
|
spreadsheet (0.6.8)
|
155
156
|
ruby-ole (>= 1.0)
|
156
|
-
sprockets (2.
|
157
|
+
sprockets (2.2.2)
|
157
158
|
hike (~> 1.2)
|
159
|
+
multi_json (~> 1.0)
|
158
160
|
rack (~> 1.0)
|
159
161
|
tilt (~> 1.1, != 1.3.0)
|
160
162
|
systemu (2.5.2)
|
161
163
|
therubyracer (0.10.2)
|
162
164
|
libv8 (~> 3.3.10)
|
163
|
-
thor (0.
|
165
|
+
thor (0.16.0)
|
164
166
|
tilt (1.3.3)
|
165
|
-
todonotes (0.1.
|
167
|
+
todonotes (0.1.1)
|
166
168
|
log4r
|
167
|
-
treetop (1.4.
|
169
|
+
treetop (1.4.12)
|
168
170
|
polyglot
|
169
171
|
polyglot (>= 0.3.1)
|
170
|
-
turn (0.9.
|
172
|
+
turn (0.9.6)
|
171
173
|
ansi
|
172
|
-
tzinfo (0.3.
|
173
|
-
uglifier (1.
|
174
|
+
tzinfo (0.3.35)
|
175
|
+
uglifier (1.3.0)
|
174
176
|
execjs (>= 0.3.0)
|
175
|
-
multi_json (>= 1.0.2)
|
176
|
-
uuid (2.3.
|
177
|
+
multi_json (~> 1.0, >= 1.0.2)
|
178
|
+
uuid (2.3.6)
|
177
179
|
macaddr (~> 1.0)
|
178
180
|
yamler (0.1.0)
|
179
181
|
|
@@ -185,16 +187,16 @@ DEPENDENCIES
|
|
185
187
|
coffee-rails
|
186
188
|
coffee-script
|
187
189
|
cover_me (~> 1.2.0)
|
188
|
-
health-data-standards (~> 2.
|
189
|
-
hqmf-parser (~> 1.0
|
190
|
-
hquery-patient-api (~> 0.
|
190
|
+
health-data-standards (~> 2.2.0)
|
191
|
+
hqmf-parser (~> 1.1.0)
|
192
|
+
hquery-patient-api (~> 1.0.0)
|
191
193
|
minitest
|
192
194
|
nokogiri
|
193
195
|
pry
|
194
|
-
rails
|
196
|
+
rails (= 3.2.9)
|
195
197
|
rake
|
196
198
|
sass-rails
|
197
|
-
sprockets
|
199
|
+
sprockets (~> 2.2.2)
|
198
200
|
therubyracer
|
199
201
|
therubyrhino
|
200
202
|
tilt
|
@@ -0,0 +1,72 @@
|
|
1
|
+
@hqmf.CustomCalc = {}
|
2
|
+
|
3
|
+
class @hqmf.CustomCalc.PercentTTREntries extends hQuery.CodedEntryList
|
4
|
+
|
5
|
+
constructor: (events) ->
|
6
|
+
super()
|
7
|
+
events = events.sort(dateSortAscending)
|
8
|
+
@push(event) for event in events
|
9
|
+
@minInr = 2.0
|
10
|
+
@maxInr = 3.0
|
11
|
+
# sort entries ascending
|
12
|
+
# filter duplicates to those closest to 2.5
|
13
|
+
# remove duplicate results on entries
|
14
|
+
|
15
|
+
calculateDaysInRange: (firstInr, secondInr) ->
|
16
|
+
|
17
|
+
if ((@belowRange(firstInr) and @belowRange(secondInr)) or (@aboveRange(firstInr) and @aboveRange(secondInr)))
|
18
|
+
0
|
19
|
+
else if (@inRange(firstInr) and @inRange(secondInr))
|
20
|
+
@differenceInDays(firstInr,secondInr)
|
21
|
+
else if (@outsideRange(firstInr) and @inRange(secondInr))
|
22
|
+
@calculateCrossingRange(firstInr,secondInr)
|
23
|
+
else if (@inRange(firstInr) and @outsideRange(secondInr))
|
24
|
+
@calculateCrossingRange(secondInr, firstInr)
|
25
|
+
else
|
26
|
+
@calculateSpanningRange(firstInr, secondInr)
|
27
|
+
|
28
|
+
calculateCrossingRange: (outside,inside) ->
|
29
|
+
outsideInr = @inrValue(outside)
|
30
|
+
insideInr = @inrValue(inside)
|
31
|
+
boundary = @maxInr
|
32
|
+
boundary = @minInr if (@belowRange(outside))
|
33
|
+
(Math.abs(boundary - insideInr)/Math.abs(insideInr-outsideInr))*@differenceInDays(outside,inside)
|
34
|
+
|
35
|
+
calculateSpanningRange: (first,second) ->
|
36
|
+
(1.0/Math.abs(@inrValue(first)-@inrValue(second)))*@differenceInDays(first,second)
|
37
|
+
|
38
|
+
inRange: (entry) ->
|
39
|
+
inr = @inrValue(entry)
|
40
|
+
inr >= @minInr and inr <= @maxInr
|
41
|
+
|
42
|
+
outsideRange: (entry) ->
|
43
|
+
!@inRange(entry)
|
44
|
+
|
45
|
+
belowRange: (entry) ->
|
46
|
+
inr = @inrValue(entry)
|
47
|
+
inr < @minInr
|
48
|
+
|
49
|
+
aboveRange: (entry) ->
|
50
|
+
inr = @inrValue(entry)
|
51
|
+
inr > @maxInr
|
52
|
+
|
53
|
+
differenceInDays: (first, second) ->
|
54
|
+
getIVL(first).low.difference(getIVL(second).low, 'd')
|
55
|
+
|
56
|
+
inrValue: (entry) ->
|
57
|
+
entry.values()[0].scalar()
|
58
|
+
|
59
|
+
totalNumberOfDays: () ->
|
60
|
+
@differenceInDays(this[0],this[this.length-1])
|
61
|
+
|
62
|
+
calculateTTR: () ->
|
63
|
+
total = 0
|
64
|
+
for left, i in this
|
65
|
+
if (i < this.length-1)
|
66
|
+
right = this[i+1]
|
67
|
+
total += @calculateDaysInRange(left, right)
|
68
|
+
total
|
69
|
+
|
70
|
+
calculatePercentTTR: () ->
|
71
|
+
@calculateTTR()/@totalNumberOfDays()*100
|
72
|
+
|
@@ -15,7 +15,7 @@ class TS
|
|
15
15
|
minute = parseInt(hl7ts.substring(10,12), 10)
|
16
16
|
if isNaN(minute)
|
17
17
|
minute = 0
|
18
|
-
@date = new Date(year, month, day, hour, minute)
|
18
|
+
@date = new Date(Date.UTC(year, month, day, hour, minute))
|
19
19
|
else
|
20
20
|
@date = new Date()
|
21
21
|
|
@@ -24,17 +24,17 @@ class TS
|
|
24
24
|
# wk (week), d (day), h (hour) and min (minute).
|
25
25
|
add: (pq) ->
|
26
26
|
if pq.unit=="a"
|
27
|
-
@date.
|
27
|
+
@date.setUTCFullYear(@date.getUTCFullYear()+pq.value)
|
28
28
|
else if pq.unit=="mo"
|
29
|
-
@date.
|
29
|
+
@date.setUTCMonth(@date.getUTCMonth()+pq.value)
|
30
30
|
else if pq.unit=="wk"
|
31
|
-
@date.
|
31
|
+
@date.setUTCDate(@date.getUTCDate()+(7*pq.value))
|
32
32
|
else if pq.unit=="d"
|
33
|
-
@date.
|
33
|
+
@date.setUTCDate(@date.getUTCDate()+pq.value)
|
34
34
|
else if pq.unit=="h"
|
35
|
-
@date.
|
35
|
+
@date.setUTCHours(@date.getUTCHours()+pq.value)
|
36
36
|
else if pq.unit=="min"
|
37
|
-
@date.
|
37
|
+
@date.setUTCMinutes(@date.getUTCMinutes()+pq.value)
|
38
38
|
else
|
39
39
|
throw "Unknown time unit: "+pq.unit
|
40
40
|
this
|
@@ -76,7 +76,7 @@ class TS
|
|
76
76
|
if @date==null || other.date==null
|
77
77
|
return false
|
78
78
|
if other.inclusive
|
79
|
-
beforeOrConcurrent(other)
|
79
|
+
@beforeOrConcurrent(other)
|
80
80
|
else
|
81
81
|
[a,b] = TS.dropSeconds(@date, other.date)
|
82
82
|
a.getTime() < b.getTime()
|
@@ -86,7 +86,7 @@ class TS
|
|
86
86
|
if @date==null || other.date==null
|
87
87
|
return false
|
88
88
|
if other.inclusive
|
89
|
-
afterOrConcurrent(other)
|
89
|
+
@afterOrConcurrent(other)
|
90
90
|
else
|
91
91
|
[a,b] = TS.dropSeconds(@date, other.date)
|
92
92
|
a.getTime() > b.getTime()
|
@@ -113,21 +113,21 @@ class TS
|
|
113
113
|
|
114
114
|
# Number of whole years between the two time stamps (as Date objects)
|
115
115
|
@yearsDifference: (earlier, later) ->
|
116
|
-
if (later.
|
117
|
-
later.
|
118
|
-
else if (later.
|
119
|
-
later.
|
120
|
-
else if (later.
|
121
|
-
later.
|
116
|
+
if (later.getUTCMonth() < earlier.getUTCMonth())
|
117
|
+
later.getUTCFullYear()-earlier.getUTCFullYear()-1
|
118
|
+
else if (later.getUTCMonth() == earlier.getUTCMonth() && later.getUTCDate() >= earlier.getUTCDate())
|
119
|
+
later.getUTCFullYear()-earlier.getUTCFullYear()
|
120
|
+
else if (later.getUTCMonth() == earlier.getUTCMonth() && later.getUTCDate() < earlier.getUTCDate())
|
121
|
+
later.getUTCFullYear()-earlier.getUTCFullYear()-1
|
122
122
|
else
|
123
|
-
later.
|
123
|
+
later.getUTCFullYear()-earlier.getUTCFullYear()
|
124
124
|
|
125
125
|
# Number of whole months between the two time stamps (as Date objects)
|
126
126
|
@monthsDifference: (earlier, later) ->
|
127
|
-
if (later.
|
128
|
-
(later.
|
127
|
+
if (later.getUTCDate() >= earlier.getUTCDate())
|
128
|
+
(later.getUTCFullYear()-earlier.getUTCFullYear())*12+later.getUTCMonth()-earlier.getUTCMonth()
|
129
129
|
else
|
130
|
-
(later.
|
130
|
+
(later.getUTCFullYear()-earlier.getUTCFullYear())*12+later.getUTCMonth()-earlier.getUTCMonth()-1
|
131
131
|
|
132
132
|
# Number of whole minutes between the two time stamps (as Date objects)
|
133
133
|
@minutesDifference: (earlier, later) ->
|
@@ -140,10 +140,8 @@ class TS
|
|
140
140
|
# Number of days betweem the two time stamps (as Date objects)
|
141
141
|
@daysDifference: (earlier, later) ->
|
142
142
|
# have to discard time portion for day difference calculation purposes
|
143
|
-
e = new Date(earlier.
|
144
|
-
|
145
|
-
l = new Date(later.getFullYear(), later.getMonth(), later.getDate())
|
146
|
-
l.setUTCHours(0)
|
143
|
+
e = new Date(Date.UTC(earlier.getUTCFullYear(), earlier.getUTCMonth(), earlier.getUTCDate()))
|
144
|
+
l = new Date(Date.UTC(later.getUTCFullYear(), later.getUTCMonth(), later.getUTCDate()))
|
147
145
|
Math.floor(TS.hoursDifference(e,l)/24)
|
148
146
|
|
149
147
|
# Number of whole weeks between the two time stmaps (as Date objects)
|
@@ -413,25 +411,25 @@ class ANYNonNull
|
|
413
411
|
atLeastOneTrue = (values...) ->
|
414
412
|
trueValues = (value for value in values when value && value.isTrue())
|
415
413
|
trueValues.length>0
|
416
|
-
|
414
|
+
hqmf.SpecificsManager.unionAll(new Boolean(trueValues.length>0), values)
|
417
415
|
@atLeastOneTrue = atLeastOneTrue
|
418
416
|
|
419
417
|
# Returns true if all of the supplied values are true
|
420
418
|
allTrue = (values...) ->
|
421
419
|
trueValues = (value for value in values when value && value.isTrue())
|
422
|
-
|
420
|
+
hqmf.SpecificsManager.intersectAll(new Boolean(trueValues.length>0 && trueValues.length==values.length), values)
|
423
421
|
@allTrue = allTrue
|
424
422
|
|
425
423
|
# Returns true if one or more of the supplied values is false
|
426
424
|
atLeastOneFalse = (values...) ->
|
427
425
|
falseValues = (value for value in values when value.isFalse())
|
428
|
-
|
426
|
+
hqmf.SpecificsManager.intersectAll(new Boolean(falseValues.length>0), values, true)
|
429
427
|
@atLeastOneFalse = atLeastOneFalse
|
430
428
|
|
431
429
|
# Returns true if all of the supplied values are false
|
432
430
|
allFalse = (values...) ->
|
433
431
|
falseValues = (value for value in values when value.isFalse())
|
434
|
-
|
432
|
+
hqmf.SpecificsManager.unionAll(new Boolean(falseValues.length>0 && falseValues.length==values.length), values, true)
|
435
433
|
@allFalse = allFalse
|
436
434
|
|
437
435
|
# Return true if compareTo matches value
|
@@ -454,12 +452,56 @@ filterEventsByValue = (events, value) ->
|
|
454
452
|
# Return only those events with a field that matches the supplied value
|
455
453
|
filterEventsByField = (events, field, value) ->
|
456
454
|
respondingEvents = (event for event in events when event.respondTo(field))
|
457
|
-
event for event in respondingEvents when value.match(event[field]())
|
455
|
+
result = (event for event in respondingEvents when value.match(event[field]()))
|
456
|
+
hqmf.SpecificsManager.maintainSpecifics(result, events)
|
458
457
|
@filterEventsByField = filterEventsByField
|
459
458
|
|
459
|
+
shiftTimes = (event, field) ->
|
460
|
+
shiftedEvent = new event.constructor(event.json)
|
461
|
+
shiftedEvent.setTimestamp(shiftedEvent[field]())
|
462
|
+
shiftedEvent
|
463
|
+
@shiftTimes = shiftTimes
|
464
|
+
|
465
|
+
adjustBoundsForField = (events, field) ->
|
466
|
+
validEvents = (event for event in events when (event.respondTo(field) and event[field]()))
|
467
|
+
shiftedEvents = (shiftTimes(event, field) for event in validEvents)
|
468
|
+
hqmf.SpecificsManager.maintainSpecifics(shiftedEvents, events)
|
469
|
+
@adjustBoundsForField = adjustBoundsForField
|
470
|
+
|
471
|
+
# Clone the supplied event and replace any facilities with just the supplied one
|
472
|
+
narrowEventForFacility = (event, facility) ->
|
473
|
+
narrowed = new event.constructor(event.json)
|
474
|
+
# uncomment the following line when patient API is modified to support multiple
|
475
|
+
# facilities
|
476
|
+
# narrowed._facilities = [facility]
|
477
|
+
narrowed
|
478
|
+
@narrowEventForFacility = narrowEventForFacility
|
479
|
+
|
480
|
+
# Return a cloned set of events, each with just one of the original facilities
|
481
|
+
denormalizeEvent = (event) ->
|
482
|
+
# the following line should be changed when the patient API is modified to support
|
483
|
+
# more than one facility per encounter
|
484
|
+
# narrowed = (narrowEventForFacility(event, facility) for facility in event.facilities)
|
485
|
+
narrowed = (narrowEventForFacility(event, facility) for facility in [event.facility])
|
486
|
+
@denormalizeEvent = denormalizeEvent
|
487
|
+
|
488
|
+
# Creates a new set of events with one location per event. Input events with more than
|
489
|
+
# one location will be duplicated once per location and each resulting event will
|
490
|
+
# be assigned one location. Start and end times of the event will be adjusted to match the
|
491
|
+
# value of the supplied field
|
492
|
+
denormalizeEventsByLocation = (events, field) ->
|
493
|
+
respondingEvents = (event for event in events when event.respondTo("facility") and event.facility())
|
494
|
+
denormalizedEvents = (denormalizeEvent(event) for event in respondingEvents)
|
495
|
+
denormalizedEvents = [].concat denormalizedEvents...
|
496
|
+
result = adjustBoundsForField(denormalizedEvents, field)
|
497
|
+
hqmf.SpecificsManager.maintainSpecifics(result, events)
|
498
|
+
@denormalizeEventsByLocation = denormalizeEventsByLocation
|
499
|
+
|
460
500
|
# Utility method to obtain the value set for an OID
|
461
501
|
getCodes = (oid) ->
|
462
|
-
OidDictionary[oid]
|
502
|
+
codes = OidDictionary[oid]
|
503
|
+
throw "value set oid could not be found: #{oid}" unless codes?
|
504
|
+
codes
|
463
505
|
@getCodes = getCodes
|
464
506
|
|
465
507
|
# Used for representing XPRODUCTs of arrays, holds both a flattened array that contains
|
@@ -475,7 +517,7 @@ class CrossProduct extends Array
|
|
475
517
|
|
476
518
|
# Create a CrossProduct of the supplied event lists.
|
477
519
|
XPRODUCT = (eventLists...) ->
|
478
|
-
|
520
|
+
hqmf.SpecificsManager.intersectAll(new CrossProduct(eventLists), eventLists)
|
479
521
|
@XPRODUCT = XPRODUCT
|
480
522
|
|
481
523
|
# Create a new list containing all the events from the supplied event lists
|
@@ -484,14 +526,14 @@ UNION = (eventLists...) ->
|
|
484
526
|
for eventList in eventLists
|
485
527
|
for event in eventList
|
486
528
|
union.push(event)
|
487
|
-
|
529
|
+
hqmf.SpecificsManager.unionAll(union, eventLists)
|
488
530
|
@UNION = UNION
|
489
531
|
|
490
532
|
# Return true if the number of events matches the supplied range
|
491
533
|
COUNT = (events, range) ->
|
492
534
|
count = events.length
|
493
535
|
result = new Boolean(range.match(count))
|
494
|
-
applySpecificOccurrenceSubset('COUNT',
|
536
|
+
applySpecificOccurrenceSubset('COUNT', hqmf.SpecificsManager.maintainSpecifics(result, events), range)
|
495
537
|
@COUNT = COUNT
|
496
538
|
|
497
539
|
# Convert an hQuery.CodedEntry or JS Date into an IVL_TS
|
@@ -521,7 +563,8 @@ eventAccessor = {
|
|
521
563
|
'SCW': 'low',
|
522
564
|
'ECWS': 'high'
|
523
565
|
'SCWE': 'low',
|
524
|
-
'CONCURRENT': 'low'
|
566
|
+
'CONCURRENT': 'low',
|
567
|
+
'DATEDIFF': 'low'
|
525
568
|
}
|
526
569
|
|
527
570
|
boundAccessor = {
|
@@ -541,7 +584,8 @@ boundAccessor = {
|
|
541
584
|
'SCW': 'low',
|
542
585
|
'ECWS': 'low'
|
543
586
|
'SCWE': 'high',
|
544
|
-
'CONCURRENT': 'low'
|
587
|
+
'CONCURRENT': 'low',
|
588
|
+
'DATEDIFF': 'low'
|
545
589
|
}
|
546
590
|
|
547
591
|
# Determine whether the supplied event falls within range of the supplied bound
|
@@ -563,7 +607,7 @@ eventMatchesBounds = (event, bounds, methodName, range) ->
|
|
563
607
|
currentMatches = eventMatchesBounds(event, boundList, methodName, range)
|
564
608
|
return [] if (currentMatches.length == 0)
|
565
609
|
matchingBounds = matchingBounds.concat(currentMatches)
|
566
|
-
return
|
610
|
+
return hqmf.SpecificsManager.maintainSpecifics(matchingBounds,bounds)
|
567
611
|
else
|
568
612
|
eventIVL = getIVL(event)
|
569
613
|
matchingBounds = (bound for bound in bounds when (
|
@@ -573,7 +617,7 @@ eventMatchesBounds = (event, bounds, methodName, range) ->
|
|
573
617
|
result &&= withinRange(methodName, eventIVL, boundIVL, range)
|
574
618
|
result
|
575
619
|
))
|
576
|
-
|
620
|
+
hqmf.SpecificsManager.maintainSpecifics(matchingBounds, bounds)
|
577
621
|
@eventMatchesBounds = eventMatchesBounds
|
578
622
|
|
579
623
|
# Determine which event match one of the supplied bounds
|
@@ -583,7 +627,7 @@ eventsMatchBounds = (events, bounds, methodName, range) ->
|
|
583
627
|
if (events.length==undefined)
|
584
628
|
events = [events]
|
585
629
|
|
586
|
-
specificContext = new
|
630
|
+
specificContext = new hqmf.SpecificOccurrence()
|
587
631
|
hasSpecificOccurrence = (events.specific_occurrence? || bounds.specific_occurrence?)
|
588
632
|
matchingEvents = []
|
589
633
|
matchingEvents.specific_occurrence = events.specific_occurrence
|
@@ -593,7 +637,7 @@ eventsMatchBounds = (events, bounds, methodName, range) ->
|
|
593
637
|
|
594
638
|
if hasSpecificOccurrence
|
595
639
|
matchingEvents.specific_occurrence = events.specific_occurrence
|
596
|
-
# TODO:
|
640
|
+
# TODO: well need a temporary variable for non specific occurrences on the left so that we can do rejections based on restrictions in the data criteria
|
597
641
|
specificContext.addRows(Row.buildRowsForMatching(events.specific_occurrence, event, bounds.specific_occurrence, matchingBounds))
|
598
642
|
else
|
599
643
|
# add all stars
|
@@ -693,37 +737,37 @@ applySpecificOccurrenceSubset = (operator, result, range, calculateSpecifics) ->
|
|
693
737
|
FIRST = (events) ->
|
694
738
|
result = []
|
695
739
|
result = [events.sort(dateSortAscending)[0]] if (events.length > 0)
|
696
|
-
applySpecificOccurrenceSubset('FIRST',
|
740
|
+
applySpecificOccurrenceSubset('FIRST',hqmf.SpecificsManager.maintainSpecifics(result, events))
|
697
741
|
@FIRST = FIRST
|
698
742
|
|
699
743
|
SECOND = (events) ->
|
700
744
|
result = []
|
701
745
|
result = [events.sort(dateSortAscending)[1]] if (events.length > 1)
|
702
|
-
applySpecificOccurrenceSubset('SECOND',
|
746
|
+
applySpecificOccurrenceSubset('SECOND',hqmf.SpecificsManager.maintainSpecifics(result, events))
|
703
747
|
@SECOND = SECOND
|
704
748
|
|
705
749
|
THIRD = (events) ->
|
706
750
|
result = []
|
707
751
|
result = [events.sort(dateSortAscending)[2]] if (events.length > 2)
|
708
|
-
applySpecificOccurrenceSubset('THIRD',
|
752
|
+
applySpecificOccurrenceSubset('THIRD',hqmf.SpecificsManager.maintainSpecifics(result, events))
|
709
753
|
@THIRD = THIRD
|
710
754
|
|
711
755
|
FOURTH = (events) ->
|
712
756
|
result = []
|
713
757
|
result = [events.sort(dateSortAscending)[3]] if (events.length > 3)
|
714
|
-
applySpecificOccurrenceSubset('FOURTH',
|
758
|
+
applySpecificOccurrenceSubset('FOURTH',hqmf.SpecificsManager.maintainSpecifics(result, events))
|
715
759
|
@FOURTH = FOURTH
|
716
760
|
|
717
761
|
FIFTH = (events) ->
|
718
762
|
result = []
|
719
763
|
result = [events.sort(dateSortAscending)[4]] if (events.length > 4)
|
720
|
-
applySpecificOccurrenceSubset('FIFTH',
|
764
|
+
applySpecificOccurrenceSubset('FIFTH',hqmf.SpecificsManager.maintainSpecifics(result, events))
|
721
765
|
@FIFTH = FIFTH
|
722
766
|
|
723
767
|
RECENT = (events) ->
|
724
768
|
result = []
|
725
769
|
result = [events.sort(dateSortDescending)[0]] if (events.length > 0)
|
726
|
-
applySpecificOccurrenceSubset('RECENT',
|
770
|
+
applySpecificOccurrenceSubset('RECENT',hqmf.SpecificsManager.maintainSpecifics(result, events))
|
727
771
|
@RECENT = RECENT
|
728
772
|
|
729
773
|
LAST = (events) ->
|
@@ -759,7 +803,7 @@ MIN = (events, range) ->
|
|
759
803
|
if (events.length > 0)
|
760
804
|
minValue = events.sort(valueSortAscending)[0].value()["scalar"]
|
761
805
|
result = new Boolean(range.match(minValue))
|
762
|
-
applySpecificOccurrenceSubset('MIN',
|
806
|
+
applySpecificOccurrenceSubset('MIN',hqmf.SpecificsManager.maintainSpecifics(result, events), range)
|
763
807
|
@MIN = MIN
|
764
808
|
|
765
809
|
MAX = (events, range) ->
|
@@ -767,9 +811,16 @@ MAX = (events, range) ->
|
|
767
811
|
if (events.length > 0)
|
768
812
|
maxValue = events.sort(valueSortDescending)[0].value()["scalar"]
|
769
813
|
result = new Boolean(range.match(maxValue))
|
770
|
-
applySpecificOccurrenceSubset('MAX',
|
814
|
+
applySpecificOccurrenceSubset('MAX',hqmf.SpecificsManager.maintainSpecifics(result, events), range)
|
771
815
|
@MAX = MAX
|
772
816
|
|
817
|
+
DATEDIFF = (events, range) ->
|
818
|
+
return hqmf.SpecificsManager.maintainSpecifics(new Boolean(false), events) if events.length < 2
|
819
|
+
throw "cannot calculate against more than 2 events" if events.length > 2
|
820
|
+
hqmf.SpecificsManager.maintainSpecifics(new Boolean(withinRange('DATEDIFF', getIVL(events[0]), getIVL(events[1]), range)), events)
|
821
|
+
@DATEDIFF = DATEDIFF
|
822
|
+
|
823
|
+
|
773
824
|
@OidDictionary = {};
|
774
825
|
|
775
826
|
hqmfjs = hqmfjs||{}
|