This repository has been archived by the owner on Nov 20, 2020. It is now read-only.
/
types.rb
257 lines (211 loc) · 6.77 KB
/
types.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
module SimpleXml
# Used to represent 'any value' in criteria that require a value be present but
# don't specify any restrictions on that value
class AnyValue
attr_reader :type
def initialize(type='ANYNonNull')
@type = type
end
def to_model
HQMF::AnyValue.new(@type)
end
end
# Represents a bound within a HQMF pauseQuantity, has a value, a unit and an
# inclusive/exclusive indicator
class Value
include SimpleXml::Utilities
TIME_UNIT_TRANSLATION = { years: 'a', months: 'mo', weeks: 'wk', days: 'd', hours: 'h', minutes: 'min', seconds: 's'}
OTHER_UNIT_TRANSLATION = { 'mmHg' => 'mm[Hg]', 'per mm3' => '/mm3', 'copies/mL' => '[copies]/mL', 'bpm' => '{H.B}/min'}
attr_reader :type, :unit, :value
def initialize(quantity, unit, inclusive, type='PQ')
@type = type
@value = quantity
@inclusive = inclusive
@unit = translate_unit(unit)
end
def derived?
false
end
def inclusive?
@inclusive
end
def expression
nil
end
def to_model
HQMF::Value.new(type,unit,value,inclusive?,derived?,expression)
end
def translate_unit(unit)
unit = (TIME_UNIT_TRANSLATION[unit.downcase.to_sym] || unit) if unit
unit = (OTHER_UNIT_TRANSLATION[unit] || unit) if unit
unit
end
end
# Represents a HQMF physical quantity which can have low and high bounds
class Range
include SimpleXml::Utilities
attr_accessor :type, :low, :high, :width
def initialize(comparison, quantity, unit, type=nil)
@type = type
set_optional_value(comparison, quantity, unit)
end
def to_model
lm = low ? low.to_model : nil
hm = high ? high.to_model : nil
HQMF::Range.new(type, lm, hm, nil)
end
private
def set_optional_value(comparison, quantity, unit)
comparison_data = translate_comparison(comparison)
value = Value.new(quantity, unit, comparison_data[:inclusive], default_bounds_type)
case comparison_data[:high_or_low]
when :high
@high = value
when :low
@low = value
when :both
@high = value
@low = value
end
end
def translate_comparison(comparison)
case comparison.downcase
when 'less than or equal to'
{high_or_low: :high, inclusive: true}
when 'less than'
{high_or_low: :high, inclusive: false}
when 'greater than or equal to'
{high_or_low: :low, inclusive: true}
when 'greater than'
{high_or_low: :low, inclusive: false}
when 'equal to'
{high_or_low: :both, inclusive: true}
else
raise "unknown mode for attribute: #{comparison}"
end
end
def default_bounds_type
case type
when 'IVL_TS'
'TS'
else
'PQ'
end
end
end
# Represents a HQMF CD value which has a code and codeSystem
class Coded
include SimpleXml::Utilities
attr_reader :code_list_id, :title
def initialize(code_list_id, title)
@code_list_id = code_list_id
@title = title
end
def to_model
HQMF::Coded.for_code_list(code_list_id, title)
end
end
class SubsetOperator
include SimpleXml::Utilities
attr_reader :type, :value
def initialize(type, value = nil)
@type = translate_type(type)
@value = value
end
def translate_type(type)
type = 'RECENT' if type == 'MOST RECENT'
type = 'MEAN' if type == 'AVG'
raise "unknown subset operator type #{type}" unless HQMF::SubsetOperator::TYPES.include? type
type
end
def to_model
vm = value ? value.to_model : nil
HQMF::SubsetOperator.new(type, vm)
end
end
class TemporalReference
include SimpleXml::Utilities
attr_accessor :type
attr_reader :reference, :range
def initialize(type, comparison, quantity, unit, reference)
@type = translate_type(type)
@range = SimpleXml::Range.new(comparison, quantity, unit, 'IVL_PQ') if comparison
@reference = Reference.new(reference.id)
end
def translate_type(type)
# todo: we now have SBDU
type = 'SBE' if type == 'SBOD'
type = 'EBE' if type == 'EBOD'
# the above two lines are required for legacy tests
type = 'EACW' if type == 'EAOCW' || type == 'EAEORECW'
type = 'EACWS' if type == 'EAOCWSO' || type == 'EASORECWS'
type = 'EBCW' if type == 'EBOCW' || type == 'EBEORECW'
type = 'EBCWS' if type == 'EBOCWSO' || type == 'EBSORECWS'
type = 'ECWS' if type == 'ECWSO'
type = 'SACWE' if type == 'SAOCWEO' || type == 'SAEORSCWE'
type = 'SACW' if type == 'SAOCW' || type == 'SASORSCW'
type = 'SBCWE' if type == 'SBOCWEO' || type == 'SBEORSCWE'
type = 'SBCW' if type == 'SBOCW' || type == 'SBSORSCW'
type = 'SCWE' if type == 'SCWEO'
type = 'OVERLAP' if type == 'Overlaps' || type == 'Overlap'
raise "unknown temporal reference type #{type}" unless HQMF::TemporalReference::TYPES.include? type
type
end
def to_model
rm = range ? range.to_model : nil
HQMF::TemporalReference.new(type, reference.to_model, rm)
end
end
# Represents a HQMF reference from a precondition to a data criteria
class Reference
include SimpleXml::Utilities
attr_accessor :id
def initialize(id)
@id = id
end
def to_model
HQMF::Reference.new(@id)
end
end
# Represents a HQMF reference from a precondition to a data criteria
class TypedReference
include SimpleXml::Utilities
attr_accessor :id,:type,:mood
def initialize(id,type,mood)
@id = id
@type = type
@mood = mood
end
def to_model
HQMF::TypedReference.new(@id,@type,@mood)
end
end
class Attribute
attr_reader :id, :code_list_id, :title
def initialize(id, code_list_id, title)
@id = id
@code_list_id = code_list_id
@title = title
end
def self.translate_attribute(attribute, doc)
mode = attribute.at_xpath('@mode').value
case mode
when 'Value Set'
attribute_entry = doc.attribute_map[attribute.at_xpath('@qdmUUID').value]
Coded.new(attribute_entry.code_list_id,attribute_entry.title)
when 'Check if Present'
AnyValue.new
else
if attribute.at_xpath('@attrDate')
date = attribute.at_xpath('@attrDate').value
unit = attribute.at_xpath('@unit').try(:value) # should this be the comparison granularity?
Utilities.build_value(mode, date, unit, 'IVL_TS')
else
quantity = attribute.at_xpath('@comparisonValue').value
unit = attribute.at_xpath('@unit').try(:value)
Utilities.build_value(mode, quantity, unit)
end
end
end
end
end