hqmf-parser 1.0.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 +23 -0
- data/README.md +903 -0
- data/Rakefile +19 -0
- data/VERSION +1 -0
- data/lib/hqmf-generator/hqmf-generator.rb +308 -0
- data/lib/hqmf-model/attribute.rb +35 -0
- data/lib/hqmf-model/data_criteria.rb +322 -0
- data/lib/hqmf-model/document.rb +172 -0
- data/lib/hqmf-model/population_criteria.rb +90 -0
- data/lib/hqmf-model/precondition.rb +85 -0
- data/lib/hqmf-model/types.rb +318 -0
- data/lib/hqmf-model/utilities.rb +52 -0
- data/lib/hqmf-parser.rb +54 -0
- data/lib/hqmf-parser/1.0/attribute.rb +68 -0
- data/lib/hqmf-parser/1.0/comparison.rb +34 -0
- data/lib/hqmf-parser/1.0/data_criteria.rb +105 -0
- data/lib/hqmf-parser/1.0/document.rb +209 -0
- data/lib/hqmf-parser/1.0/expression.rb +52 -0
- data/lib/hqmf-parser/1.0/population_criteria.rb +79 -0
- data/lib/hqmf-parser/1.0/precondition.rb +89 -0
- data/lib/hqmf-parser/1.0/range.rb +65 -0
- data/lib/hqmf-parser/1.0/restriction.rb +157 -0
- data/lib/hqmf-parser/1.0/utilities.rb +41 -0
- data/lib/hqmf-parser/2.0/data_criteria.rb +319 -0
- data/lib/hqmf-parser/2.0/document.rb +165 -0
- data/lib/hqmf-parser/2.0/population_criteria.rb +53 -0
- data/lib/hqmf-parser/2.0/precondition.rb +44 -0
- data/lib/hqmf-parser/2.0/types.rb +223 -0
- data/lib/hqmf-parser/2.0/utilities.rb +30 -0
- data/lib/hqmf-parser/converter/pass1/data_criteria_converter.rb +254 -0
- data/lib/hqmf-parser/converter/pass1/document_converter.rb +183 -0
- data/lib/hqmf-parser/converter/pass1/population_criteria_converter.rb +135 -0
- data/lib/hqmf-parser/converter/pass1/precondition_converter.rb +164 -0
- data/lib/hqmf-parser/converter/pass1/precondition_extractor.rb +159 -0
- data/lib/hqmf-parser/converter/pass1/simple_data_criteria.rb +35 -0
- data/lib/hqmf-parser/converter/pass1/simple_operator.rb +89 -0
- data/lib/hqmf-parser/converter/pass1/simple_population_criteria.rb +10 -0
- data/lib/hqmf-parser/converter/pass1/simple_precondition.rb +63 -0
- data/lib/hqmf-parser/converter/pass1/simple_restriction.rb +64 -0
- data/lib/hqmf-parser/converter/pass2/comparison_converter.rb +91 -0
- data/lib/hqmf-parser/converter/pass2/operator_converter.rb +169 -0
- data/lib/hqmf-parser/converter/pass3/specific_occurrence_converter.rb +86 -0
- data/lib/hqmf-parser/converter/pass3/specific_occurrence_converter_bak.rb +70 -0
- data/lib/hqmf-parser/parser.rb +22 -0
- data/lib/hqmf-parser/value_sets/value_set_parser.rb +206 -0
- data/lib/tasks/coverme.rake +8 -0
- data/lib/tasks/hqmf.rake +141 -0
- data/lib/tasks/value_sets.rake +23 -0
- metadata +159 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
module HQMF2
|
2
|
+
# Represents an HQMF population criteria, also supports all the same methods as
|
3
|
+
# HQMF2::Precondition
|
4
|
+
class PopulationCriteria
|
5
|
+
|
6
|
+
include HQMF2::Utilities
|
7
|
+
|
8
|
+
attr_reader :preconditions, :id, :hqmf_id, :title, :type
|
9
|
+
|
10
|
+
# Create a new population criteria from the supplied HQMF entry
|
11
|
+
# @param [Nokogiri::XML::Element] the HQMF entry
|
12
|
+
def initialize(entry, doc)
|
13
|
+
@doc = doc
|
14
|
+
@entry = entry
|
15
|
+
@hqmf_id = attr_val('./*/cda:id/@extension')
|
16
|
+
@title = attr_val('./*/cda:code/cda:displayName/@value')
|
17
|
+
@type = attr_val('./*/cda:code/@code')
|
18
|
+
@preconditions = @entry.xpath('./*/cda:precondition[not(@nullFlavor)]', HQMF2::Document::NAMESPACES).collect do |precondition|
|
19
|
+
Precondition.new(precondition, @doc)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_human_readable_id(id)
|
24
|
+
@id = id
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return true of this precondition represents a conjunction with nested preconditions
|
28
|
+
# or false of this precondition is a reference to a data criteria
|
29
|
+
def conjunction?
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get the conjunction code, e.g. allTrue, allFalse
|
34
|
+
# @return [String] conjunction code
|
35
|
+
def conjunction_code
|
36
|
+
case @type
|
37
|
+
when HQMF::PopulationCriteria::IPP, HQMF::PopulationCriteria::DENOM, HQMF::PopulationCriteria::NUMER
|
38
|
+
HQMF::Precondition::ALL_TRUE
|
39
|
+
when HQMF::PopulationCriteria::EXCEP, HQMF::PopulationCriteria::DENEX
|
40
|
+
HQMF::Precondition::AT_LEAST_ONE_TRUE
|
41
|
+
else
|
42
|
+
raise "Unknown population type [#{@type}]"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_model
|
47
|
+
mps = preconditions.collect {|p| p.to_model}
|
48
|
+
HQMF::PopulationCriteria.new(id, hqmf_id, type, mps, title)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module HQMF2
|
2
|
+
|
3
|
+
class Precondition
|
4
|
+
|
5
|
+
include HQMF2::Utilities
|
6
|
+
|
7
|
+
attr_reader :preconditions, :reference
|
8
|
+
|
9
|
+
def initialize(entry, doc)
|
10
|
+
@doc = doc
|
11
|
+
@entry = entry
|
12
|
+
@preconditions = @entry.xpath('./*/cda:precondition', HQMF2::Document::NAMESPACES).collect do |precondition|
|
13
|
+
Precondition.new(precondition, @doc)
|
14
|
+
end
|
15
|
+
reference_def = @entry.at_xpath('./*/cda:id', HQMF2::Document::NAMESPACES)
|
16
|
+
if reference_def
|
17
|
+
@reference = Reference.new(reference_def)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return true of this precondition represents a conjunction with nested preconditions
|
22
|
+
# or false of this precondition is a reference to a data criteria
|
23
|
+
def conjunction?
|
24
|
+
@preconditions.length>0
|
25
|
+
end
|
26
|
+
|
27
|
+
# Get the conjunction code, e.g. allTrue, allFalse
|
28
|
+
# @return [String] conjunction code
|
29
|
+
def conjunction_code
|
30
|
+
if conjunction?
|
31
|
+
@entry.at_xpath('./*[1]', HQMF2::Document::NAMESPACES).name
|
32
|
+
else
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_model
|
38
|
+
pcs = preconditions.collect {|p| p.to_model}
|
39
|
+
mr = reference ? reference.to_model : nil
|
40
|
+
HQMF::Precondition.new(nil, pcs, mr, conjunction_code, false)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
module HQMF2
|
2
|
+
# Used to represent 'any value' in criteria that require a value be present but
|
3
|
+
# don't specify any restrictions on that value
|
4
|
+
class AnyValue
|
5
|
+
attr_reader :type
|
6
|
+
|
7
|
+
def initialize(type='ANYNonNull')
|
8
|
+
@type = type
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_model
|
12
|
+
HQMF::AnyValue.new(@type)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Represents a bound within a HQMF pauseQuantity, has a value, a unit and an
|
17
|
+
# inclusive/exclusive indicator
|
18
|
+
class Value
|
19
|
+
include HQMF2::Utilities
|
20
|
+
|
21
|
+
attr_reader :type, :unit, :value
|
22
|
+
|
23
|
+
def initialize(entry, default_type='PQ')
|
24
|
+
@entry = entry
|
25
|
+
@type = attr_val('./@xsi:type') || default_type
|
26
|
+
@unit = attr_val('./@unit')
|
27
|
+
@value = attr_val('./@value')
|
28
|
+
end
|
29
|
+
|
30
|
+
def inclusive?
|
31
|
+
case attr_val('./@inclusive')
|
32
|
+
when 'false'
|
33
|
+
false
|
34
|
+
else
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def derived?
|
40
|
+
case attr_val('./@nullFlavor')
|
41
|
+
when 'DER'
|
42
|
+
true
|
43
|
+
else
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def expression
|
49
|
+
if !derived?
|
50
|
+
nil
|
51
|
+
else
|
52
|
+
attr_val('./cda:expression/@value')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_model
|
57
|
+
HQMF::Value.new(type,unit,value,inclusive?,derived?,expression)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Represents a HQMF physical quantity which can have low and high bounds
|
62
|
+
class Range
|
63
|
+
include HQMF2::Utilities
|
64
|
+
attr_accessor :low, :high, :width
|
65
|
+
|
66
|
+
def initialize(entry, type=nil)
|
67
|
+
@type = type
|
68
|
+
@entry = entry
|
69
|
+
if @entry
|
70
|
+
@low = optional_value('./cda:low', default_bounds_type)
|
71
|
+
@high = optional_value('./cda:high', default_bounds_type)
|
72
|
+
@width = optional_value('./cda:width', 'PQ')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def type
|
77
|
+
@type || attr_val('./@xsi:type')
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_model
|
81
|
+
lm = low ? low.to_model : nil
|
82
|
+
hm = high ? high.to_model : nil
|
83
|
+
wm = width ? width.to_model : nil
|
84
|
+
HQMF::Range.new(type, lm, hm, wm)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def optional_value(xpath, type)
|
90
|
+
value_def = @entry.at_xpath(xpath, HQMF2::Document::NAMESPACES)
|
91
|
+
if value_def
|
92
|
+
Value.new(value_def, type)
|
93
|
+
else
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def default_bounds_type
|
99
|
+
case type
|
100
|
+
when 'IVL_TS'
|
101
|
+
'TS'
|
102
|
+
else
|
103
|
+
'PQ'
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Represents a HQMF effective time which is a specialization of a interval
|
109
|
+
class EffectiveTime < Range
|
110
|
+
def initialize(entry)
|
111
|
+
super
|
112
|
+
end
|
113
|
+
|
114
|
+
def type
|
115
|
+
'IVL_TS'
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Represents a HQMF CD value which has a code and codeSystem
|
120
|
+
class Coded
|
121
|
+
include HQMF2::Utilities
|
122
|
+
|
123
|
+
def initialize(entry)
|
124
|
+
@entry = entry
|
125
|
+
end
|
126
|
+
|
127
|
+
def type
|
128
|
+
attr_val('./@xsi:type') || 'CD'
|
129
|
+
end
|
130
|
+
|
131
|
+
def system
|
132
|
+
attr_val('./@codeSystem')
|
133
|
+
end
|
134
|
+
|
135
|
+
def code
|
136
|
+
attr_val('./@code')
|
137
|
+
end
|
138
|
+
|
139
|
+
def code_list_id
|
140
|
+
attr_val('./@valueSet')
|
141
|
+
end
|
142
|
+
|
143
|
+
def title
|
144
|
+
attr_val('./@displayName')
|
145
|
+
end
|
146
|
+
|
147
|
+
def value
|
148
|
+
code
|
149
|
+
end
|
150
|
+
|
151
|
+
def derived?
|
152
|
+
false
|
153
|
+
end
|
154
|
+
|
155
|
+
def unit
|
156
|
+
nil
|
157
|
+
end
|
158
|
+
|
159
|
+
def to_model
|
160
|
+
HQMF::Coded.new(type, system, code, code_list_id, title)
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
class SubsetOperator
|
166
|
+
include HQMF2::Utilities
|
167
|
+
|
168
|
+
attr_reader :type, :value
|
169
|
+
|
170
|
+
def initialize(entry)
|
171
|
+
@entry = entry
|
172
|
+
@type = attr_val('./cda:subsetCode/@code')
|
173
|
+
value_def = @entry.at_xpath('./*/cda:repeatNumber', HQMF2::Document::NAMESPACES)
|
174
|
+
if value_def
|
175
|
+
@value = HQMF2::Range.new(value_def, 'IVL_INT')
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def to_model
|
180
|
+
vm = value ? value.to_model : nil
|
181
|
+
HQMF::SubsetOperator.new(type, vm)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
class TemporalReference
|
186
|
+
include HQMF2::Utilities
|
187
|
+
|
188
|
+
attr_reader :type, :reference, :range
|
189
|
+
|
190
|
+
def initialize(entry)
|
191
|
+
@entry = entry
|
192
|
+
@type = attr_val('./@typeCode')
|
193
|
+
@reference = Reference.new(@entry.at_xpath('./*/cda:id', HQMF2::Document::NAMESPACES))
|
194
|
+
range_def = @entry.at_xpath('./cda:pauseQuantity', HQMF2::Document::NAMESPACES)
|
195
|
+
if range_def
|
196
|
+
@range = HQMF2::Range.new(range_def, 'IVL_PQ')
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def to_model
|
201
|
+
rm = range ? range.to_model : nil
|
202
|
+
HQMF::TemporalReference.new(type, reference.to_model, rm)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Represents a HQMF reference from a precondition to a data criteria
|
207
|
+
class Reference
|
208
|
+
include HQMF2::Utilities
|
209
|
+
|
210
|
+
def initialize(entry)
|
211
|
+
@entry = entry
|
212
|
+
end
|
213
|
+
|
214
|
+
def id
|
215
|
+
attr_val('./@extension')
|
216
|
+
end
|
217
|
+
|
218
|
+
def to_model
|
219
|
+
HQMF::Reference.new(id)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module HQMF2
|
2
|
+
module Utilities
|
3
|
+
|
4
|
+
include HQMF::Conversion::Utilities
|
5
|
+
|
6
|
+
# Utility function to handle optional attributes
|
7
|
+
# @param xpath an XPath that identifies an XML attribute
|
8
|
+
# @return the value of the attribute or nil if the attribute is missing
|
9
|
+
def attr_val(xpath)
|
10
|
+
Utilities::attr_val(@entry, xpath)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Utility function to handle optional attributes
|
14
|
+
# @param xpath an XPath that identifies an XML attribute
|
15
|
+
# @return the value of the attribute or nil if the attribute is missing
|
16
|
+
def self.attr_val(node, xpath)
|
17
|
+
attr = node.at_xpath(xpath, HQMF2::Document::NAMESPACES)
|
18
|
+
if attr
|
19
|
+
attr.value
|
20
|
+
else
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_xml
|
26
|
+
@entry.to_xml
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,254 @@
|
|
1
|
+
module HQMF
|
2
|
+
# Class representing an HQMF document
|
3
|
+
class DataCriteriaConverter
|
4
|
+
|
5
|
+
attr_reader :v1_data_criteria_by_id, :v2_data_criteria, :v2_data_criteria_to_delete, :measure_period_criteria, :measure_period_v1_keys, :specific_occurrences
|
6
|
+
|
7
|
+
def initialize(doc, measure_period)
|
8
|
+
@doc = doc
|
9
|
+
@v1_data_criteria_by_id = {}
|
10
|
+
@v2_data_criteria = []
|
11
|
+
@v2_data_criteria_to_delete = {}
|
12
|
+
@specific_occurrences = {}
|
13
|
+
@measure_period = measure_period
|
14
|
+
parse()
|
15
|
+
end
|
16
|
+
|
17
|
+
def final_v2_data_criteria
|
18
|
+
@v2_data_criteria.delete_if {|criteria| @v2_data_criteria_to_delete[criteria.id] }
|
19
|
+
end
|
20
|
+
|
21
|
+
# duplicates a data criteria. This is important because we may be modifying source data criteria like patient characteristic birthdate to add restrictions
|
22
|
+
# the restrictions added may be different for the numerator, denominator, different IPP_1, IPP_2, etc.
|
23
|
+
def duplicate_data_criteria(data_criteria, parent_id)
|
24
|
+
|
25
|
+
# if this is a specific occurrence, then we do not want to duplicate it.
|
26
|
+
# we may need to duplicate it for a population however.
|
27
|
+
# return data_criteria if (specific_occurrences[data_criteria.id])
|
28
|
+
|
29
|
+
if (data_criteria.is_a? HQMF::Converter::SimpleDataCriteria and data_criteria.precondition_id == parent_id)
|
30
|
+
new_data_criteria = data_criteria
|
31
|
+
else
|
32
|
+
new_data_criteria = HQMF::Converter::SimpleDataCriteria.from_data_criteria(data_criteria)
|
33
|
+
new_data_criteria.assign_precondition(parent_id)
|
34
|
+
@v2_data_criteria << new_data_criteria
|
35
|
+
# we want to delete the original for data criteria that have been duplicated
|
36
|
+
@v2_data_criteria_to_delete[data_criteria.id] = true if !@v2_data_criteria_to_delete.keys.include? data_criteria.id
|
37
|
+
end
|
38
|
+
|
39
|
+
new_data_criteria
|
40
|
+
end
|
41
|
+
|
42
|
+
# make sure that if a data criteria is used as a target, that it is not deleted by someone else.
|
43
|
+
# this is required for birthdate in NQF0106
|
44
|
+
def validate_not_deleted(target)
|
45
|
+
@v2_data_criteria_to_delete[target] = false
|
46
|
+
end
|
47
|
+
|
48
|
+
# grouping data criteria are used to allow a single reference off of a temporal reference or subset operator
|
49
|
+
# grouping data criteria can reference either regular data criteria as children, or other grouping data criteria
|
50
|
+
def create_group_data_criteria(preconditions, type, value, parent_id, id, standard_category, qds_data_type)
|
51
|
+
extract_group_data_criteria_tree(HQMF::DataCriteria::UNION,preconditions, type, parent_id)
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_group_data_criteria(children, section, parent_id, derivation_operator)
|
55
|
+
|
56
|
+
criteria_ids = children.map(&:id)
|
57
|
+
# make sure nobody else is going to delete the criteria we've grouped
|
58
|
+
criteria_ids.each {|target| validate_not_deleted(target)}
|
59
|
+
|
60
|
+
id = "#{parent_id}_#{section}_#{@@ids.next}"
|
61
|
+
title = "#{id}"
|
62
|
+
description = ""
|
63
|
+
definition = 'derived'
|
64
|
+
_display_name,_code_list_id,_status,_value,_field_values,_effective_time,_inline_code_list,_negation_code_list_id, = nil
|
65
|
+
_negation = false
|
66
|
+
|
67
|
+
group_criteria = HQMF::DataCriteria.new(id, title, _display_name, description, _code_list_id, criteria_ids, derivation_operator, definition, _status,
|
68
|
+
_value, _field_values, _effective_time, _inline_code_list,_negation,_negation_code_list_id,nil,nil,nil,nil)
|
69
|
+
|
70
|
+
@v2_data_criteria << group_criteria
|
71
|
+
|
72
|
+
group_criteria
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
# pull the children data criteria out of a set of preconditions
|
77
|
+
def extract_group_data_criteria_tree(conjunction, preconditions, type, parent_id)
|
78
|
+
|
79
|
+
children = []
|
80
|
+
preconditions.each do |precondition|
|
81
|
+
if (precondition.comparison?)
|
82
|
+
if (precondition.reference.id == HQMF::Document::MEASURE_PERIOD_ID)
|
83
|
+
children << measure_period_criteria
|
84
|
+
else
|
85
|
+
children << v2_data_criteria_by_id[precondition.reference.id]
|
86
|
+
end
|
87
|
+
else
|
88
|
+
converted_conjunction = convert_grouping_conjunction(precondition.conjunction_code)
|
89
|
+
children << extract_group_data_criteria_tree(converted_conjunction, precondition.preconditions, type, parent_id)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# if we have just one child element, just return it. An AND or OR of a single item is not useful.
|
94
|
+
if (children.size > 1)
|
95
|
+
build_group_data_criteria(children, type, parent_id, conjunction)
|
96
|
+
else
|
97
|
+
children.first
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
def convert_grouping_conjunction(conjunction)
|
103
|
+
case conjunction
|
104
|
+
when HQMF::Precondition::AT_LEAST_ONE_TRUE
|
105
|
+
HQMF::DataCriteria::UNION
|
106
|
+
when HQMF::Precondition::ALL_TRUE
|
107
|
+
HQMF::DataCriteria::XPRODUCT
|
108
|
+
else
|
109
|
+
'unknown'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# pull the children data criteria out of a set of preconditions
|
114
|
+
def self.extract_data_criteria(preconditions, data_criteria_converter)
|
115
|
+
flattened = []
|
116
|
+
preconditions.each do |precondition|
|
117
|
+
if (precondition.comparison?)
|
118
|
+
if (precondition.reference.id == HQMF::Document::MEASURE_PERIOD_ID)
|
119
|
+
flattened << data_criteria_converter.measure_period_criteria
|
120
|
+
else
|
121
|
+
flattened << data_criteria_converter.v2_data_criteria_by_id[precondition.reference.id]
|
122
|
+
end
|
123
|
+
else
|
124
|
+
flattened.concat(extract_data_criteria(precondition.preconditions,data_criteria_converter))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
flattened
|
128
|
+
end
|
129
|
+
|
130
|
+
def v2_data_criteria_by_id
|
131
|
+
criteria_by_id = {}
|
132
|
+
@v2_data_criteria.each do |criteria|
|
133
|
+
criteria_by_id[criteria.id] = criteria
|
134
|
+
end
|
135
|
+
criteria_by_id
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def parse()
|
141
|
+
@doc[:data_criteria].each do |key,criteria|
|
142
|
+
parsed_criteria = HQMF::DataCriteriaConverter.convert(key, criteria)
|
143
|
+
@v2_data_criteria << parsed_criteria
|
144
|
+
@v1_data_criteria_by_id[criteria[:id]] = parsed_criteria
|
145
|
+
@specific_occurrences[parsed_criteria.id] = criteria[:derived_from] != nil
|
146
|
+
end
|
147
|
+
create_measure_period_v1_data_criteria(@doc,@measure_period,@v1_data_criteria_by_id)
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.convert(key, criteria)
|
151
|
+
|
152
|
+
# @param [String] id
|
153
|
+
# @param [String] title
|
154
|
+
# @param [String] standard_category
|
155
|
+
# @param [String] qds_data_type
|
156
|
+
# @param [String] subset_code
|
157
|
+
# @param [String] code_list_id
|
158
|
+
# @param [String] property
|
159
|
+
# @param [String] type
|
160
|
+
# @param [String] status
|
161
|
+
# @param [boolean] negation
|
162
|
+
# @param [String] negation_code_list_id
|
163
|
+
# @param [Value|Range|Coded] value
|
164
|
+
# @param [Range] effective_time
|
165
|
+
# @param [Hash<String,String>] inline_code_list
|
166
|
+
|
167
|
+
id = convert_key(key)
|
168
|
+
title = criteria[:title]
|
169
|
+
title = title.match(/.*:\s+(.+)/)[1]
|
170
|
+
description = criteria[:description]
|
171
|
+
code_list_id = criteria[:code_list_id]
|
172
|
+
definition = criteria[:definition]
|
173
|
+
status = criteria[:status]
|
174
|
+
negation = criteria[:negation]
|
175
|
+
negation_code_list_id = criteria[:negation_code_list_id]
|
176
|
+
specific_occurrence = criteria[:specific_occurrence]
|
177
|
+
specific_occurrence_const = criteria[:specific_occurrence_const]
|
178
|
+
|
179
|
+
value = nil # value is filled out by backfill_patient_characteristics for things like gender and by REFR restrictions
|
180
|
+
effective_time = nil # filled out by temporal reference code
|
181
|
+
temporal_references = # filled out by operator code
|
182
|
+
subset_operators = nil # filled out by operator code
|
183
|
+
children_criteria = nil # filled out by operator and temporal reference code
|
184
|
+
derivation_operator = nil # filled out by operator and temporal reference code
|
185
|
+
negation_code_list_id = nil # filled out by RSON restrictions
|
186
|
+
field_values = nil # field values are filled out by SUBJ and REFR restrictions
|
187
|
+
inline_code_list = nil # inline code list is only used in HQMF V2, so we can just pass in nil
|
188
|
+
display_name=nil
|
189
|
+
|
190
|
+
HQMF::DataCriteria.new(id, title, display_name, description, code_list_id, children_criteria, derivation_operator, definition, status,
|
191
|
+
value, field_values, effective_time, inline_code_list, negation, negation_code_list_id, temporal_references, subset_operators,specific_occurrence,specific_occurrence_const)
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
# this method creates V1 data criteria for the measurement period. These data criteria can be
|
197
|
+
# referenced properly within the restrictions
|
198
|
+
def create_measure_period_v1_data_criteria(doc,measure_period,v1_data_criteria_by_id)
|
199
|
+
|
200
|
+
attributes = doc[:attributes]
|
201
|
+
attributes.keys.each {|key| attributes[key.to_s] = attributes[key]}
|
202
|
+
|
203
|
+
measure_period_key = attributes['MEASUREMENT_PERIOD'][:id]
|
204
|
+
measure_start_key = attributes['MEASUREMENT_START_DATE'][:id]
|
205
|
+
measure_end_key = attributes['MEASUREMENT_END_DATE'][:id]
|
206
|
+
|
207
|
+
@measure_period_v1_keys = {measure_start: measure_start_key, measure_end: measure_end_key, measure_period: measure_period_key}
|
208
|
+
|
209
|
+
type = 'variable'
|
210
|
+
code_list_id,negation_code_list_id,property,status,field_values,effective_time,inline_code_list,children_criteria,derivation_operator,temporal_references,subset_operators=nil
|
211
|
+
|
212
|
+
#####
|
213
|
+
##
|
214
|
+
######### SET MEASURE PERIOD
|
215
|
+
##
|
216
|
+
#####
|
217
|
+
|
218
|
+
measure_period_id = HQMF::Document::MEASURE_PERIOD_ID
|
219
|
+
value = measure_period
|
220
|
+
measure_criteria = HQMF::DataCriteria.new(measure_period_id,measure_period_id,nil,measure_period_id,code_list_id,children_criteria,derivation_operator,measure_period_id,status,
|
221
|
+
value,field_values,effective_time,inline_code_list, false, nil, temporal_references,subset_operators,nil,nil)
|
222
|
+
|
223
|
+
# set the measure period data criteria for all measure period keys
|
224
|
+
v1_data_criteria_by_id[measure_period_key] = measure_criteria
|
225
|
+
v1_data_criteria_by_id[measure_start_key] = measure_criteria
|
226
|
+
v1_data_criteria_by_id[measure_end_key] = measure_criteria
|
227
|
+
@measure_period_criteria = measure_criteria
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
def self.title_from_description(title, description)
|
233
|
+
title.gsub(/^#{Regexp.escape(description).gsub('\\ ',':?,?\\ ')}:\s*/i,'')
|
234
|
+
end
|
235
|
+
|
236
|
+
def self.convert_key(key)
|
237
|
+
key.to_s.downcase.gsub('_', ' ').split(' ').map {|w| w.capitalize }.join('')
|
238
|
+
end
|
239
|
+
|
240
|
+
# Simple class to issue monotonically increasing integer identifiers
|
241
|
+
class Counter
|
242
|
+
def initialize
|
243
|
+
@count = 0
|
244
|
+
end
|
245
|
+
|
246
|
+
def next
|
247
|
+
@count+=1
|
248
|
+
end
|
249
|
+
end
|
250
|
+
@@ids = Counter.new
|
251
|
+
|
252
|
+
|
253
|
+
end
|
254
|
+
end
|