cqm-parsers 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +29 -0
- data/README.md +21 -0
- data/Rakefile +19 -0
- data/lib/ext/code.rb +10 -0
- data/lib/ext/data_element.rb +24 -0
- data/lib/hqmf-model/attribute.rb +63 -0
- data/lib/hqmf-model/data_criteria.rb +467 -0
- data/lib/hqmf-model/document.rb +253 -0
- data/lib/hqmf-model/population_criteria.rb +102 -0
- data/lib/hqmf-model/precondition.rb +94 -0
- data/lib/hqmf-model/types.rb +457 -0
- data/lib/hqmf-model/utilities.rb +52 -0
- data/lib/hqmf-parser.rb +116 -0
- data/lib/hqmf-parser/1.0/attribute.rb +121 -0
- data/lib/hqmf-parser/1.0/comparison.rb +34 -0
- data/lib/hqmf-parser/1.0/data_criteria.rb +92 -0
- data/lib/hqmf-parser/1.0/document.rb +195 -0
- data/lib/hqmf-parser/1.0/expression.rb +60 -0
- data/lib/hqmf-parser/1.0/observation.rb +61 -0
- data/lib/hqmf-parser/1.0/population_criteria.rb +75 -0
- data/lib/hqmf-parser/1.0/precondition.rb +90 -0
- data/lib/hqmf-parser/1.0/range.rb +76 -0
- data/lib/hqmf-parser/1.0/restriction.rb +162 -0
- data/lib/hqmf-parser/1.0/utilities.rb +55 -0
- data/lib/hqmf-parser/2.0/data_criteria.rb +372 -0
- data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_base_extract.rb +80 -0
- data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_definition_from_template_or_type_extract.rb +201 -0
- data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_post_processing.rb +85 -0
- data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_specific_occurrences_and_source_data_criteria_extract.rb +117 -0
- data/lib/hqmf-parser/2.0/document.rb +304 -0
- data/lib/hqmf-parser/2.0/document_helpers/doc_population_helper.rb +173 -0
- data/lib/hqmf-parser/2.0/document_helpers/doc_utilities.rb +131 -0
- data/lib/hqmf-parser/2.0/field_value_helper.rb +251 -0
- data/lib/hqmf-parser/2.0/population_criteria.rb +134 -0
- data/lib/hqmf-parser/2.0/precondition.rb +73 -0
- data/lib/hqmf-parser/2.0/source_data_criteria_helper.rb +112 -0
- data/lib/hqmf-parser/2.0/types.rb +448 -0
- data/lib/hqmf-parser/2.0/utilities.rb +45 -0
- data/lib/hqmf-parser/2.0/value_set_helper.rb +104 -0
- data/lib/hqmf-parser/converter/pass1/data_criteria_converter.rb +257 -0
- data/lib/hqmf-parser/converter/pass1/document_converter.rb +133 -0
- data/lib/hqmf-parser/converter/pass1/population_criteria_converter.rb +185 -0
- data/lib/hqmf-parser/converter/pass1/precondition_converter.rb +173 -0
- data/lib/hqmf-parser/converter/pass1/precondition_extractor.rb +201 -0
- data/lib/hqmf-parser/converter/pass1/simple_data_criteria.rb +26 -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 +51 -0
- data/lib/hqmf-parser/converter/pass1/simple_restriction.rb +64 -0
- data/lib/hqmf-parser/converter/pass2/comparison_converter.rb +112 -0
- data/lib/hqmf-parser/converter/pass2/operator_converter.rb +102 -0
- data/lib/hqmf-parser/cql/data_criteria.rb +57 -0
- data/lib/hqmf-parser/cql/data_criteria_helpers/dc_definition_from_template_or_type_extract.rb +79 -0
- data/lib/hqmf-parser/cql/data_criteria_helpers/dc_post_processing.rb +43 -0
- data/lib/hqmf-parser/cql/document.rb +78 -0
- data/lib/hqmf-parser/cql/document_helpers/doc_population_helper.rb +124 -0
- data/lib/hqmf-parser/cql/value_set_helper.rb +103 -0
- data/lib/hqmf-parser/parser.rb +100 -0
- data/lib/qrda-export/catI-r5/qrda1_r5.rb +125 -0
- data/lib/qrda-export/helper/cat_1_view_helper.rb +142 -0
- data/lib/qrda-export/helper/code_system_helper.rb +77 -0
- data/lib/qrda-export/helper/date_helper.rb +81 -0
- data/lib/qrda-import/base-importers/demographics_importer.rb +47 -0
- data/lib/qrda-import/base-importers/medication_importer.rb +22 -0
- data/lib/qrda-import/base-importers/section_importer.rb +196 -0
- data/lib/qrda-import/cda_identifier.rb +19 -0
- data/lib/qrda-import/data-element-importers/adverse_event_importer.rb +23 -0
- data/lib/qrda-import/data-element-importers/allergy_intolerance_importer.rb +21 -0
- data/lib/qrda-import/data-element-importers/assessment_performed_importer.rb +23 -0
- data/lib/qrda-import/data-element-importers/communication_from_patient_to_provider_importer.rb +18 -0
- data/lib/qrda-import/data-element-importers/communication_from_provider_to_patient_importer.rb +18 -0
- data/lib/qrda-import/data-element-importers/communication_from_provider_to_provider_importer.rb +20 -0
- data/lib/qrda-import/data-element-importers/device_applied_importer.rb +23 -0
- data/lib/qrda-import/data-element-importers/device_order_importer.rb +18 -0
- data/lib/qrda-import/data-element-importers/diagnosis_importer.rb +23 -0
- data/lib/qrda-import/data-element-importers/diagnostic_study_order_importer.rb +20 -0
- data/lib/qrda-import/data-element-importers/diagnostic_study_performed_importer.rb +30 -0
- data/lib/qrda-import/data-element-importers/encounter_order_importer.rb +20 -0
- data/lib/qrda-import/data-element-importers/encounter_performed_importer.rb +41 -0
- data/lib/qrda-import/data-element-importers/immunization_administered_importer.rb +18 -0
- data/lib/qrda-import/data-element-importers/intervention_order_importer.rb +18 -0
- data/lib/qrda-import/data-element-importers/intervention_performed_importer.rb +22 -0
- data/lib/qrda-import/data-element-importers/laboratory_test_order_importer.rb +20 -0
- data/lib/qrda-import/data-element-importers/laboratory_test_performed_importer.rb +28 -0
- data/lib/qrda-import/data-element-importers/medication_active_importer.rb +17 -0
- data/lib/qrda-import/data-element-importers/medication_administered_importer.rb +17 -0
- data/lib/qrda-import/data-element-importers/medication_discharge_importer.rb +19 -0
- data/lib/qrda-import/data-element-importers/medication_dispensed_importer.rb +19 -0
- data/lib/qrda-import/data-element-importers/medication_order_importer.rb +16 -0
- data/lib/qrda-import/data-element-importers/patient_characteristic_expired.rb +21 -0
- data/lib/qrda-import/data-element-importers/physical_exam_performed_importer.rb +26 -0
- data/lib/qrda-import/data-element-importers/procedure_order_importer.rb +26 -0
- data/lib/qrda-import/data-element-importers/procedure_performed_importer.rb +34 -0
- data/lib/qrda-import/data-element-importers/substance_administered_importer.rb +16 -0
- data/lib/qrda-import/entry_finder.rb +20 -0
- data/lib/qrda-import/entry_package.rb +16 -0
- data/lib/qrda-import/narrative_reference_handler.rb +33 -0
- data/lib/qrda-import/patient_importer.rb +105 -0
- data/lib/util/code_system_helper.rb +76 -0
- data/lib/util/counter.rb +20 -0
- data/lib/util/hqmf_template_helper.rb +39 -0
- metadata +340 -0
@@ -0,0 +1,134 @@
|
|
1
|
+
module HQMF2
|
2
|
+
# Represents an HQMF population criteria, also supports all the same methods as
|
3
|
+
# HQMF2::Precondition
|
4
|
+
class PopulationCriteria
|
5
|
+
include HQMF2::Utilities
|
6
|
+
|
7
|
+
attr_reader :preconditions, :id, :hqmf_id, :title, :aggregator, :comments
|
8
|
+
# need to do this to allow for setting the type to OBSERV for
|
9
|
+
attr_accessor :type
|
10
|
+
# Create a new population criteria from the supplied HQMF entry
|
11
|
+
# @param [Nokogiri::XML::Element] the HQMF entry
|
12
|
+
def initialize(entry, doc, id_generator)
|
13
|
+
@id_generator = id_generator
|
14
|
+
@doc = doc
|
15
|
+
@entry = entry
|
16
|
+
setup_derived_entry_elements(id_generator)
|
17
|
+
# modify type to meet current expected population names
|
18
|
+
@type = 'IPP' if @type == 'IPOP' || @type == 'IPPOP'
|
19
|
+
@comments = nil if comments.empty?
|
20
|
+
# MEAN is handled in current code. Changed since it should have the same effect
|
21
|
+
@aggregator = 'MEAN' if @aggregator == 'AVERAGE'
|
22
|
+
@hqmf_id = @type unless @hqmf_id # The id extension is not required, if it's not provided use the code
|
23
|
+
handle_type(id_generator)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Handles how the code should deal with the type definition (aggregate vs non-aggregate)
|
27
|
+
def handle_type(id_generator)
|
28
|
+
if @type != 'AGGREGATE'
|
29
|
+
# Generate the precondition for this population
|
30
|
+
if @preconditions.length > 1 ||
|
31
|
+
(@preconditions.length == 1 && @preconditions[0].conjunction != conjunction_code)
|
32
|
+
@preconditions = [Precondition.new(id_generator.next_id, conjunction_code, @preconditions)]
|
33
|
+
end
|
34
|
+
else
|
35
|
+
# Extract the data criteria this population references
|
36
|
+
dc = handle_observation_criteria
|
37
|
+
@preconditions = [Precondition.new(id_generator.next_id, nil, nil, false, HQMF2::Reference.new(dc.id))] if dc
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Handles extracting elements from the entry
|
42
|
+
def setup_derived_entry_elements(id_generator)
|
43
|
+
@hqmf_id = attr_val('./*/cda:id/@root') || attr_val('./*/cda:typeId/@extension')
|
44
|
+
@title = attr_val('./*/cda:code/cda:displayName/@value').try(:titleize)
|
45
|
+
@type = attr_val('./*/cda:code/@code')
|
46
|
+
@comments = @entry.xpath('./*/cda:text/cda:xml/cda:qdmUserComments/cda:item/text()', HQMF2::Document::NAMESPACES)
|
47
|
+
.map(&:content)
|
48
|
+
handle_preconditions(id_generator)
|
49
|
+
obs_test = attr_val('./cda:measureObservationDefinition/@classCode')
|
50
|
+
# If there are no measure observations, or there is a title, then there are no aggregations to extract
|
51
|
+
return unless !@title && obs_test.to_s == 'OBS'
|
52
|
+
@title = attr_val('../cda:code/cda:displayName/@value')
|
53
|
+
@aggregator = attr_val('./cda:measureObservationDefinition/cda:methodCode/cda:item/@code')
|
54
|
+
end
|
55
|
+
|
56
|
+
# specifically handles extracting the preconditions for the population criteria
|
57
|
+
def handle_preconditions(id_generator)
|
58
|
+
# Nest multiple preconditions under a single root precondition
|
59
|
+
@preconditions = @entry.xpath('./*/cda:precondition[not(@nullFlavor)]', HQMF2::Document::NAMESPACES)
|
60
|
+
.collect do |pre|
|
61
|
+
precondition = Precondition.parse(pre, @doc, id_generator)
|
62
|
+
precondition.reference.nil? && precondition.preconditions.empty? ? nil : precondition
|
63
|
+
end
|
64
|
+
# Remove uneeded nils from the array
|
65
|
+
@preconditions.compact!
|
66
|
+
end
|
67
|
+
|
68
|
+
# extracts out any measure observation definitons, creating from them the proper criteria to generate a precondition
|
69
|
+
def handle_observation_criteria
|
70
|
+
exp = @entry.at_xpath('./cda:measureObservationDefinition/cda:value/cda:expression/@value',
|
71
|
+
HQMF2::Document::NAMESPACES)
|
72
|
+
# Measure Observations criteria rely on computed expressions. If it doesn't have one,
|
73
|
+
# then it is likely formatted improperly.
|
74
|
+
fail 'Measure Observations criteria is missing computed expression(s) ' if exp.nil?
|
75
|
+
parts = exp.to_s.split('-')
|
76
|
+
dc = parse_parts_to_dc(parts)
|
77
|
+
@doc.add_data_criteria(dc)
|
78
|
+
# Update reference_ids with any newly referenced data criteria
|
79
|
+
dc.children_criteria.each { |cc| @doc.add_reference_id(cc) } unless dc&.children_criteria.nil?
|
80
|
+
dc
|
81
|
+
end
|
82
|
+
|
83
|
+
# generates the value given in an expression based on the number of criteria it references.
|
84
|
+
def parse_parts_to_dc(parts)
|
85
|
+
case parts.length
|
86
|
+
when 1
|
87
|
+
# If there is only one part, it is a reference to an existing data criteria's value
|
88
|
+
@doc.find_criteria_by_lvn(parts.first.strip.split('.')[0])
|
89
|
+
when 2
|
90
|
+
# If there are two parts, there is a computation performed, specifically time difference, on the two criteria
|
91
|
+
children = parts.collect { |p| @doc.find_criteria_by_lvn(p.strip.split('.')[0]).id }
|
92
|
+
id = "GROUP_TIMEDIFF_#{@id_generator.next_id}"
|
93
|
+
HQMF2::DataCriteriaWrapper.new(id: id,
|
94
|
+
title: id,
|
95
|
+
subset_operators: [HQMF::SubsetOperator.new('DATETIMEDIFF', nil)],
|
96
|
+
children_criteria: children,
|
97
|
+
derivation_operator: HQMF::DataCriteria::XPRODUCT,
|
98
|
+
type: 'derived',
|
99
|
+
definition: 'derived',
|
100
|
+
negation: false,
|
101
|
+
source_data_criteria: id
|
102
|
+
)
|
103
|
+
else
|
104
|
+
# If there are neither one or 2 parts, the code should fail
|
105
|
+
fail "No defined extraction method to handle #{parts.length} parts"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def create_human_readable_id(id)
|
110
|
+
@id = id
|
111
|
+
end
|
112
|
+
|
113
|
+
# Get the conjunction code, ALL_TRUE or AT_LEAST_ONE_TRUE
|
114
|
+
# @return [String] conjunction code
|
115
|
+
def conjunction_code
|
116
|
+
case @type
|
117
|
+
when HQMF::PopulationCriteria::IPP, HQMF::PopulationCriteria::DENOM, HQMF::PopulationCriteria::NUMER,
|
118
|
+
HQMF::PopulationCriteria::MSRPOPL, HQMF::PopulationCriteria::STRAT
|
119
|
+
HQMF::Precondition::ALL_TRUE
|
120
|
+
when HQMF::PopulationCriteria::DENEXCEP, HQMF::PopulationCriteria::DENEX, HQMF::PopulationCriteria::MSRPOPLEX,
|
121
|
+
HQMF::PopulationCriteria::NUMEX
|
122
|
+
HQMF::Precondition::AT_LEAST_ONE_TRUE
|
123
|
+
else
|
124
|
+
fail "Unknown population type [#{@type}]"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Generates this classes hqmf-model equivalent
|
129
|
+
def to_model
|
130
|
+
mps = preconditions.collect(&:to_model)
|
131
|
+
HQMF::PopulationCriteria.new(id, hqmf_id, type, mps, title, aggregator, comments)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module HQMF2
|
2
|
+
# Represents the logic that defines grouping of criteria and actions done on it.
|
3
|
+
class Precondition
|
4
|
+
include HQMF2::Utilities
|
5
|
+
|
6
|
+
attr_reader :preconditions, :reference, :conjunction, :id
|
7
|
+
|
8
|
+
def self.parse(entry, doc, id_generator)
|
9
|
+
doc = doc
|
10
|
+
entry = entry
|
11
|
+
aggregation = entry.at_xpath('./cda:allTrue | ./cda:atLeastOneTrue | ./cda:allFalse | ./cda:atLeastOneFalse',
|
12
|
+
HQMF2::Document::NAMESPACES)
|
13
|
+
|
14
|
+
# Sets the reference criteria for the precondition (if it exists)
|
15
|
+
reference_def = entry.at_xpath('./*/cda:id', HQMF2::Document::NAMESPACES)
|
16
|
+
reference_def ||= entry.at_xpath('./cda:join/cda:templateId/cda:item', HQMF2::Document::NAMESPACES)
|
17
|
+
reference = Reference.new(reference_def) if reference_def
|
18
|
+
|
19
|
+
# Unless there is an aggregator, no further actions are necessary.
|
20
|
+
return new(id_generator.next_id, nil, [], false, reference) unless aggregation
|
21
|
+
|
22
|
+
precondition_entries = entry.xpath('./*/cda:precondition', HQMF2::Document::NAMESPACES)
|
23
|
+
preconditions = precondition_entries.collect do |precondition|
|
24
|
+
precondition = Precondition.parse(precondition, doc, id_generator)
|
25
|
+
# There are cases where a precondition may contain no references or preconditions, and should be ignored.
|
26
|
+
precondition.reference.nil? && precondition.preconditions.empty? ? nil : precondition
|
27
|
+
end
|
28
|
+
preconditions.compact!
|
29
|
+
handle_aggregation(id_generator, reference, preconditions, aggregation)
|
30
|
+
end
|
31
|
+
|
32
|
+
# "False" aggregators exist, and require special handling, so this manages that and returns the
|
33
|
+
# proper precondition.
|
34
|
+
def self.handle_aggregation(id_generator, reference, preconditions, aggregation, conjunction = nil)
|
35
|
+
negation = false
|
36
|
+
conjunction = aggregation.name
|
37
|
+
case conjunction
|
38
|
+
# DeMorgan's law is used to handle negated caes: e.g. to find if all are false, negate the "at least one true"
|
39
|
+
# check.
|
40
|
+
when 'allFalse'
|
41
|
+
negation = true
|
42
|
+
conjunction = 'atLeastOneTrue'
|
43
|
+
when 'atLeastOneFalse'
|
44
|
+
negation = true
|
45
|
+
conjunction = 'allTrue'
|
46
|
+
end
|
47
|
+
# Return the proper precondition given if a negation exists
|
48
|
+
if negation
|
49
|
+
# Wrap the negation in a seperate precondition which this will reference
|
50
|
+
precondition_wrapper = new(id_generator.next_id, conjunction, preconditions, true, reference)
|
51
|
+
new(id_generator.next_id, conjunction, [precondition_wrapper])
|
52
|
+
else
|
53
|
+
new(id_generator.next_id, conjunction, preconditions, false, reference)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(id, conjunction, preconditions = [], negation = false, reference = nil)
|
58
|
+
@preconditions = preconditions || []
|
59
|
+
@conjunction = conjunction
|
60
|
+
@reference = reference
|
61
|
+
@negation = negation
|
62
|
+
@id = id
|
63
|
+
end
|
64
|
+
|
65
|
+
# Generates this classes hqmf-model equivalent
|
66
|
+
def to_model
|
67
|
+
pcs = @preconditions.collect(&:to_model)
|
68
|
+
mr = @reference ? @reference.to_model : nil
|
69
|
+
cc = @conjunction
|
70
|
+
HQMF::Precondition.new(@id, pcs, mr, cc, @negation)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module HQMF2
|
2
|
+
# Generates the Source Data Criteria from the entries in the HQMF
|
3
|
+
class SourceDataCriteriaHelper
|
4
|
+
# Generates an identifier based on the leftover elements included in the source data criteria.
|
5
|
+
def self.identifier(criteria)
|
6
|
+
sha256 = ''
|
7
|
+
sha256 << "#{criteria.code_list_id}:"
|
8
|
+
sha256 << "#{criteria.definition}:"
|
9
|
+
sha256 << "#{criteria.status}:"
|
10
|
+
sha256 << "#{criteria.specific_occurrence}:"
|
11
|
+
sha256 << "#{criteria.specific_occurrence_const}:"
|
12
|
+
sha256 << "#{criteria.variable}:"
|
13
|
+
sha256 << (criteria.children_criteria.nil? ? '<nil>:' : "#{criteria.children_criteria.sort.join(',')}:")
|
14
|
+
|
15
|
+
Digest::SHA256.hexdigest sha256
|
16
|
+
end
|
17
|
+
|
18
|
+
# Rejects any derived elements as they should never be used as source.
|
19
|
+
def self.should_reject?(dc)
|
20
|
+
dc.definition == 'derived'
|
21
|
+
end
|
22
|
+
|
23
|
+
# Removes unnecessary elements from a data criteria to create a source data criteria
|
24
|
+
def self.strip_non_sc_elements(dc)
|
25
|
+
if [HQMF::DataCriteria::SATISFIES_ANY, HQMF::DataCriteria::SATISFIES_ALL].include? dc.definition
|
26
|
+
dc.instance_variable_set(:@definition, 'derived')
|
27
|
+
end
|
28
|
+
dc.instance_variable_set(:@source_data_criteria, dc.id)
|
29
|
+
dc.instance_variable_set(:@field_values, {})
|
30
|
+
dc.instance_variable_set(:@temporal_references, [])
|
31
|
+
dc.instance_variable_set(:@subset_operators, [])
|
32
|
+
dc.instance_variable_set(:@value, nil)
|
33
|
+
dc.instance_variable_set(:@negation, false)
|
34
|
+
dc.instance_variable_set(:@negation_code_list_id, nil)
|
35
|
+
dc
|
36
|
+
end
|
37
|
+
|
38
|
+
# determins if a data criteria has any non-SDC fields set (i.e., those fields need to be stripped)
|
39
|
+
def self.already_stripped?(dc)
|
40
|
+
dc.field_values.blank? && dc.temporal_references.blank? && dc.subset_operators.blank? && dc.value.blank? && dc.negation.blank? && dc.negation_code_list_id.blank?
|
41
|
+
end
|
42
|
+
|
43
|
+
# Creates a data criteria based on an entry xml, removes any unnecessary elements (for the source),
|
44
|
+
# and adds a data criteria reference if none exist
|
45
|
+
def self.as_source_data_criteria(entry, data_criteria_references = {}, occurrences_map = {})
|
46
|
+
dc = DataCriteria.new(entry, data_criteria_references, occurrences_map)
|
47
|
+
dc.original_id = dc.id
|
48
|
+
unless dc.definition == 'derived' # && dc.temporal_references.blank? && dc.subset_operators.blank? && dc.value.blank? && dc.field_values.blank?
|
49
|
+
# add "_source" to the id to differentiate from the non-source
|
50
|
+
dc.id = "#{dc.id}_source"
|
51
|
+
end
|
52
|
+
dc = SourceDataCriteriaHelper.strip_non_sc_elements(dc)
|
53
|
+
# add it as a reference
|
54
|
+
if dc && (data_criteria_references[dc.id].nil? || data_criteria_references[dc.id].code_list_id.nil?)
|
55
|
+
data_criteria_references[dc.original_id] = dc
|
56
|
+
end
|
57
|
+
|
58
|
+
dc
|
59
|
+
end
|
60
|
+
|
61
|
+
# Check if there is an existing entry in the source data criteria list that matches the candidate passed in
|
62
|
+
# this is used to prevent adding duplicate source data criteria entries when one already exists
|
63
|
+
def self.find_existing_source_data_criteria(list, candidate)
|
64
|
+
list.each do |sdc|
|
65
|
+
# check if we have an exact match on an existing SDC
|
66
|
+
return sdc if SourceDataCriteriaHelper.identifier(sdc) == SourceDataCriteriaHelper.identifier(candidate)
|
67
|
+
# we have another existing copy of the specific occurrence (identified via the constant and occurrence lettering), use that rather than duplicating... there will not be an
|
68
|
+
# exact match for variables since a new child will have been generated
|
69
|
+
return sdc if !sdc.specific_occurrence_const.nil? && sdc.specific_occurrence_const == candidate.specific_occurrence_const && sdc.specific_occurrence == candidate.specific_occurrence
|
70
|
+
end
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
|
74
|
+
# Given a list of criteria obtained from the XML, generate most of the source data criteria (since no explicit
|
75
|
+
# sources are given). After generating the source data criteria, filter the list to not include repeated,
|
76
|
+
# unnecessary sources, but maintain and return map of those that have been removed to those that they were replaced
|
77
|
+
# with.
|
78
|
+
def self.get_source_data_criteria_list(full_criteria_list, data_criteria_references = {}, occurrences_map = {})
|
79
|
+
# currently, this will erase the sources if the ids are the same, but will not correct references later on
|
80
|
+
source_data_criteria = full_criteria_list.map do |entry|
|
81
|
+
SourceDataCriteriaHelper.as_source_data_criteria(entry, data_criteria_references, occurrences_map)
|
82
|
+
end
|
83
|
+
|
84
|
+
collapsed_source_data_criteria_map = {}
|
85
|
+
uniq_source_data_criteria = {}
|
86
|
+
source_data_criteria.each do |sdc|
|
87
|
+
identifier = SourceDataCriteriaHelper.identifier(sdc)
|
88
|
+
if uniq_source_data_criteria.key? identifier
|
89
|
+
collapsed_source_data_criteria_map[sdc.original_id] = uniq_source_data_criteria[identifier].id
|
90
|
+
else
|
91
|
+
uniq_source_data_criteria[identifier] = sdc
|
92
|
+
end
|
93
|
+
end
|
94
|
+
unique = uniq_source_data_criteria.values.reject { |dc| SourceDataCriteriaHelper.should_reject?(dc) }
|
95
|
+
|
96
|
+
# we need an empty data criteria in source that acts as the target for the specific occurrence
|
97
|
+
# the data criteria that we are duplicating will eventually get turned into a specific occurrence
|
98
|
+
occurrences = unique.select {|dc| occurrences_map[dc.id] && dc.definition != 'derived' }
|
99
|
+
occurrences.each do |occurrence|
|
100
|
+
# do not create a nonspecific SDC for variables
|
101
|
+
unless occurrence.variable
|
102
|
+
dc = SourceDataCriteriaHelper.as_source_data_criteria(occurrence.entry)
|
103
|
+
dc.id = "#{dc.id}_nonSpecific"
|
104
|
+
dc.instance_variable_set(:@source_data_criteria, dc.id)
|
105
|
+
unique << dc unless SourceDataCriteriaHelper.find_existing_source_data_criteria(unique, dc)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
[unique, collapsed_source_data_criteria_map]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,448 @@
|
|
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
|
+
# Generates this classes hqmf-model equivalent
|
12
|
+
def to_model
|
13
|
+
HQMF::AnyValue.new(@type)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Represents a bound within a HQMF pauseQuantity, has a value, a unit and an
|
18
|
+
# inclusive/exclusive indicator
|
19
|
+
class Value
|
20
|
+
include HQMF2::Utilities
|
21
|
+
|
22
|
+
attr_reader :type, :unit, :value
|
23
|
+
|
24
|
+
def initialize(entry, default_type = 'PQ', force_inclusive = false, _parent = nil)
|
25
|
+
@entry = entry
|
26
|
+
@type = attr_val('./@xsi:type') || default_type
|
27
|
+
@unit = attr_val('./@unit')
|
28
|
+
@value = attr_val('./@value')
|
29
|
+
@force_inclusive = force_inclusive
|
30
|
+
|
31
|
+
# FIXME: Remove below when lengthOfStayQuantity unit is fixed
|
32
|
+
@unit = 'd' if @unit == 'days'
|
33
|
+
end
|
34
|
+
|
35
|
+
def inclusive?
|
36
|
+
# If the high and low value are at any time the same, then it must be an inclusive value.
|
37
|
+
equivalent = attr_val('../cda:low/@value') == attr_val('../cda:high/@value')
|
38
|
+
|
39
|
+
# If and inclusivity value is set for any specific value, then mark the value as inclusive.
|
40
|
+
# IDEA: This could be limited in future iterations by including the parent type (temporal reference, subset code,
|
41
|
+
# etc.)
|
42
|
+
inclusive_temporal_ref? || inclusive_length_of_stay? || inclusive_basic_values? || inclusive_subsets? ||
|
43
|
+
equivalent || @force_inclusive
|
44
|
+
end
|
45
|
+
|
46
|
+
# Check whether the temporal reference should be marked as inclusive
|
47
|
+
def inclusive_temporal_ref?
|
48
|
+
# FIXME: NINF is used instead of 0 sometimes...? (not in the IG)
|
49
|
+
# FIXME: Given nullFlavor, but IG uses it and nullValue everywhere...
|
50
|
+
less_than_equal_tr = attr_val('../@highClosed') == 'true' &&
|
51
|
+
(attr_val('../cda:low/@value') == '0' || attr_val('../cda:low/@nullFlavor') == 'NINF')
|
52
|
+
greater_than_equal_tr = attr_val('../cda:high/@nullFlavor') == 'PINF' &&
|
53
|
+
attr_val('../cda:low/@value')
|
54
|
+
# Both less and greater require lowClosed to be set to true
|
55
|
+
(less_than_equal_tr || greater_than_equal_tr) && attr_val('../@lowClosed') == 'true'
|
56
|
+
end
|
57
|
+
|
58
|
+
# Check whether the length of stay should be inclusive.
|
59
|
+
def inclusive_length_of_stay?
|
60
|
+
# lengthOfStay - EH111, EH108
|
61
|
+
less_than_equal_los = attr_val('../cda:low/@nullFlavor') == 'NINF' &&
|
62
|
+
attr_val('../@highClosed') != 'false'
|
63
|
+
|
64
|
+
greater_than_equal_los = attr_val('../cda:high/@nullFlavor') == 'PINF' &&
|
65
|
+
attr_val('../@lowClosed') != 'false'
|
66
|
+
# Both less and greater require that the type is PQ
|
67
|
+
(less_than_equal_los || greater_than_equal_los) && attr_val('@xsi:type') == 'PQ'
|
68
|
+
end
|
69
|
+
|
70
|
+
# Check is the basic values should be marked as inclusive, currently only checks for greater than case
|
71
|
+
def inclusive_basic_values?
|
72
|
+
# Basic values - EP65, EP9, and more
|
73
|
+
attr_val('../cda:high/@nullFlavor') == 'PINF' &&
|
74
|
+
attr_val('../cda:low/@value') &&
|
75
|
+
attr_val('../@lowClosed') != 'false' &&
|
76
|
+
attr_val('../@xsi:type') == 'IVL_PQ'
|
77
|
+
end
|
78
|
+
|
79
|
+
# Check if subset values should be marked as inclusive. Currently only handles greater than
|
80
|
+
def inclusive_subsets?
|
81
|
+
# subset - EP128, EH108
|
82
|
+
attr_val('../cda:low/@value') != '0' &&
|
83
|
+
!attr_val('../cda:high/@value') &&
|
84
|
+
attr_val('../@lowClosed') != 'false' &&
|
85
|
+
!attr_val('../../../../../qdm:subsetCode/@code').nil?
|
86
|
+
end
|
87
|
+
|
88
|
+
def derived?
|
89
|
+
case attr_val('./@nullFlavor')
|
90
|
+
when 'DER'
|
91
|
+
true
|
92
|
+
else
|
93
|
+
false
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def expression
|
98
|
+
if !derived?
|
99
|
+
nil
|
100
|
+
else
|
101
|
+
attr_val('./cda:expression/@value')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Generates this classes hqmf-model equivalent
|
106
|
+
def to_model
|
107
|
+
HQMF::Value.new(type, unit, value, inclusive?, derived?, expression)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Represents a HQMF physical quantity which can have low and high bounds
|
112
|
+
class Range
|
113
|
+
include HQMF2::Utilities
|
114
|
+
attr_accessor :low, :high, :width
|
115
|
+
|
116
|
+
def initialize(entry, type = nil)
|
117
|
+
@type = type
|
118
|
+
@entry = entry
|
119
|
+
return unless @entry
|
120
|
+
@low = optional_value("#{default_element_name}/cda:low", default_bounds_type)
|
121
|
+
@high = optional_value("#{default_element_name}/cda:high", default_bounds_type)
|
122
|
+
# Unset low bound to resolve verbose value bounds descriptions
|
123
|
+
@low = nil if (@high.try(:value) && @high.value.to_i > 0) && (@low.try(:value) && @low.value.try(:to_i) == 0)
|
124
|
+
@width = optional_value("#{default_element_name}/cda:width", 'PQ')
|
125
|
+
end
|
126
|
+
|
127
|
+
def type
|
128
|
+
@type || attr_val('./@xsi:type')
|
129
|
+
end
|
130
|
+
|
131
|
+
# Generates this classes hqmf-model equivalent
|
132
|
+
def to_model
|
133
|
+
lm = low.try(:to_model)
|
134
|
+
hm = high.try(:to_model)
|
135
|
+
wm = width.try(:to_model)
|
136
|
+
model_type = type
|
137
|
+
if @entry.at_xpath('./cda:uncertainRange', HQMF2::Document::NAMESPACES)
|
138
|
+
model_type = 'IVL_PQ'
|
139
|
+
end
|
140
|
+
|
141
|
+
if generate_any_value?(lm, hm)
|
142
|
+
# Generate AnyValue if the only elements in the range are AnyValues.
|
143
|
+
HQMF::AnyValue.new
|
144
|
+
elsif generate_value?(lm, hm)
|
145
|
+
# Generate a singel value if both low and high are the same
|
146
|
+
HQMF::Value.new(lm.type, nil, lm.value, lm.inclusive?, lm.derived?, lm.expression)
|
147
|
+
else
|
148
|
+
HQMF::Range.new(model_type, lm, hm, wm)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Check if are only AnyValue elements for low and high
|
153
|
+
def generate_any_value?(lm, hm)
|
154
|
+
(lm.nil? || lm.is_a?(HQMF::AnyValue)) && (hm.nil? || hm.is_a?(HQMF::AnyValue))
|
155
|
+
end
|
156
|
+
|
157
|
+
# Check if the value for the range should actually produce a single value instead of a range (if low and high are
|
158
|
+
# the same)
|
159
|
+
def generate_value?(lm, hm)
|
160
|
+
!lm.nil? && lm.try(:value) == hm.try(:value) && lm.try(:unit).nil? && hm.try(:unit).nil?
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
# Either derives a value from a specific path or generates a new value (or returns nil if none found)
|
166
|
+
def optional_value(xpath, type)
|
167
|
+
value_def = @entry.at_xpath(xpath, HQMF2::Document::NAMESPACES)
|
168
|
+
return unless value_def
|
169
|
+
if value_def['flavorId'] == 'ANY.NONNULL'
|
170
|
+
AnyValue.new
|
171
|
+
else
|
172
|
+
created_value = Value.new(value_def, type)
|
173
|
+
# Return nil if no value was parsed
|
174
|
+
created_value if created_value.try(:value)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Defines how the time based element should be described
|
179
|
+
def default_element_name
|
180
|
+
case type
|
181
|
+
when 'IVL_PQ'
|
182
|
+
'.'
|
183
|
+
when 'IVL_TS'
|
184
|
+
'cda:phase'
|
185
|
+
else
|
186
|
+
'cda:uncertainRange'
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Sets up the default bound type as either time based or a physical quantity
|
191
|
+
def default_bounds_type
|
192
|
+
case type
|
193
|
+
when 'IVL_TS'
|
194
|
+
'TS'
|
195
|
+
else
|
196
|
+
'PQ'
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Represents an HQMF effective time which is a specialization of an interval
|
202
|
+
class EffectiveTime < Range
|
203
|
+
def initialize(entry)
|
204
|
+
super
|
205
|
+
end
|
206
|
+
|
207
|
+
def type
|
208
|
+
'IVL_TS'
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# Represents a HQMF CD value which has a code and codeSystem
|
213
|
+
class Coded
|
214
|
+
include HQMF2::Utilities
|
215
|
+
|
216
|
+
def initialize(entry)
|
217
|
+
@entry = entry
|
218
|
+
end
|
219
|
+
|
220
|
+
def type
|
221
|
+
attr_val('./@xsi:type') || 'CD'
|
222
|
+
end
|
223
|
+
|
224
|
+
def system
|
225
|
+
attr_val('./@codeSystem')
|
226
|
+
end
|
227
|
+
|
228
|
+
def code
|
229
|
+
attr_val('./@code')
|
230
|
+
end
|
231
|
+
|
232
|
+
def code_list_id
|
233
|
+
attr_val('./@valueSet')
|
234
|
+
end
|
235
|
+
|
236
|
+
def title
|
237
|
+
attr_val('./*/@value')
|
238
|
+
end
|
239
|
+
|
240
|
+
def value
|
241
|
+
code
|
242
|
+
end
|
243
|
+
|
244
|
+
def derived?
|
245
|
+
false
|
246
|
+
end
|
247
|
+
|
248
|
+
def unit
|
249
|
+
nil
|
250
|
+
end
|
251
|
+
|
252
|
+
# Generates this classes hqmf-model equivalent
|
253
|
+
def to_model
|
254
|
+
HQMF::Coded.new(type, system, code, code_list_id, title)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
# Represents a subset of a specific group (the first in the group, the sum of the group, etc.)
|
258
|
+
class SubsetOperator
|
259
|
+
include HQMF2::Utilities
|
260
|
+
|
261
|
+
attr_reader :type, :value
|
262
|
+
ORDER_SUBSETS = %w(FIRST SECOND THIRD FOURTH FIFTH)
|
263
|
+
LAST_SUBSETS = %w(LAST RECENT)
|
264
|
+
TIME_SUBSETS = %w(DATEDIFF TIMEDIFF)
|
265
|
+
QDM_TYPE_MAP = { 'QDM_LAST:' => 'RECENT', 'QDM_SUM:SUM' => 'COUNT' }
|
266
|
+
|
267
|
+
def initialize(entry)
|
268
|
+
@entry = entry
|
269
|
+
|
270
|
+
sequence_number = attr_val('./cda:sequenceNumber/@value')
|
271
|
+
qdm_subset_code = attr_val('./qdm:subsetCode/@code')
|
272
|
+
subset_code = attr_val('./cda:subsetCode/@code')
|
273
|
+
if sequence_number
|
274
|
+
@type = ORDER_SUBSETS[sequence_number.to_i - 1]
|
275
|
+
else
|
276
|
+
@type = translate_type(subset_code, qdm_subset_code)
|
277
|
+
end
|
278
|
+
|
279
|
+
value_def = handle_value_definition
|
280
|
+
@value = HQMF2::Range.new(value_def, 'IVL_PQ') if value_def && !@value
|
281
|
+
end
|
282
|
+
|
283
|
+
# Return the value definition (what to calculate it on) associated with this subset.
|
284
|
+
# Other values, such as type and value, may be modified depending on this value.
|
285
|
+
def handle_value_definition
|
286
|
+
value_def = @entry.at_xpath('./*/cda:repeatNumber', HQMF2::Document::NAMESPACES)
|
287
|
+
unless value_def
|
288
|
+
# TODO: HQMF needs better differentiation between SUM & COUNT...
|
289
|
+
# currently using presence of repeatNumber...
|
290
|
+
@type = 'SUM' if @type == 'COUNT'
|
291
|
+
value_def = @entry.at_xpath('./*/cda:value', HQMF2::Document::NAMESPACES)
|
292
|
+
end
|
293
|
+
|
294
|
+
# TODO: Resolve extracting values embedded in criteria within outboundRel's
|
295
|
+
if @type == 'SUM'
|
296
|
+
value_def = @entry.at_xpath('./*/*/*/cda:value', HQMF2::Document::NAMESPACES)
|
297
|
+
end
|
298
|
+
|
299
|
+
if value_def
|
300
|
+
value_type = value_def.at_xpath('./@xsi:type', HQMF2::Document::NAMESPACES)
|
301
|
+
@value = HQMF2::AnyValue.new if String.try_convert(value_type) == 'ANY'
|
302
|
+
end
|
303
|
+
|
304
|
+
value_def
|
305
|
+
end
|
306
|
+
|
307
|
+
# Take a qdm type code to map it to a subset operator, or failing at finding that, return the given subset code.
|
308
|
+
def translate_type(subset_code, qdm_subset_code)
|
309
|
+
combined = "#{qdm_subset_code}:#{subset_code}"
|
310
|
+
if QDM_TYPE_MAP[combined]
|
311
|
+
QDM_TYPE_MAP[combined]
|
312
|
+
else
|
313
|
+
subset_code
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
# Generates this classes hqmf-model equivalent
|
318
|
+
def to_model
|
319
|
+
vm = value ? value.to_model : nil
|
320
|
+
HQMF::SubsetOperator.new(type, vm)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
# Represents a time bounded reference. Wraps the "Range" class
|
325
|
+
class TemporalReference
|
326
|
+
include HQMF2::Utilities
|
327
|
+
|
328
|
+
attr_reader :type, :reference, :range
|
329
|
+
|
330
|
+
# Use updated mappings to HDS temporal reference types (as used in SimpleXML Parser)
|
331
|
+
# https://github.com/projecttacoma/simplexml_parser/blob/fa0f589d98059b88d77dc3cb465b62184df31671/lib/model/types.rb#L167
|
332
|
+
UPDATED_TYPES = {
|
333
|
+
'EAOCW' => 'EACW',
|
334
|
+
'EAEORECW' => 'EACW',
|
335
|
+
'EAOCWSO' => 'EACWS',
|
336
|
+
'EASORECWS' => 'EACWS',
|
337
|
+
'EBOCW' => 'EBCW',
|
338
|
+
'EBEORECW' => 'EBCW',
|
339
|
+
'EBOCWSO' => 'EBCWS',
|
340
|
+
'EBSORECWS' => 'EBCWS',
|
341
|
+
'ECWSO' => 'ECWS',
|
342
|
+
'SAOCWEO' => 'SACWE',
|
343
|
+
'SAEORSCWE' => 'SACWE',
|
344
|
+
'SAOCW' => 'SACW',
|
345
|
+
'SASORSCW' => 'SACW',
|
346
|
+
'SBOCWEO' => 'SBCWE',
|
347
|
+
'SBEORSCWE' => 'SBCWE',
|
348
|
+
'SBOCW' => 'SBCW',
|
349
|
+
'SBSORSCW' => 'SBCW',
|
350
|
+
'SCWEO' => 'SCWE',
|
351
|
+
'OVERLAPS' => 'OVERLAP'
|
352
|
+
}
|
353
|
+
|
354
|
+
def initialize(entry)
|
355
|
+
@entry = entry
|
356
|
+
@type = UPDATED_TYPES[attr_val('./@typeCode')] || attr_val('./@typeCode')
|
357
|
+
@reference = Reference.new(@entry.at_xpath('./*/cda:id', HQMF2::Document::NAMESPACES))
|
358
|
+
range_def = @entry.at_xpath('./qdm:temporalInformation/qdm:delta', HQMF2::Document::NAMESPACES)
|
359
|
+
@range = HQMF2::Range.new(range_def, 'IVL_PQ') if range_def
|
360
|
+
end
|
361
|
+
|
362
|
+
# Generates this classes hqmf-model equivalent
|
363
|
+
def to_model
|
364
|
+
rm = range ? range.to_model : nil
|
365
|
+
HQMF::TemporalReference.new(type, reference.to_model, rm)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
# Represents a HQMF reference to a data criteria that has a given type
|
370
|
+
class TypedReference
|
371
|
+
include HQMF2::Utilities
|
372
|
+
attr_accessor :id, :type, :mood
|
373
|
+
|
374
|
+
# Create a new HQMF::Reference
|
375
|
+
# @param [String] id
|
376
|
+
def initialize(entry, type = nil, verbose = false)
|
377
|
+
@entry = entry
|
378
|
+
@type = type || attr_val('./@classCode')
|
379
|
+
@mood = attr_val('./@moodCode')
|
380
|
+
@entry = entry.elements.first unless entry.at_xpath('./@extension')
|
381
|
+
@verbose = verbose
|
382
|
+
end
|
383
|
+
|
384
|
+
# Generate the reference for the typed reference to use
|
385
|
+
def reference
|
386
|
+
value = "#{attr_val('./@extension')}_#{attr_val('./@root')}"
|
387
|
+
strip_tokens(value)
|
388
|
+
end
|
389
|
+
|
390
|
+
# Generates this classes hqmf-model equivalent
|
391
|
+
def to_model
|
392
|
+
HQMF::TypedReference.new(reference, @type, @mood)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
# Represents a HQMF reference from a precondition to a data criteria
|
397
|
+
class Reference
|
398
|
+
include HQMF2::Utilities
|
399
|
+
|
400
|
+
def initialize(entry)
|
401
|
+
@entry = entry
|
402
|
+
end
|
403
|
+
|
404
|
+
# Generates the id to use for a reference
|
405
|
+
def id
|
406
|
+
if @entry.is_a? String
|
407
|
+
@entry
|
408
|
+
else
|
409
|
+
id = strip_tokens("#{attr_val('./@extension')}_#{attr_val('./@root')}")
|
410
|
+
# Handle MeasurePeriod references for calculation code
|
411
|
+
id = 'MeasurePeriod' if id.try(:start_with?, 'measureperiod')
|
412
|
+
id
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
# Generates this classes hqmf-model equivalent
|
417
|
+
def to_model
|
418
|
+
HQMF::Reference.new(id)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
# Creates a Data Criteria given a map of options, and is used when full
|
423
|
+
# criteria parsing is not necessary.
|
424
|
+
class DataCriteriaWrapper
|
425
|
+
attr_accessor :status, :value, :effective_time
|
426
|
+
attr_accessor :temporal_references, :subset_operators, :children_criteria
|
427
|
+
attr_accessor :derivation_operator, :negation, :negation_code_list_id, :description
|
428
|
+
attr_accessor :field_values, :source_data_criteria, :specific_occurrence_const
|
429
|
+
attr_accessor :specific_occurrence, :comments
|
430
|
+
attr_accessor :id, :title, :definition, :variable, :code_list_id, :value, :inline_code_list
|
431
|
+
|
432
|
+
def initialize(opts = {})
|
433
|
+
opts.each { |k, v| instance_variable_set("@#{k}", v) }
|
434
|
+
end
|
435
|
+
|
436
|
+
# Generates this classes hqmf-model equivalent
|
437
|
+
def to_model
|
438
|
+
mv = @value ? @value.to_model : nil
|
439
|
+
met = @effective_time ? @effective_time.to_model : nil
|
440
|
+
mtr = @temporal_references
|
441
|
+
mso = @subset_operators
|
442
|
+
HQMF::DataCriteria.new(@id, @title, nil, @description, @code_list_id, @children_criteria,
|
443
|
+
@derivation_operator, @definition, @status, mv, field_values, met, @inline_code_list,
|
444
|
+
@negation, @negation_code_list_id, mtr, mso, @specific_occurrence,
|
445
|
+
@specific_occurrence_const, @source_data_criteria, @comments, @variable)
|
446
|
+
end
|
447
|
+
end
|
448
|
+
end
|