class HQMF::Measure::LogicExtractor
Constants
- AGGREGATOR_MAP
- CONJUNCTION_MAP
- FLIP_CONJUNCTION_MAP
- INTERVAL_DEFINITIONS
- INTERVAL_TYPES_DEFINITIONS
- LOGIC_OPERATOR_MAP
- OPERATOR_MAP
- POPULATION_MAP
- SATISFIES_DEFINITIONS
- SET_OPERATOR_MAP
- SUBSET_MAP
- TIMING_MAP
- UNIT_MAP
Public Class Methods
get_measure_logic_diff(measure, other, by_population=false)
click to toggle source
# File lib/measures/logic_extractor.rb, line 483 def self.get_measure_logic_diff(measure, other, by_population=false) return if other.nil? measure_totals = {:total => 0, :deletions => 0, :insertions => 0, :unchanged => 0} unless by_population compute_diff(get_measure_logic_text(measure), get_measure_logic_text(other), measure_totals) else measure_text = get_measure_logic_text(measure, by_population) other_text = get_measure_logic_text(other, by_population) verify_populations(measure_text, other_text) diff = {:cms_id => measure.cms_id, :populations => [], :totals => {}} measure_text.each_with_index do |population, index| population_totals = {:total => 0, :deletions => 0, :insertions => 0, :unchanged => 0} first = population[:lines] second = other_text.at(index)[:lines] diff[:populations] << compute_diff(first, second, population_totals, population[:code]) measure_totals[:total] += population_totals[:total] measure_totals[:deletions] += population_totals[:deletions] measure_totals[:insertions] += population_totals[:insertions] measure_totals[:unchanged] += population_totals[:unchanged] end diff[:totals] = measure_totals diff end end
get_measure_logic_text(measure, by_population=false)
click to toggle source
Diff methods ###
# File lib/measures/logic_extractor.rb, line 458 def self.get_measure_logic_text(measure, by_population=false) return '' if measure.measure_logic.blank? unless by_population lines = '' measure.measure_logic.each do |population| population[:lines].each do |line| lines << "#{line}#{"\n" unless line.ends_with?("\n")}" end end lines else measure_logic_text = [] measure.measure_logic.each do |population| measure_logic = {:code => population[:code], :lines => []} lines = '' population[:lines].each do |line| lines << "#{line}#{"\n" unless line.ends_with?("\n")}" end measure_logic[:lines] = lines measure_logic_text << measure_logic end measure_logic_text end end
Private Class Methods
compute_diff(text1, text2, totals, code='test')
click to toggle source
# File lib/measures/logic_extractor.rb, line 510 def self.compute_diff(text1, text2, totals, code='test') diffs = Diffy::Diff.new(text1, text2, :include_plus_and_minus_in_html => true, :allow_empty_diff => false) # File.write("#{code}.html", "<html>\n<style>#{Diffy::CSS}</style>\n#{diffs.to_s(:html)}\n</html>") results = {:code => code, :lines => []} diffs.each_with_index do |line, ind| case line when /^\+/ totals[:insertions] += 1 results[:lines] << :ins when /^-/ totals[:deletions] += 1 results[:lines] << :del else totals[:unchanged] += 1 results[:lines] << :unchanged end totals[:total] += 1 end results.merge! totals end
verify_populations(measure_text, other_text)
click to toggle source
Make sure we have the same populations in the same order for both measures
# File lib/measures/logic_extractor.rb, line 532 def self.verify_populations(measure_text, other_text) measure_codes = measure_text.map { |p| p[:code] } other_codes = other_text.map { |p| p[:code] } # First add any missing codes to each (measure_codes - other_codes).each { |code| other_text << { code: code, lines: [] } } (other_codes - measure_codes).each { |code| measure_text << { code: code, lines: [] } } # Then sort each canonically, allowing for NUMER, NUMER_1, etc sorter = proc do |p| if match = p[:code].match(/(.*)_(\d+)/) [HQMF::PopulationCriteria::ALL_POPULATION_CODES.index(match[1]) || Float::INFINITY, match[2].to_i] else [HQMF::PopulationCriteria::ALL_POPULATION_CODES.index(p[:code]) || Float::INFINITY, 0] end end measure_text.sort_by!(&sorter) other_text.sort_by!(&sorter) end
Public Instance Methods
data_criteria_logic(reference, expand_variable=false, hide_title=nil, indent=nil)
click to toggle source
# File lib/measures/logic_extractor.rb, line 230 def data_criteria_logic(reference, expand_variable=false, hide_title=nil, indent=nil) results = [] indent ||= "" data_criteria = @measure.data_criteria[reference] unless data_criteria data_criteria = @measure.source_data_criteria[reference] end data_criteria['key'] ||= reference if !data_criteria['field_values'].blank? data_criteria['field_values'].each do |key, field| if field.blank? field = {} data_criteria['field_values'][key] = field end field['key'] = key field['key_title'] = translate_field(key) end end # handle field values on data_criteria is_satisfies = SATISFIES_DEFINITIONS.include?(data_criteria['definition']) is_derived = data_criteria['type'].to_s == 'derived' has_children = is_derived && (!data_criteria['variable'] || expand_variable) is_set_op = SET_OPERATOR_MAP.keys.include?(data_criteria['derivation_operator']) if data_criteria['subset_operators'] data_criteria['subset_operators'].each do |so| results.concat subset_operator_logic(so) end end if has_children if is_satisfies results.concat satisfies_logic(data_criteria['key'], indent+"\t") else if data_criteria['children_criteria'] if is_set_op unless expand_variable results << "\n#{indent+"\t\t"}#{translate_set_operator(data_criteria['derivation_operator'])}:" end end line = "#{indent}" data_criteria['children_criteria'].each_with_index do |cc, cc_ind| unless is_set_op line << "#{translate_logic_operator(data_criteria['derivation_operator'])} : " end data_criteria_logic(cc, false, nil, indent+"\t").each_with_index do |dc, dc_ind| results << "#{line}\t#{dc}" end end if data_criteria['temporal_references'] data_criteria['temporal_references'].each do |tr| results << "#{indent+"\t\t"}#{temporal_reference_logic(tr).first}" end end end end else line = "#{indent}" line = "" if hide_title && !results.blank? unless hide_title if data_criteria['specific_occurrence'] line << "Occurrence #{data_criteria['specific_occurrence']}: " end line << "$" if data_criteria['variable'] line << data_criteria['description'] end if data_criteria['value'] unless data_criteria['type'].to_s == 'characteristic' line << "(result#{value_logic(data_criteria['value'])[0]})" end end if data_criteria['field_values'] line << " ( " data_criteria['field_values'].each do |field, fv| line << fv['key_title'] if fv['key_title'] line << "#{value_logic(fv)[0]}" if fv['type'] != 'ANYNonNull' end line << " )" end if data_criteria['negation'] line << " ( Not Done : #{translate_oid(data_criteria['negation_code_list_id'])} )" end results << line if data_criteria['temporal_references'] if data_criteria['temporal_references'].length > 1 data_criteria['temporal_references'].each do |tr| results << "#{indent}#{temporal_reference_logic(tr)}" end else data_criteria['temporal_references'].each do |tr| results << temporal_reference_logic(tr).first end end end end results.last << "\n" unless results.last.end_with?("\n") results end
population_criteria_logic(population)
click to toggle source
# File lib/measures/logic_extractor.rb, line 334 def population_criteria_logic(population) results = [] root_precondition = population['preconditions'][0] if population['preconditions'] aggregator = population['aggregator'] ( comments = root_precondition.try(:[],'comments') || [] ) | ( population['comments'] || [] ) results.concat comments if comments unless root_precondition.blank? if root_precondition['preconditions'] root_precondition['preconditions'].each do |precondition| results.concat precondition_logic(precondition, root_precondition, root_precondition['negation'] || false) end else unless aggregator.blank? results << "\t#{translate_aggregator(aggregator)}\n" end results.concat data_criteria_logic(root_precondition['reference']) end else results << "\tNone\n" end results end
population_logic(measure)
click to toggle source
# File lib/measures/logic_extractor.rb, line 359 def population_logic(measure) results = [] @measure = measure populations = @measure.population_criteria.keys populations.each do |population| population_results = {:code => population, :lines => []} population_results[:lines] << "\n#{translate_population(population)}\n" population_results[:lines].concat population_criteria_logic(@measure.population_criteria[population]) results << population_results end variables_text = variables_logic unless variables_text.blank? variable_results = {:code => "VARIABLES", :lines => variables_text } results << variable_results end results end
precondition_logic(precondition, parent_precondition=nil, parent_negation=false, indent=nil)
click to toggle source
# File lib/measures/logic_extractor.rb, line 95 def precondition_logic(precondition, parent_precondition=nil, parent_negation=false, indent=nil) results = [] precondition_key = "precondition_#{precondition['id']}" parent_preocondition_key = "precondition_#{parent_precondition['id']}" conjunction = translate_conjunction(parent_precondition['conjunction_code']) suppress = true if precondition['negation'] && precondition['preconditions'] && precondition['preconditions'].length == 1 conjunction = FLIP_CONJUNCTION_MAP[conjunction] if parent_negation comments = precondition['comments'] || [] if precondition['reference'] data_criteria = @measure['data_criteria'][precondition['reference']] comments.concat data_criteria['comments'] || [] end indent ||= "" indent += "\t" line = "" unless suppress if comments results.concat comments end line << "#{indent}#{conjunction}" line << " NOT" if parent_negation line << ":" results << line end if precondition['preconditions'] results.last << "\n" unless results.blank? precondition['preconditions'].each do |p| results.concat precondition_logic(p, precondition, precondition['negation'], indent) end else results.last << " " results.concat data_criteria_logic(precondition['reference']) end results end
satisfies_logic(reference, indent=nil)
click to toggle source
# File lib/measures/logic_extractor.rb, line 190 def satisfies_logic(reference, indent=nil) results = [] indent ||= "" data_criteria = @measure.data_criteria[reference] root_criteria = @measure.data_criteria[data_criteria['children_criteria'][0]] line = "" line << "Occurrence #{root_criteria['specific_occurrence']}:" if root_criteria['specific_occurrence'] line << "$" if root_criteria['variable'] line << "#{root_criteria['description']} #{translate_operator(data_criteria['definition'])}\n" results << line data_criteria['children_criteria'].each do |cc| results << "#{data_criteria_logic(cc, false, true, indent+"\t").join('')}" end if data_criteria['temporal_references'] data_criteria['temporal_references'].each do |tr| results << "#{indent+"\t"}#{temporal_reference_logic(tr).join()}" end end results end
subset_operator_logic(subset_operator)
click to toggle source
# File lib/measures/logic_extractor.rb, line 133 def subset_operator_logic(subset_operator) results = [] line = "#{translate_subset(subset_operator['type'])}" if subset_operator['value'] unless subset_operator['value']['type'].to_s == 'ANYNonNull' line << "#{value_logic(subset_operator['value'])[0]}" end end line << ": " results << line end
temporal_reference_logic(temporal_reference)
click to toggle source
# File lib/measures/logic_extractor.rb, line 215 def temporal_reference_logic(temporal_reference) results = [] line = "" line << "#{value_logic(temporal_reference['range'])[0]}" if temporal_reference['range'] line << " #{translate_timing(temporal_reference['type'])} " if temporal_reference['reference'].to_s == "MeasurePeriod" line << "\"Measurement Period\"" else line << "#{data_criteria_logic(temporal_reference['reference']).join()}" end results << line end
translate_aggregator(code)
click to toggle source
# File lib/measures/logic_extractor.rb, line 404 def translate_aggregator(code) AGGREGATOR_MAP[code] end
translate_conjunction(conjunction)
click to toggle source
# File lib/measures/logic_extractor.rb, line 452 def translate_conjunction(conjunction) CONJUNCTION_MAP[conjunction] end
translate_date(date)
click to toggle source
# File lib/measures/logic_extractor.rb, line 448 def translate_date(date) date end
translate_field(field_key)
click to toggle source
# File lib/measures/logic_extractor.rb, line 416 def translate_field(field_key) HQMF::DataCriteria::FIELDS[field_key][:title] end
translate_logic_operator(conjunction)
click to toggle source
# File lib/measures/logic_extractor.rb, line 408 def translate_logic_operator(conjunction) LOGIC_OPERATOR_MAP[conjunction] end
translate_oid(oid)
click to toggle source
# File lib/measures/logic_extractor.rb, line 440 def translate_oid(oid) begin @measure.value_sets.where({:oid => oid}).first.display_name rescue oid end end
translate_operator(definition)
click to toggle source
# File lib/measures/logic_extractor.rb, line 424 def translate_operator(definition) OPERATOR_MAP[definition] end
translate_population(code)
click to toggle source
# File lib/measures/logic_extractor.rb, line 396 def translate_population(code) if match = code.match(/(.*)_(\d+)/) "#{POPULATION_MAP[match[1]]} #{match[2].to_i + 1}" else POPULATION_MAP[code] end end
translate_set_operator(conjunction)
click to toggle source
# File lib/measures/logic_extractor.rb, line 412 def translate_set_operator(conjunction) SET_OPERATOR_MAP[conjunction] end
translate_subset(subset)
click to toggle source
# File lib/measures/logic_extractor.rb, line 420 def translate_subset(subset) SUBSET_MAP[subset] end
translate_timing(code)
click to toggle source
# File lib/measures/logic_extractor.rb, line 428 def translate_timing(code) TIMING_MAP[code].downcase end
translate_unit(unit, value)
click to toggle source
# File lib/measures/logic_extractor.rb, line 432 def translate_unit(unit, value) if UNIT_MAP[unit] UNIT_MAP[unit] + ( value.to_i > 1 ? 's' : '' ) else unit end end
value_logic(value, range_comparison=nil)
click to toggle source
# File lib/measures/logic_extractor.rb, line 145 def value_logic(value, range_comparison=nil) results = [] is_range = INTERVAL_DEFINITIONS.include?(value['type']) is_equivalent = is_range && value['high'] && value['low'] && (value['high']['value'] == value['low']['value']) && value['high']['inclusive?'] && value['low']['inclusive?'] is_value = INTERVAL_TYPES_DEFINITIONS.include?(value['type']) is_any_non_null = value['type'].to_s == 'ANYNonNull' is_ts = value['type'].to_s == 'TS' line = "" unless is_any_non_null if is_value line << "#{range_comparison || ''}" line << "=" if value['inclusive?'] if is_ts line << translate_date(value['value']) else line << " #{value['value']}" end line << " #{translate_unit(value['unit'], value['value'])}" else if is_range if value['high'] && value['low'] if is_equivalent line = value_logic(value['low'])[0] else line = "#{value_logic(value['low'], '>')[0]} and #{value_logic(value['high'], '<')[0]}" end else if value['high'] line = " #{value_logic(value['high'], '<')[0]}" else if value['low'] line = " #{value_logic(value['low'], '>')[0]}" end end end else line << ": #{translate_oid(value['code_list_id'])}" if value['type'].to_s == 'CD' end end end results << line end
variables_logic()
click to toggle source
# File lib/measures/logic_extractor.rb, line 379 def variables_logic results = [] variables = @measure['source_data_criteria'].select{ |key, attrs| attrs['variable'] == true } has_variables = variables.length > 0 if has_variables results << "\nVariables\n" variables.each do |title, v| results << "\t$#{v['description']} = \n" results.concat data_criteria_logic(v['source_data_criteria'], true, nil, "\t") end end results end