class HQMF2::Document

Class representing an HQMF document

Constants

NAMESPACES

Attributes

attributes[R]
hqmf_set_id[R]
hqmf_version_number[R]
id[R]
measure_period[R]
populations[R]
source_data_criteria[R]

Public Class Methods

new(hqmf_contents, use_default_measure_period = true) click to toggle source

Create a new HQMF2::Document instance by parsing the given HQMF contents @param [String] containing the HQMF contents to be parsed

# File lib/hqmf-parser/2.0/document.rb, line 14
def initialize(hqmf_contents, use_default_measure_period = true)
  setup_default_values(hqmf_contents, use_default_measure_period)

  extract_criteria

  # Extract the population criteria and population collections
  pop_helper = HQMF2::DocumentPopulationHelper.new(@entry, @doc, self, @id_generator, @reference_ids)
  @populations, @population_criteria = pop_helper.extract_populations_and_criteria

  # Remove any data criteria from the main data criteria list that already has an equivalent member
  #  and no references to it. The goal of this is to remove any data criteria that should not
  #  be purely a source.
  @data_criteria.reject! do |dc|
    criteria_covered_by_criteria?(dc)
  end
end
parse(hqmf_contents) click to toggle source

Parse an XML document from the supplied contents @return [Nokogiri::XML::Document]

# File lib/hqmf-parser/2.0/document.rb, line 94
def self.parse(hqmf_contents)
  doc = hqmf_contents.is_a?(Nokogiri::XML::Document) ? hqmf_contents : Nokogiri::XML(hqmf_contents)
  doc.root.add_namespace_definition('cda', 'urn:hl7-org:v3')
  doc
end

Public Instance Methods

add_data_criteria(dc) click to toggle source

Adds data criteria to the Document's criteria list needed so data criteria can be added to a document from other objects

# File lib/hqmf-parser/2.0/document.rb, line 72
def add_data_criteria(dc)
  @data_criteria << dc
end
add_reference_id(id) click to toggle source

Adds id of a data criteria to the list of reference ids

# File lib/hqmf-parser/2.0/document.rb, line 88
def add_reference_id(id)
  @reference_ids << id
end
all_data_criteria() click to toggle source

Get all the data criteria defined by the measure @return [Array] an array of HQMF2::DataCriteria describing the data elements used by the measure

# File lib/hqmf-parser/2.0/document.rb, line 59
def all_data_criteria
  @data_criteria
end
all_population_criteria() click to toggle source

Get all the population criteria defined by the measure @return [Array] an array of HQMF2::PopulationCriteria

# File lib/hqmf-parser/2.0/document.rb, line 46
def all_population_criteria
  @population_criteria
end
all_reference_ids() click to toggle source

Get ids of data criteria directly referenced by others @return [Array] an array of ids of directly referenced data criteria

# File lib/hqmf-parser/2.0/document.rb, line 83
def all_reference_ids
  @reference_ids
end
data_criteria(id) click to toggle source

Get a specific data criteria by id. @param [String] id the data criteria identifier @return [HQMF2::DataCriteria] the matching data criteria, raises an Exception if not found

# File lib/hqmf-parser/2.0/document.rb, line 66
def data_criteria(id)
  find(@data_criteria, :id, id)
end
description() click to toggle source

Get the description of the measure @return [String] the description

# File lib/hqmf-parser/2.0/document.rb, line 39
def description
  description = @doc.at_xpath('cda:QualityMeasureDocument/cda:text/@value', NAMESPACES)
  description.nil? ? '' : description.inner_text
end
find(collection, attribute, value) click to toggle source

Finds an element within the collection given that has an instance variable or method of “attribute” with a value of “value”

# File lib/hqmf-parser/2.0/document.rb, line 112
def find(collection, attribute, value)
  collection.find { |e| e.send(attribute) == value }
end
find_criteria_by_lvn(local_variable_name) click to toggle source

Finds a data criteria by it's local variable name

# File lib/hqmf-parser/2.0/document.rb, line 77
def find_criteria_by_lvn(local_variable_name)
  find(@data_criteria, :local_variable_name, local_variable_name)
end
population_criteria(id) click to toggle source

Get a specific population criteria by id. @param [String] id the population identifier @return [HQMF2::PopulationCriteria] the matching criteria, raises an Exception if not found

# File lib/hqmf-parser/2.0/document.rb, line 53
def population_criteria(id)
  find(@population_criteria, :id, id)
end
title() click to toggle source

Get the title of the measure @return [String] the title

# File lib/hqmf-parser/2.0/document.rb, line 33
def title
  @doc.at_xpath('cda:QualityMeasureDocument/cda:title/@value', NAMESPACES).inner_text
end
to_model() click to toggle source

Generates this classes hqmf-model equivalent

# File lib/hqmf-parser/2.0/document.rb, line 101
def to_model
  dcs = all_data_criteria.collect(&:to_model)
  pcs = all_population_criteria.collect(&:to_model)
  sdc = source_data_criteria.collect(&:to_model)
  HQMF::Document.new(@id, @id, @hqmf_set_id, @hqmf_version_number, @cms_id,
                     title, description, pcs, dcs, sdc,
                     @attributes, @measure_period, @populations)
end

Private Instance Methods

extract_criteria() click to toggle source
# File lib/hqmf-parser/2.0/document.rb, line 238
def extract_criteria
  # Extract the data criteria
  extracted_criteria = []
  @doc.xpath('cda:QualityMeasureDocument/cda:component/cda:dataCriteriaSection/cda:entry', NAMESPACES)
    .each do |entry|
    extracted_criteria << entry
  end

  # Extract the source data criteria from data criteria
  @source_data_criteria, collapsed_source_data_criteria = SourceDataCriteriaHelper.get_source_data_criteria_list(
    extracted_criteria, @data_criteria_references, @occurrences_map)

  extracted_criteria.each do |entry|
    criteria = DataCriteria.new(entry, @data_criteria_references, @occurrences_map)
    handle_data_criteria(criteria, collapsed_source_data_criteria)
    @data_criteria << criteria
  end
end
extract_measure_period_or_default(default) click to toggle source

Extracts a measure period from the document or returns the default measure period

(if the default value is set to true).
# File lib/hqmf-parser/2.0/document.rb, line 154
def extract_measure_period_or_default(default)
  if default
    mp_low = HQMF::Value.new('TS', nil, '201201010000', nil, nil, nil)
    mp_high = HQMF::Value.new('TS', nil, '201212312359', nil, nil, nil)
    mp_width = HQMF::Value.new('PQ', 'a', '1', nil, nil, nil)
    HQMF::EffectiveTime.new(mp_low, mp_high, mp_width)
  else
    measure_period_def = @doc.at_xpath('cda:QualityMeasureDocument/cda:controlVariable/cda:measurePeriod/cda:value',
                                       NAMESPACES)
    EffectiveTime.new(measure_period_def).to_model if measure_period_def
  end
end
handle_attribute_code(attribute, code, name) click to toggle source

Extracts the code used by a particular attribute

# File lib/hqmf-parser/2.0/document.rb, line 201
def handle_attribute_code(attribute, code, name)
  null_flavor = attribute.at_xpath('./cda:code/@nullFlavor', NAMESPACES).try(:value)
  o_text = attribute.at_xpath('./cda:code/cda:originalText/@value', NAMESPACES).try(:value)
  code_obj = HQMF::Coded.new(attribute.at_xpath('./cda:code/@xsi:type', NAMESPACES).try(:value) || 'CD',
                             attribute.at_xpath('./cda:code/@codeSystem', NAMESPACES).try(:value),
                             code,
                             attribute.at_xpath('./cda:code/@valueSet', NAMESPACES).try(:value),
                             name,
                             null_flavor,
                             o_text)
  [code_obj, null_flavor, o_text]
end
handle_attribute_value(attribute, value) click to toggle source

Extracts the value used by a particular attribute

# File lib/hqmf-parser/2.0/document.rb, line 215
def handle_attribute_value(attribute, value)
  type = attribute.at_xpath('./cda:value/@xsi:type', NAMESPACES).try(:value)
  case type
  when 'II'
    if value.nil?
      value = attribute.at_xpath('./cda:value/@extension', NAMESPACES).try(:value)
    end
    HQMF::Identifier.new(type,
                         attribute.at_xpath('./cda:value/@root', NAMESPACES).try(:value),
                         attribute.at_xpath('./cda:value/@extension', NAMESPACES).try(:value))
  when 'ED'
    HQMF::ED.new(type, value, attribute.at_xpath('./cda:value/@mediaType', NAMESPACES).try(:value))
  when 'CD'
    HQMF::Coded.new('CD',
                    attribute.at_xpath('./cda:value/@codeSystem', NAMESPACES).try(:value),
                    attribute.at_xpath('./cda:value/@code', NAMESPACES).try(:value),
                    attribute.at_xpath('./cda:value/@valueSet', NAMESPACES).try(:value),
                    attribute.at_xpath('./cda:value/cda:displayName/@value', NAMESPACES).try(:value))
  else
    value.present? ? HQMF::GenericValueContainer.new(type, value) : HQMF::AnyValue.new(type)
  end
end
handle_data_criteria(criteria, collapsed_source_data_criteria) click to toggle source
# File lib/hqmf-parser/2.0/document.rb, line 257
def handle_data_criteria(criteria, collapsed_source_data_criteria)
  # Sometimes there are multiple criteria with the same ID, even though they're different; in the HQMF
  # criteria refer to parent criteria via outboundRelationship, using an extension (aka ID) and a root;
  # we use just the extension to follow the reference, and build the lookup hash using that; since they
  # can repeat, we wind up overwriting some content. This becomes important when we want, for example,
  # the code_list_id and we overwrite the parent with the code_list_id with a child with the same ID
  # without the code_list_id. As a temporary approach, we only overwrite a data criteria reference if
  # it doesn't have a code_list_id. As a longer term approach we may want to use the root for lookups.
  if criteria && (@data_criteria_references[criteria.id].try(:code_list_id).nil?)
    @data_criteria_references[criteria.id] = criteria
  end
  if collapsed_source_data_criteria.key?(criteria.id)
    candidate = find(all_data_criteria, :id, collapsed_source_data_criteria[criteria.id])
    # derived criteria should not be collapsed... they do not have enough info to be collapsed and may cross into the wrong criteria
    # only add the collapsed as a source for derived if it is stripped of any temporal references, fields, etc. to make sure we don't cross into an incorrect source
    if ((criteria.definition != 'derived') || (!candidate.nil? && SourceDataCriteriaHelper.already_stripped?(candidate)))
      criteria.instance_variable_set(:@source_data_criteria, collapsed_source_data_criteria[criteria.id])
    end
  end

  handle_variable(criteria, collapsed_source_data_criteria) if criteria.variable
  handle_specific_source_data_criteria_reference(criteria)
  @reference_ids.concat(criteria.children_criteria)
  if criteria.temporal_references
    criteria.temporal_references.each do |tr|
      @reference_ids << tr.reference.id if tr.reference.id != HQMF::Document::MEASURE_PERIOD_ID
    end
  end
end
handle_specific_source_data_criteria_reference(criteria) click to toggle source

For specific occurrence data criteria, make sure the source data criteria reference points to the correct source data criteria.

# File lib/hqmf-parser/2.0/document.rb, line 289
def handle_specific_source_data_criteria_reference(criteria)
  original_sdc = find(@source_data_criteria, :id, criteria.source_data_criteria)
  updated_sdc = find(@source_data_criteria, :id, criteria.id)
  if !updated_sdc.nil? && !criteria.specific_occurrence.nil? && (original_sdc.nil? || original_sdc.specific_occurrence.nil?)
    criteria.instance_variable_set(:@source_data_criteria, criteria.id)
  end
  return if original_sdc.nil?
  if (criteria.specific_occurrence && !original_sdc.specific_occurrence)
    original_sdc.instance_variable_set(:@specific_occurrence, criteria.specific_occurrence)
    original_sdc.instance_variable_set(:@specific_occurrence_const, criteria.specific_occurrence_const)
    original_sdc.instance_variable_set(:@code_list_id, criteria.code_list_id)
  end
end
read_attribute(attribute) click to toggle source

Handles parsing the attributes of the document

# File lib/hqmf-parser/2.0/document.rb, line 168
def read_attribute(attribute)
  id = attribute.at_xpath('./cda:id/@root', NAMESPACES).try(:value)
  code = attribute.at_xpath('./cda:code/@code', NAMESPACES).try(:value)
  name = attribute.at_xpath('./cda:code/cda:displayName/@value', NAMESPACES).try(:value)
  value = attribute.at_xpath('./cda:value/@value', NAMESPACES).try(:value)

  id_obj = nil
  if attribute.at_xpath('./cda:id', NAMESPACES)
    id_obj = HQMF::Identifier.new(attribute.at_xpath('./cda:id/@xsi:type', NAMESPACES).try(:value),
                                  id,
                                  attribute.at_xpath('./cda:id/@extension', NAMESPACES).try(:value))
  end

  code_obj = nil
  if attribute.at_xpath('./cda:code', NAMESPACES)
    code_obj, null_flavor, o_text = handle_attribute_code(attribute, code, name)

    # Mapping for nil values to align with 1.0 parsing
    code = null_flavor if code.nil?
    name = o_text if name.nil?

  end

  value_obj = nil
  value_obj = handle_attribute_value(attribute, value) if attribute.at_xpath('./cda:value', NAMESPACES)

  # Handle the cms_id - changed to eCQM in MAT 5.4 (QDM 5.3)
  @cms_id = "CMS#{value}v#{@hqmf_version_number.to_i}" if (!name.nil?) && ((name.include? 'eMeasure Identifier') || (name.include? 'eCQM Identifier'))

  HQMF::Attribute.new(id, code, value, nil, name, id_obj, code_obj, value_obj)
end
setup_default_values(hqmf_contents, use_default_measure_period) click to toggle source

Handles setup of the base values of the document, defined here as ones that are either

obtained from the xml directly or with limited parsing
# File lib/hqmf-parser/2.0/document.rb, line 120
def setup_default_values(hqmf_contents, use_default_measure_period)
  @id_generator = IdGenerator.new
  @doc = @entry = Document.parse(hqmf_contents)

  @id = attr_val('cda:QualityMeasureDocument/cda:id/@extension') ||
        attr_val('cda:QualityMeasureDocument/cda:id/@root').upcase
  @hqmf_set_id = attr_val('cda:QualityMeasureDocument/cda:setId/@extension') ||
                 attr_val('cda:QualityMeasureDocument/cda:setId/@root').upcase
  @hqmf_version_number = attr_val('cda:QualityMeasureDocument/cda:versionNumber/@value')

  # TODO: -- figure out if this is the correct thing to do -- probably not, but is
  # necessary to get the bonnie comparison to work.  Currently
  # defaulting measure period to a period of 1 year from 2012 to 2013 this is overriden during
  # calculation with correct year information .  Need to investigate parsing mp from meaures.
  @measure_period = extract_measure_period_or_default(use_default_measure_period)

  # Extract measure attributes
  # TODO: Review
  @attributes = @doc.xpath('/cda:QualityMeasureDocument/cda:subjectOf/cda:measureAttribute', NAMESPACES)
                .collect do |attribute|
    read_attribute(attribute)
  end

  @data_criteria = []
  @source_data_criteria = []
  @data_criteria_references = {}
  @occurrences_map = {}

  # Used to keep track of referenced data criteria ids
  @reference_ids = []
end