Module: CqmValidators::ReportedResultExtractor

Included in:
PerformanceRateValidator
Defined in:
lib/reported_result_extractor.rb

Constant Summary collapse

ALL_POPULATION_CODES =

takes a document and a list of 1 or more id hashes, e.g.: [set_id:“03876d69-085b-415c-ae9d-9924171040c2”, ipp:“D77106C4-8ED0-4C5D-B29E-13DBF255B9FF”, den:“8B0FA80F-8FFE-494C-958A-191C1BB36DBF”, num:“9363135E-A816-451F-8022-96CDA7E540DD”] returns nil if nothing matching is found returns a hash with the values of the populations filled out along with the population_ids added to the result

%w[IPP DENOM NUMER NUMEX DENEX DENEXCEP MSRPOPL MSRPOPLEX].freeze

Instance Method Summary collapse

Instance Method Details

#convert_value(value_node) ⇒ Object

convert numbers in value nodes to Int / Float as necessary TODO: add more types other than ‘REAL’



122
123
124
125
126
127
# File 'lib/reported_result_extractor.rb', line 122

def convert_value(value_node)
  return if value_node.nil?
  return value_node['value'].to_f if value_node['type'] == 'REAL' || value_node['value'].include?('.')

  value_node['value'].to_i
end

#extract_component_value(node, code, id, strata = nil) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/reported_result_extractor.rb', line 86

def extract_component_value(node, code, id, strata = nil)
  code = 'IPOP' if code == 'IPP'
  xpath_observation = %( cda:component/cda:observation[./cda:value[@code = "#{code}"] and ./cda:reference/cda:externalObservation/cda:id[#{translate('@root')}='#{id.upcase}']])
  cv = node.at_xpath(xpath_observation)
  return nil unless cv

  val = nil
  if strata
    strata_path = %( cda:entryRelationship[@typeCode="COMP"]/cda:observation[./cda:templateId[@root = "2.16.840.1.113883.10.20.27.3.4"]  and ./cda:reference/cda:externalObservation/cda:id[#{translate('@root')}='#{strata.upcase}']])
    n = cv.xpath(strata_path)
    val = get_aggregate_count(n) if n
  else
    val = get_aggregate_count(cv)
  end
  # Performance rate is only applicable for unstratified values
  pref_rate_value = extract_performance_rate(node, code, id) if code == 'NUMER' && strata.nil?
  [val, (strata.nil? ? extract_supplemental_data(cv) : nil), pref_rate_value]
end

#extract_cv_value(node, id, msrpopl, pop_code, strata = nil) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/reported_result_extractor.rb', line 70

def extract_cv_value(node, id, msrpopl, pop_code, strata = nil)
  xpath_observation = %( cda:component/cda:observation[./cda:value[@code = "#{pop_code}"] and ./cda:reference/cda:externalObservation/cda:id[#{translate('@root')}='#{msrpopl.upcase}']])
  cv = node.at_xpath(xpath_observation)
  return nil unless cv

  val = nil
  if strata
    strata_path = %( cda:entryRelationship[@typeCode="COMP"]/cda:observation[./cda:templateId[@root = "2.16.840.1.113883.10.20.27.3.4"]  and ./cda:reference/cda:externalObservation/cda:id[#{translate('@root')}='#{strata.upcase}']])
    n = cv.xpath(strata_path)
    val = get_cv_value(n, id)
  else
    val = get_cv_value(cv, id)
  end
  val
end

#extract_performance_rate(node, _code, id) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/reported_result_extractor.rb', line 105

def extract_performance_rate(node, _code, id)
  xpath_perf_rate = %( cda:component/cda:observation[./cda:templateId[@root = "2.16.840.1.113883.10.20.27.3.14"] and ./cda:reference/cda:externalObservation/cda:id[#{translate('@root')}='#{id.upcase}']]/cda:value)
  perf_rate = node.at_xpath(xpath_perf_rate)
  pref_rate_value = {}
  unless perf_rate.nil?
    if perf_rate.at_xpath('./@nullFlavor')
      pref_rate_value['nullFlavor'] = 'NA'
    else
      pref_rate_value['value'] = perf_rate.at_xpath('./@value').value
    end
    return pref_rate_value
  end
  nil
end

#extract_results_by_ids(measure, poulation_set_id, doc, stratification_id = nil) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/reported_result_extractor.rb', line 13

def extract_results_by_ids(measure, poulation_set_id, doc, stratification_id = nil)
  results = nil
  nodes = find_measure_node(measure.hqmf_id, doc)

  if nodes.nil? || nodes.empty?
    # short circuit and return nil
    return {}
  end

  nodes.each do |n|
    popset_index = measure.population_sets.map(&:population_set_id).find_index do |pop_set|
      pop_set == poulation_set_id
    end
    results = get_measure_components(n, measure.population_sets.where(population_set_id: poulation_set_id).first, stratification_id, popset_index)
    break if !results.nil? || (!results.nil? && !results.empty?)
  end
  return nil if results.nil?

  results
end

#extract_supplemental_data(cv) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/reported_result_extractor.rb', line 146

def extract_supplemental_data(cv)
  ret = {}
  supplemental_data_mapping = { 'RACE' => '2.16.840.1.113883.10.20.27.3.8',
                                'ETHNICITY' => '2.16.840.1.113883.10.20.27.3.7',
                                'SEX' => '2.16.840.1.113883.10.20.27.3.6',
                                'PAYER' => '2.16.840.1.113883.10.20.27.3.9' }
  supplemental_data_mapping.each_pair do |supp, id|
    key_hash = {}
    xpath = "cda:entryRelationship/cda:observation[cda:templateId[@root='#{id}']]"
    (cv.xpath(xpath) || []).each do |node|
      value = node.at_xpath('cda:value')
      count = get_aggregate_count(node)
      if value.at_xpath('./@nullFlavor')
        if supp == 'PAYER' && value['xsi:type'] == 'CD' && value['nullFlavor'] == 'OTH' && value.at_xpath('cda:translation') && value.at_xpath('cda:translation')['code']
          key_hash[value.at_xpath('cda:translation')['code']] = count
        else
          key_hash['UNK'] = count
        end
      else
        key_hash[value['code']] = count
      end
    end
    ret[supp.to_s] = key_hash
  end
  ret
end

#find_measure_node(id, doc) ⇒ Object



34
35
36
37
38
39
# File 'lib/reported_result_extractor.rb', line 34

def find_measure_node(id, doc)
  xpath_measures = %(/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section
   /cda:entry/cda:organizer[ ./cda:templateId[@root = "2.16.840.1.113883.10.20.27.3.1"]
   and ./cda:reference/cda:externalDocument/cda:id[#{translate('@extension')}='#{id.upcase}' and #{translate('@root')}='2.16.840.1.113883.4.738']])
  doc.xpath(xpath_measures)
end

#get_aggregate_count(node) ⇒ Object

given an observation node with an aggregate count node, return the reported and expected value within the count node



139
140
141
142
143
144
# File 'lib/reported_result_extractor.rb', line 139

def get_aggregate_count(node)
  xpath_value = 'cda:entryRelationship/cda:observation[./cda:templateId[@root="2.16.840.1.113883.10.20.27.3.3"]]/cda:value'
  value_node = node.at_xpath(xpath_value)
  value = convert_value(value_node) if value_node
  value
end

#get_cv_value(node, cv_id) ⇒ Object

given an observation node with an aggregate count node, return the reported and expected value within the count node



130
131
132
133
134
135
136
# File 'lib/reported_result_extractor.rb', line 130

def get_cv_value(node, cv_id)
  xpath_value = %(cda:entryRelationship/cda:observation[./cda:templateId[@root="2.16.840.1.113883.10.20.27.3.2"] and ./cda:reference/cda:externalObservation/cda:id[#{translate('@root')}='#{cv_id.upcase}']]/cda:value)

  value_node = node.at_xpath(xpath_value)
  value = convert_value(value_node) if value_node
  value
end

#get_measure_components(n, population_set, stratification_id, popset_index) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/reported_result_extractor.rb', line 41

def get_measure_components(n, population_set, stratification_id, popset_index)
  # observations are a hash of population/value. For example {"DENOM"=>108.0, "NUMER"=>2}
  results = { supplemental_data: {}, observations: {} }
  stratification = stratification_id ? population_set.stratifications.where(stratification_id: stratification_id).first.hqmf_id : nil
  ALL_POPULATION_CODES.each do |pop_code|
    next unless population_set.populations[pop_code]

    get_observed_values(results, n, pop_code, population_set, stratification, popset_index)
    val, sup, pr = extract_component_value(n, pop_code, population_set.populations[pop_code]['hqmf_id'], stratification)
    unless val.nil?
      results[pop_code] = val
      results[:supplemental_data][pop_code] = sup
    end
    results['PR'] = pr unless pr.nil?
  end
  results
end

#get_observed_values(results, n, pop_code, population_set, stratification, popset_index) ⇒ Object



59
60
61
62
63
64
65
66
67
68
# File 'lib/reported_result_extractor.rb', line 59

def get_observed_values(results, n, pop_code, population_set, stratification, popset_index)
  statement_name = population_set.populations[pop_code]['statement_name']
  # look to see if there is an observation that corresponds to the specific statement_name
  statement_observation = population_set.observations.select { |obs| obs.observation_parameter.statement_name == statement_name }[popset_index]
  # return unless an observation is found
  unless statement_observation.nil?
    hqmf_id = population_set.populations[pop_code]['hqmf_id']
    results[:observations][pop_code] = extract_cv_value(n, statement_observation.hqmf_id, hqmf_id, pop_code, stratification)
  end
end

#translate(id) ⇒ Object



173
174
175
# File 'lib/reported_result_extractor.rb', line 173

def translate(id)
  %{translate(#{id}, "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")}
end