lib/hqmf-parser/2.0/data_criteria.rb in health-data-standards-3.6.1 vs lib/hqmf-parser/2.0/data_criteria.rb in health-data-standards-3.7.0
- old
+ new
@@ -1,395 +1,369 @@
module HQMF2
# Represents a data criteria specification
class DataCriteria
-
- include HQMF2::Utilities
-
+ include HQMF2::Utilities, HQMF2::DataCriteriaTypeAndDefinitionExtraction, HQMF2::DataCriteriaPostProcessing
+
+ attr_accessor :id
+ attr_accessor :original_id
attr_reader :property, :type, :status, :value, :effective_time, :section
- attr_reader :temporal_references, :subset_operators, :children_criteria
+ attr_reader :temporal_references, :subset_operators, :children_criteria
attr_reader :derivation_operator, :negation, :negation_code_list_id, :description
attr_reader :field_values, :source_data_criteria, :specific_occurrence_const
- attr_reader :specific_occurrence, :is_source_data_criteria, :comments
+ attr_reader :specific_occurrence, :comments, :is_derived_specific_occurrence_variable
+ attr_reader :entry, :definition, :variable, :local_variable_name
- VARIABLE_TEMPLATE = "0.1.2.3.4.5.6.7.8.9.1"
- SATISFIES_ANY_TEMPLATE = "0.1.2.3.4.5.6.7.8.9.2"
- SATISFIES_ALL_TEMPLATE = "0.1.2.3.4.5.6.7.8.9.3"
-
- CONJUNCTION_CODE_TO_DERIVATION_OP = {
- 'OR' => 'UNION',
- 'AND' => 'XPRODUCT'
- }
-
CRITERIA_GLOB = "*[substring(name(),string-length(name())-7) = \'Criteria\']"
# Create a new instance based on the supplied HQMF entry
# @param [Nokogiri::XML::Element] entry the parsed HQMF entry
- def initialize(entry)
+ def initialize(entry, data_criteria_references = {}, occurrences_map = {})
@entry = entry
- @local_variable_name = extract_local_variable_name
- @status = attr_val('./*/cda:statusCode/@code')
- @description = attr_val("./#{CRITERIA_GLOB}/cda:text/@value")
- extract_negation()
- extract_specific_or_source()
- @effective_time = extract_effective_time
- @temporal_references = extract_temporal_references
- @derivation_operator = extract_derivation_operator
- @field_values = extract_field_values
- @subset_operators = extract_subset_operators
- @children_criteria = extract_child_criteria
- @id_xpath = './*/cda:id/@extension'
- @code_list_xpath = './*/cda:code'
- @value_xpath = './*/cda:value'
- @comments = @entry.xpath("./#{CRITERIA_GLOB}/cda:text/cda:xml/cda:qdmUserComments/cda:item/text()", HQMF2::Document::NAMESPACES).map{ |v| v.content }
- @variable = false
-
- # Try to determine what kind of data criteria we are dealing with
- # First we look for a template id and if we find one just use the definition
- # status and negation associated with that
- if !extract_type_from_template_id()
- # If no template id or not one we recognize then try to determine type from
- # the definition element
- extract_type_from_definition()
- end
-
- patch_xpaths_for_criteria_type()
+ @data_criteria_references = data_criteria_references
+ @occurrences_map = occurrences_map
+ basic_setup
+ @variable = DataCriteriaMethods.extract_variable(@local_variable_name, @id)
+ @field_values = DataCriteriaMethods.extract_field_values(@entry, @negation)
+ @description = extract_description
+ obtain_specific_and_source = SpecificOccurrenceAndSource.new(@entry, @id, @local_variable_name,
+ @data_criteria_references, @occurrences_map)
+ # Pulling these 5 variables out via destructing
+ @source_data_criteria,
+ @source_data_criteria_root,
+ @source_data_criteria_extension,
+ @specific_occurrence,
+ @specific_occurrence_const = obtain_specific_and_source.extract_specific_occurrences_and_source_data_criteria
+ extract_definition_from_template_or_type
+ post_processing
end
- def patch_xpaths_for_criteria_type
- # Patch xpaths when necessary, HQMF data criteria are irregular in structure so
- # the same information is found in different places depending on the type of
- # criteria
- # Assumes @definition and @status are already set
- case @definition
- when 'transfer_to', 'transfer_from'
- @code_list_xpath = './cda:observationCriteria/cda:value'
- when 'diagnosis', 'diagnosis_family_history'
- @code_list_xpath = './cda:observationCriteria/cda:value'
- when 'physical_exam', 'risk_category_assessment', 'procedure_result', 'laboratory_test', 'diagnostic_study_result', 'functional_status_result', 'intervention_result'
- @value = extract_value
- when 'medication'
- case @status
- when 'dispensed', 'ordered'
- @code_list_xpath = './cda:supplyCriteria/cda:participation/cda:role/cda:code'
- else # active or administered
- @code_list_xpath = './cda:substanceAdministrationCriteria/cda:participation/cda:role/cda:code'
- end
- when 'patient_characteristic', 'patient_characteristic_birthdate', 'patient_characteristic_clinical_trial_participant', 'patient_characteristic_expired', 'patient_characteristic_gender', 'patient_characteristic_age', 'patient_characteristic_languages', 'patient_characteristic_marital_status', 'patient_characteristic_race'
- @value = extract_value
- when 'variable'
- @value = extract_value
- end
- end
-
- def extract_local_variable_name
- lvn = @entry.at_xpath("./cda:localVariableName")
- lvn["value"] if lvn
- end
-
- def extract_type_from_definition
- if @entry.at_xpath("./cda:grouperCriteria")
- if @local_variable_name && @local_variable_name.match(/qdm_/)
- @variable = true
- end
- @definition = 'derived'
- return
- end
- # See if we can find a match for the entry definition value and status.
- entry_type = attr_val('./*/cda:definition/*/cda:id/@extension')
- begin
- settings = HQMF::DataCriteria.get_settings_for_definition(entry_type, @status)
- @definition = entry_type
- rescue
- # if no exact match then try a string match just using entry definition value
- case entry_type
- when 'Problem', 'Problems'
- @definition = 'diagnosis'
- when 'Encounter', 'Encounters'
- @definition = 'encounter'
- when 'LabResults', 'Results'
- @definition = 'laboratory_test'
- when 'Procedure', 'Procedures'
- @definition = 'procedure'
- when 'Medication', 'Medications'
- @definition = 'medication'
- if !@status
- @status = 'active'
- end
- when 'RX'
- @definition = 'medication'
- if !@status
- @status = 'dispensed'
- end
- when 'Demographics'
- @definition = definition_for_demographic
- when 'Derived'
- @definition = 'derived'
- when nil
- @definition = 'variable'
- else
- raise "Unknown data criteria template identifier [#{entry_type}]"
- end
- end
- end
-
- def extract_type_from_template_id
- template_ids = @entry.xpath('./*/cda:templateId/cda:item', HQMF2::Document::NAMESPACES).collect do |template_def|
- HQMF2::Utilities.attr_val(template_def, '@root')
- end
- if template_ids.include?(HQMF::DataCriteria::SOURCE_DATA_CRITERIA_TEMPLATE_ID)
- @is_source_data_criteria = true
- end
- found = false
- template_ids.each do |template_id|
- defs = HQMF::DataCriteria.definition_for_template_id(template_id)
-
- if defs
- @definition = defs['definition']
- @status = defs['status'].length > 0 ? defs['status'] : nil
- @negation = defs['negation']
- found ||= true
- elsif template_id == VARIABLE_TEMPLATE
- @derivation_operator = HQMF::DataCriteria::INTERSECT if @derivation_operator == HQMF::DataCriteria::XPRODUCT
- @definition ||= 'derived'
- @negation = false
- @variable = true
- found ||= true
- elsif template_id == SATISFIES_ANY_TEMPLATE
- @definition = HQMF::DataCriteria::SATISFIES_ANY
- @negation = false
- return true
- elsif template_id == SATISFIES_ALL_TEMPLATE
- @definition = HQMF::DataCriteria::SATISFIES_ALL
- @derivation_operator = HQMF::DataCriteria::INTERSECT
- @negation = false
- found ||= true
- end
- end
- found
- end
-
def to_s
props = {
- :property => property,
- :type => type,
- :status => status,
- :section => section
+ property: property,
+ type: type,
+ status: status,
+ section: section
}
- "DataCriteria#{props.to_s}"
+ "DataCriteria#{props}"
end
-
+
+ # TODO: Remove id method if id attribute is sufficient
# Get the identifier of the criteria, used elsewhere within the document for referencing
# @return [String] the identifier of this data criteria
- def id
- attr_val(@id_xpath)
- end
-
+ # def id
+ # attr_val(@id_xpath)
+ # end
+
# Get the title of the criteria, provides a human readable description
# @return [String] the title of this data criteria
def title
- dispValue = attr_val("#{@code_list_xpath}/cda:displayName/@value")
- desc = nil
- if @description && (@description.include? ":")
- desc = @description.match(/.*:\s+(.+)/)[1]
- end
- dispValue || desc || id
+ disp_value = attr_val("#{@code_list_xpath}/cda:displayName/@value")
+ @title || disp_value || @description || id # allow defined titles to take precedence
end
-
+
# Get the code list OID of the criteria, used as an index to the code list database
# @return [String] the code list identifier of this data criteria
def code_list_id
- attr_val("#{@code_list_xpath}/@valueSet")
+ @code_list_id || attr_val("#{@code_list_xpath}/@valueSet")
end
-
- def inline_code_list
- codeSystem = attr_val("#{@code_list_xpath}/@codeSystem")
- if codeSystem
- codeSystemName = HealthDataStandards::Util::CodeSystemHelper.code_system_for(codeSystem)
+
+ # Generates this classes hqmf-model equivalent
+ def to_model
+ mv = value.try(:to_model)
+ met = effective_time.try(:to_model)
+ mtr = temporal_references.collect(&:to_model)
+ mso = subset_operators.collect(&:to_model)
+ field_values = retrieve_field_values_model_for_model
+
+ retrieve_title_and_description_for_model unless @variable || @derivation_operator
+
+ @code_list_id = nil if @derivation_operator
+
+ # prevent json model generation of empty children and comments
+ cc = children_criteria.present? ? children_criteria : nil
+ comments = @comments.present? ? @comments : nil
+
+ HQMF::DataCriteria.new(id, title, nil, description, @code_list_id, cc, derivation_operator, @definition, status,
+ mv, field_values, met, retrieve_code_system_for_model, @negation, @negation_code_list_id,
+ mtr, mso, @specific_occurrence, @specific_occurrence_const, @source_data_criteria,
+ comments, @variable)
+ end
+
+ # Return a new DataCriteria instance with only grouper attributes set.
+ # A grouper criteria allows multiple data criteria events to be contained in a single
+ # logical set (a union or intersection of these multiple events - i.e. A during (B or C or D)).
+ # Grouper criteria also provide a way to combine multiple criteria that reference a specific
+ # occurrence of an event.
+ def extract_variable_grouper
+ return unless @variable
+ @variable = false
+ @id = "GROUP_#{@id}"
+ if @children_criteria.length == 1 && @children_criteria[0] =~ /GROUP_/
+ reference_criteria = @data_criteria_references[@children_criteria.first]
+ return if reference_criteria.nil?
+ duplicate_child_info(reference_criteria)
+ @definition = reference_criteria.definition
+ @status = reference_criteria.status
+ @children_criteria = []
+ end
+ @specific_occurrence = nil
+ @specific_occurrence_const = nil
+ # set the source data criteria id to the id for variables
+ @source_data_criteria = @id
+ DataCriteria.new(@entry, @data_criteria_references, @occurrences_map).extract_as_grouper
+ end
+
+ # Extract this data criteria as a grouper data criteria
+ # SHOULD only be called on a variable data criteria instance
+ def extract_as_grouper
+ @field_values = {}
+ @temporal_references = []
+ @subset_operators = []
+ @derivation_operator = HQMF::DataCriteria::UNION
+ @definition = 'derived'
+ @status = nil
+ @children_criteria = ["GROUP_#{@id}"]
+ @source_data_criteria = @id
+ self
+ end
+
+ # Handle elements that are marked as variable groupers that should not be turned into a "holding element"
+ # (defined as a data criteria that encapsulates the calculation material for other data criteria elements,
+ # where the other data criteria elements reference the holding element as a child element)
+ def handle_derived_specific_occurrence_variable
+ # If the first child is all the exists, and it has been marked as a "group" element, switch this over to map to
+ # the new element.
+ if !@data_criteria_references["GROUP_#{@children_criteria.first}"].nil? && @children_criteria.length == 1
+ @children_criteria[0] = "GROUP_#{@children_criteria.first}"
+ # If the group element is not found, extract the information from the child and force it into the variable.
+ elsif @children_criteria.length == 1 && @children_criteria.first.present?
+ reference_criteria = @data_criteria_references[@children_criteria.first]
+ return if reference_criteria.nil?
+ duplicate_child_info(reference_criteria)
+ @children_criteria = reference_criteria.children_criteria
+ end
+ end
+
+ # clone method. This is needed because we need to extract a new source data criteria for variables
+ # typically "cloning" is done by re-parsing the xml entry, however with post processing that does
+ # not give us the correct SDC data when we are trying to recreate since we are looping back through
+ # the same data criteria before it has finished processing: See: DocUtilities.extract_source_data_criteria
+ def clone
+ other = DataCriteria.new(@entry, @data_criteria_references, @occurrences_map)
+ other.instance_variable_set(:@id, @id)
+ other.instance_variable_set(:@original_id, @original_id)
+ other.instance_variable_set(:@property, @property)
+ other.instance_variable_set(:@type, @type)
+ other.instance_variable_set(:@status, @status)
+ other.instance_variable_set(:@code_list_id, @code_list_id)
+ other.instance_variable_set(:@value, @value)
+ other.instance_variable_set(:@effective_time, @effective_time)
+ other.instance_variable_set(:@section, @section)
+ other.instance_variable_set(:@temporal_references, @temporal_references)
+ other.instance_variable_set(:@subset_operators, @subset_operators)
+ other.instance_variable_set(:@children_criteria, @children_criteria)
+ other.instance_variable_set(:@derivation_operator, @derivation_operator)
+ other.instance_variable_set(:@negation, @negation)
+ other.instance_variable_set(:@negation_code_list_id, @negation_code_list_id)
+ other.instance_variable_set(:@description, @description)
+ other.instance_variable_set(:@field_values, @field_values)
+ other.instance_variable_set(:@source_data_criteria, @source_data_criteria)
+ other.instance_variable_set(:@specific_occurrence_const, @specific_occurrence_const)
+ other.instance_variable_set(:@specific_occurrence, @specific_occurrence)
+ other.instance_variable_set(:@comments, @comments)
+ other.instance_variable_set(:@is_derived_specific_occurrence_variable, @is_derived_specific_occurrence_variable)
+ other.instance_variable_set(:@entry, @entry)
+ other.instance_variable_set(:@definition, @definition)
+ other.instance_variable_set(:@variable, @variable)
+ other.instance_variable_set(:@local_variable_name, @local_variable_name)
+ other
+ end
+
+ private
+
+ # Handles elments that can be extracted directly from the xml. Utilises the "BaseExtractions" class.
+ def basic_setup
+ @status = attr_val('./*/cda:statusCode/@code')
+ @id_xpath = './*/cda:id/@extension'
+ @id = "#{attr_val('./*/cda:id/@extension')}_#{attr_val('./*/cda:id/@root')}"
+ @comments = @entry.xpath("./#{CRITERIA_GLOB}/cda:text/cda:xml/cda:qdmUserComments/cda:item/text()",
+ HQMF2::Document::NAMESPACES).map(&:content)
+ @code_list_xpath = './*/cda:code'
+ @value_xpath = './*/cda:value'
+ @is_derived_specific_occurrence_variable = false
+ simple_extractions = DataCriteriaBaseExtractions.new(@entry)
+ @template_ids = simple_extractions.extract_template_ids
+ @local_variable_name = simple_extractions.extract_local_variable_name
+ @temporal_references = simple_extractions.extract_temporal_references
+ @derivation_operator = simple_extractions.extract_derivation_operator
+ @children_criteria = simple_extractions.extract_child_criteria
+ @subset_operators = simple_extractions.extract_subset_operators
+ @negation, @negation_code_list_id = simple_extractions.extract_negation
+ end
+
+ # Extract the description (with some special handling if this is a variable). The MAT has added an encoded
+ # form of the variable name in the localVariableName field which is used if available. If not, fall back
+ # to the extension.
+ def extract_description
+ if @variable
+ encoded_name = attr_val('./cda:localVariableName/@value')
+ encoded_name = DataCriteriaMethods.extract_description_for_variable(encoded_name) if encoded_name
+ return encoded_name if encoded_name.present?
+ attr_val("./#{CRITERIA_GLOB}/cda:id/@extension")
else
- codeSystemName = attr_val("#{@code_list_xpath}/@codeSystemName")
+ attr_val("./#{CRITERIA_GLOB}/cda:text/@value") ||
+ attr_val("./#{CRITERIA_GLOB}/cda:title/@value") ||
+ attr_val("./#{CRITERIA_GLOB}/cda:id/@extension")
end
- codeValue = attr_val("#{@code_list_xpath}/@code")
- if codeSystemName && codeValue
- {codeSystemName => [codeValue]}
+ end
+
+ # Extract the code system from the xml taht the document should use
+ def retrieve_code_system_for_model
+ code_system = attr_val("#{@code_list_xpath}/@codeSystem")
+ if code_system
+ code_system_name = HealthDataStandards::Util::CodeSystemHelper.code_system_for(code_system)
else
- nil
+ code_system_name = attr_val("#{@code_list_xpath}/@codeSystemName")
end
+ code_value = attr_val("#{@code_list_xpath}/@code")
+ { code_system_name => [code_value] } if code_system_name && code_value
end
-
- def to_model
- mv = value ? value.to_model : nil
- met = effective_time ? effective_time.to_model : nil
- mtr = temporal_references.collect {|ref| ref.to_model}
- mso = subset_operators.collect {|opr| opr.to_model}
+ # Duplicates information from a child element to this data criteria if none exits.
+ # If the duplication requires that come values should be overwritten, do so only in the function calling this.
+ def duplicate_child_info(child_ref)
+ @title ||= child_ref.title
+ @type ||= child_ref.subset_operators
+ @definition ||= child_ref.definition
+ @status ||= child_ref.status
+ @code_list_id ||= child_ref.code_list_id
+ @temporal_references = child_ref.temporal_references if @temporal_references.empty?
+ @subset_operators ||= child_ref.subset_operators
+ @variable ||= child_ref.variable
+ @value ||= child_ref.value
+ end
+
+ # Generate the models of the field values
+ def retrieve_field_values_model_for_model
field_values = {}
@field_values.each_pair do |id, val|
field_values[id] = val.to_model
end
+ @code_list_id ||= code_list_id
# Model transfers as a field
- if ['transfer_to', 'transfer_from'].include? @definition
- field_values ||= {}
+ if %w(transfer_to transfer_from).include? @definition
field_code_list_id = @code_list_id
- if !field_code_list_id
+ @code_list_id = nil
+ unless field_code_list_id
field_code_list_id = attr_val("./#{CRITERIA_GLOB}/cda:outboundRelationship/#{CRITERIA_GLOB}/cda:value/@valueSet")
end
field_values[@definition.upcase] = HQMF::Coded.for_code_list(field_code_list_id, title)
end
- field_values = nil if field_values.empty?
+ return field_values unless field_values.empty?
+ end
- HQMF::DataCriteria.new(id, title, nil, description, code_list_id, children_criteria,
- derivation_operator, @definition, status, mv, field_values, met, inline_code_list,
- @negation, @negation_code_list_id, mtr, mso, @specific_occurrence,
- @specific_occurrence_const, @source_data_criteria, @comments, @variable)
+ # Generate the title and description used when producing the model
+ def retrieve_title_and_description_for_model
+ # drop "* Value Set" from titles
+ exact_desc = title.split(' ')[0...-3].join(' ')
+ # don't drop anything for patient characterstic titles
+ exact_desc = title if @definition.start_with?('patient_characteristic') && !title.end_with?('Value Set')
+
+ # remove * Value Set from title
+ title_match = title.match(/(.*) \w+ [Vv]alue [Ss]et/)
+ @title = title_match[1] if title_match && title_match.length > 1
+
+ @description = "#{@description}: #{exact_desc}"
end
-
- private
-
- def extract_negation
- negation = attr_val('./*/@actionNegationInd')
- @negation = (negation=='true')
- if @negation
- @negation_code_list_id = attr_val('./*/cda:reasonCode/cda:item/@valueSet')
- else
- @negation_code_list_id = nil
- end
- end
-
- def extract_child_criteria
- @entry.xpath("./*/cda:outboundRelationship[@typeCode='COMP']/cda:criteriaReference/cda:id", HQMF2::Document::NAMESPACES).collect do |ref|
- Reference.new(ref).id
- end.compact
- end
-
- def extract_effective_time
- effective_time_def = @entry.at_xpath('./*/cda:effectiveTime', HQMF2::Document::NAMESPACES)
- if effective_time_def
- EffectiveTime.new(effective_time_def)
- else
- nil
- end
- end
-
- def all_subset_operators
- @entry.xpath('./*/cda:excerpt', HQMF2::Document::NAMESPACES).collect do |subset_operator|
- SubsetOperator.new(subset_operator)
- end
- end
-
- def extract_derivation_operator
- codes = @entry.xpath("./*/cda:outboundRelationship[@typeCode='COMP']/cda:conjunctionCode/@code", HQMF2::Document::NAMESPACES)
- codes.inject(nil) do | d_op, code |
- raise "More than one derivation operator in data criteria" if d_op && d_op != CONJUNCTION_CODE_TO_DERIVATION_OP[code.value]
- CONJUNCTION_CODE_TO_DERIVATION_OP[code.value]
- end
- end
-
- def extract_subset_operators
- all_subset_operators.select do |operator|
- operator.type != 'UNION' && operator.type != 'XPRODUCT'
- end
- end
-
- def extract_specific_or_source
- specific_def = @entry.at_xpath('./*/cda:outboundRelationship[@typeCode="OCCR"]', HQMF2::Document::NAMESPACES)
- source_def = @entry.at_xpath('./*/cda:outboundRelationship[cda:subsetCode/@code="SOURCE"]', HQMF2::Document::NAMESPACES)
- if specific_def
- @source_data_criteria = HQMF2::Utilities.attr_val(specific_def, './cda:criteriaReference/cda:id/@extension')
- @specific_occurrence_const = HQMF2::Utilities.attr_val(specific_def, './cda:localVariableName/@controlInformationRoot')
- @specific_occurrence = HQMF2::Utilities.attr_val(specific_def, './cda:localVariableName/@controlInformationExtension')
- if !@specific_occurrence
- @specific_occurrence = "A"
- @specific_occurrence_const = @source_data_criteria.upcase
- end
- elsif source_def
- @source_data_criteria = HQMF2::Utilities.attr_val(source_def, './cda:criteriaReference/cda:id/@extension')
- end
- end
-
- def extract_field_values
+ end
+
+ # Holds methods not tied to the data criteria's instance variables
+ class DataCriteriaMethods
+ # Given an entry, and whether or not it's negated, extract out the proper field values for the data criteria.
+ def self.extract_field_values(entry, negation)
fields = {}
# extract most fields which use the same structure
- @entry.xpath('./*/cda:outboundRelationship[*/cda:code]', HQMF2::Document::NAMESPACES).each do |field|
+ entry.xpath('./*/cda:outboundRelationship[*/cda:code]', HQMF2::Document::NAMESPACES).each do |field|
code = HQMF2::Utilities.attr_val(field, './*/cda:code/@code')
code_id = HQMF::DataCriteria::VALUE_FIELDS[code]
- value = DataCriteria.parse_value(field, './*/cda:value')
- fields[code_id] = value if value && code_id
+ # No need to run if there is no code id
+ next if (negation && code_id == 'REASON') || code_id.nil?
+ value = DataCriteriaMethods.parse_value(field, './*/cda:value')
+ value ||= DataCriteriaMethods.parse_value(field, './*/cda:effectiveTime')
+ fields[code_id] = value
end
# special case for facility location which uses a very different structure
- @entry.xpath('./*/cda:outboundRelationship[*/cda:participation]', HQMF2::Document::NAMESPACES).each do |field|
+ entry.xpath('./*/cda:outboundRelationship[*/cda:participation]', HQMF2::Document::NAMESPACES).each do |field|
code = HQMF2::Utilities.attr_val(field, './*/cda:participation/cda:role/@classCode')
code_id = HQMF::DataCriteria::VALUE_FIELDS[code]
+ next if code_id.nil?
value = Coded.new(field.at_xpath('./*/cda:participation/cda:role/cda:code', HQMF2::Document::NAMESPACES))
- fields[code_id] = value if value && code_id
+ fields[code_id] = value
end
+ fields.merge! HQMF2::FieldValueHelper.parse_field_values(entry)
# special case for fulfills operator. assuming there is only a possibility of having one of these
- fulfils = @entry.at_xpath('./*/cda:outboundRelationship[@typeCode="FLFS"]/cda:criteriaReference', HQMF2::Document::NAMESPACES)
- if fulfils
- # grab the child element if we don't have a reference
- fields["FLFS"] = TypedReference.new(fulfils)
- end
+ fulfills = entry.at_xpath('./*/cda:outboundRelationship[@typeCode="FLFS"]/cda:criteriaReference',
+ HQMF2::Document::NAMESPACES)
+ # grab the child element if we don't have a reference
+ fields['FLFS'] = TypedReference.new(fulfills) if fulfills
fields
end
-
- def extract_temporal_references
- @entry.xpath('./*/cda:temporallyRelatedInformation', HQMF2::Document::NAMESPACES).collect do |temporal_reference|
- TemporalReference.new(temporal_reference)
+
+ # Use the new MAT feature to extract the human generated (or computer generated) variable names from the xml.
+ def self.extract_description_for_variable(encoded_name)
+ if encoded_name.match(/^qdm_var_/)
+ # Strip out initial qdm_var_ string, trailing _*, and possible occurrence reference
+ encoded_name.gsub!(/^qdm_var_|/, '')
+ encoded_name.gsub!(/Occurrence[A-Z]of/, '')
+ # This code needs to handle measures created before the MAT added variable name hints; for those, don't strip
+ # the final identifier
+ unless encoded_name.match(/^(SATISFIES ALL|SATISFIES ANY|UNION|INTERSECTION)/)
+ encoded_name.gsub!(/_[^_]+$/, '')
+ end
+ encoded_name
+ elsif encoded_name.match(/^localVar_/)
+ encoded_name.gsub!(/^localVar_/, '')
+ encoded_name
end
end
-
- def extract_value()
- DataCriteria.parse_value(@entry, @value_xpath)
- end
-
+
+ # Parses the value for a given xpath
def self.parse_value(node, xpath)
- value = nil
value_def = node.at_xpath(xpath, HQMF2::Document::NAMESPACES)
if value_def
+ return AnyValue.new if value_def.at_xpath('@flavorId') == 'ANY.NONNULL'
value_type_def = value_def.at_xpath('@xsi:type', HQMF2::Document::NAMESPACES)
- if value_type_def
- value_type = value_type_def.value
- case value_type
- when 'PQ'
- value = Value.new(value_def, 'PQ', true)
- when 'TS'
- value = Value.new(value_def)
- when 'IVL_PQ', 'IVL_INT'
- value = Range.new(value_def)
- when 'CD'
- value = Coded.new(value_def)
- when 'ANY'
- value = AnyValue.new()
- else
- raise "Unknown value type [#{value_type}]"
- end
- end
+ return handle_value_type(value_type_def, value_def) if value_type_def
end
- value
end
-
- def definition_for_demographic
- demographic_type = attr_val('./cda:observationCriteria/cda:code/@code')
- case demographic_type
- when '21112-8'
- "patient_characteristic_birthdate"
- when '424144002'
- "patient_characteristic_age"
- when '263495000'
- "patient_characteristic_gender"
- when '102902016'
- "patient_characteristic_languages"
- when '125680007'
- "patient_characteristic_marital_status"
- when '103579009'
- "patient_characteristic_race"
+
+ # Derives the type associated with a specific value
+ def self.handle_value_type(value_type_def, value_def)
+ value_type = value_type_def.value
+ case value_type
+ when 'PQ'
+ Value.new(value_def, 'PQ', true)
+ when 'TS'
+ Value.new(value_def)
+ when 'IVL_PQ', 'IVL_INT'
+ Range.new(value_def)
+ when 'CD'
+ Coded.new(value_def)
+ when 'ANY', 'IVL_TS'
+ # FIXME: (10/26/2015) IVL_TS should be able to handle other values, not just AnyValue
+ AnyValue.new
else
- raise "Unknown demographic identifier [#{demographic_type}]"
+ fail "Unknown value type [#{value_type}]"
end
-
end
+ # Determine if this instance is a qdm variable
+ def self.extract_variable(local_variable_name, id)
+ variable = (local_variable_name =~ /.*qdm_var_/).present? unless local_variable_name.blank?
+ variable ||= (id =~ /.*qdm_var_/).present? unless id.blank?
+ variable
+ end
end
-
end