lib/measure_validator.rb in cqm-validators-0.1.1 vs lib/measure_validator.rb in cqm-validators-1.0.1.0

- old
+ new

@@ -1,130 +1,127 @@ +# frozen_string_literal: true + module CqmValidators - class MeasureValidator - include BaseValidator - - def initialize(template_oid) - @template_oid = template_oid - - end - - def validate(file, data={}) + class MeasureValidator + include BaseValidator + + def initialize(template_oid) + @template_oid = template_oid + end + + def validate(file, data = {}) @errors = [] @doc = get_document(file) @doc.root.add_namespace_definition('cda', 'urn:hl7-org:v3') - measure_ids = HealthDataStandards::CQM::Measure.all.map(&:hqmf_id) + measure_ids = CQM::Measure.all.map(&:hqmf_id) doc_measure_ids = @doc.xpath(measure_selector).map(&:value).map(&:upcase) - #list of all of the set ids in the QRDA + # list of all of the set ids in the QRDA doc_neutral_ids = @doc.xpath(neutral_measure_selector).map(&:value).map(&:upcase).sort - #list of all of the setids in the QRDA that are also in the bundle, includes duplicates if code appears twice in document - bundle_neutral_ids = HealthDataStandards::CQM::Measure.distinct(:hqmf_set_id) + # list of all of the setids in the QRDA that are also in the bundle, includes duplicates if code appears twice in document + bundle_neutral_ids = CQM::Measure.distinct(:hqmf_set_id) doc_bundle_neutral_ids = doc_neutral_ids - (doc_neutral_ids - bundle_neutral_ids) validate_measure_ids(doc_measure_ids, measure_ids, data) validate_set_ids(doc_neutral_ids, doc_bundle_neutral_ids, data) - if validate_no_repeating_measure_population_ids(data) - validate_measure_ids_set_ids_usage(doc_bundle_neutral_ids, doc_measure_ids, data) - end - + validate_measure_ids_set_ids_usage(doc_bundle_neutral_ids, doc_measure_ids, data) if validate_no_repeating_measure_population_ids(data) + @errors + end + + private + + # returns true if there are no repeating measures, check to see that the measure id usage is correct + def validate_no_repeating_measure_population_ids(data = {}) + no_duplicate_measures = true + doc_population_ids = @doc.xpath(measure_population_selector).map(&:value).map(&:upcase).sort + duplicates = doc_population_ids.group_by { |e| e }.select { |_k, v| v.size > 1 }.map(&:first) + duplicates.each do |duplicate| + begin + measure_id = @doc.xpath(find_measure_node_for_population(duplicate)).at_xpath("cda:reference/cda:externalDocument/cda:id[./@root='2.16.840.1.113883.4.738']/@extension") + @errors << build_error("Population #{duplicate} for Measure #{measure_id.value} reported more than once", '/', data[:file_name]) + rescue + @errors << build_error("Population #{duplicate} for reported more than once", '/', data[:file_name]) end - - private - - #returns true if there are no repeating measures, check to see that the measure id usage is correct - def validate_no_repeating_measure_population_ids(data={}) - noDuplicateMeasures = true - doc_population_ids = @doc.xpath(measure_population_selector).map(&:value).map(&:upcase).sort - duplicates = doc_population_ids.group_by{ |e| e }.select { |k, v| v.size > 1 }.map(&:first) - duplicates.each do |duplicate| - begin - measureId = @doc.xpath(find_measure_node_for_population(duplicate)).at_xpath("cda:reference/cda:externalDocument/cda:id[./@root='2.16.840.1.113883.4.738']/@extension") - @errors << build_error("Population #{duplicate} for Measure #{measureId.value} reported more than once", "/", data[:file_name]) - rescue - @errors << build_error("Population #{duplicate} for reported more than once", "/", data[:file_name]) - end - noDuplicateMeasures = false - end - return noDuplicateMeasures - end - - def validate_measure_ids(doc_measure_ids, measure_ids, data={}) + no_duplicate_measures = false + end + no_duplicate_measures + end + + def validate_measure_ids(doc_measure_ids, measure_ids, data = {}) (doc_measure_ids - measure_ids).map do |hqmf_id| - @errors << build_error("Invalid HQMF ID Found: #{hqmf_id}", "/", data[:file_name]) + @errors << build_error("Invalid HQMF ID Found: #{hqmf_id}", '/', data[:file_name]) end - end - - def validate_set_ids(doc_neutral_ids, doc_bundle_neutral_ids, data={}) - #an error will be returned for all of the setids that are in the QRDA that aren't in the bundle + end + + def validate_set_ids(doc_neutral_ids, doc_bundle_neutral_ids, data = {}) + # an error will be returned for all of the setids that are in the QRDA that aren't in the bundle (doc_neutral_ids - doc_bundle_neutral_ids).map do |hqmf_set_id| - @errors << build_error("Invalid HQMF Set ID Found: #{hqmf_set_id}", "/", data[:file_name]) + @errors << build_error("Invalid HQMF Set ID Found: #{hqmf_set_id}", '/', data[:file_name]) end - end - - #does not work if the same measure is reported more than once, nested under the repeating measures test - def validate_measure_ids_set_ids_usage(doc_bundle_neutral_ids, doc_measure_ids, data={}) - #for each of the setIds that are in the bundle, check that they are for the correct measure id + end + + # does not work if the same measure is reported more than once, nested under the repeating measures test + def validate_measure_ids_set_ids_usage(doc_bundle_neutral_ids, doc_measure_ids, data = {}) + # for each of the setIds that are in the bundle, check that they are for the correct measure id entries_start_position = @doc.xpath(first_entry) - previous = "" + previous = '' index = 1 doc_bundle_neutral_ids.each do |hqmf_set_id| - #selects the measure id that is in the same entry as the set id - #iterates through multiple instances of the same setId - if previous == hqmf_set_id - index = index + 1 - else - index = 1 - end - measure_id_entry = doc_measure_ids[(@doc.xpath(location_of_set_id(hqmf_set_id,index)) - entries_start_position)] + # selects the measure id that is in the same entry as the set id + # iterates through multiple instances of the same setId + index = if previous == hqmf_set_id + index + 1 + else + 1 + end + measure_id_entry = doc_measure_ids[(@doc.xpath(location_of_set_id(hqmf_set_id, index)) - entries_start_position)] previous = hqmf_set_id - #queries database to see if there is a measure with the combindation of setId and measureId - if HealthDataStandards::CQM::Measure.where(hqmf_id: measure_id_entry, hqmf_set_id: hqmf_set_id).length() == 0 - @errors << build_error("Invalid HQMF Set ID Found: #{hqmf_set_id} for HQMF ID: #{measure_id_entry}", "/", data[:file_name]) + # queries database to see if there is a measure with the combindation of setId and measureId + if CQM::Measure.where(hqmf_id: measure_id_entry, hqmf_set_id: hqmf_set_id).empty? + @errors << build_error("Invalid HQMF Set ID Found: #{hqmf_set_id} for HQMF ID: #{measure_id_entry}", '/', data[:file_name]) end end - end - - def measure_selector - "/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section/cda:entry" + - "/cda:organizer[./cda:templateId[@root='#{@template_oid}']]/cda:reference[@typeCode='REFR']" + - "/cda:externalDocument[@classCode='DOC']/cda:id[@root='2.16.840.1.113883.4.738']/@extension" - end - - #finds all of the setIds in the QRDA document - def neutral_measure_selector - "/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section/cda:entry" + - "/cda:organizer[./cda:templateId[@root='#{@template_oid}']]/cda:reference[@typeCode='REFR']" + - "/cda:externalDocument[@classCode='DOC']/cda:setId/@root" - end - - #finds the node index of the first entry element in the measure template - def first_entry - "count(//cda:entry[cda:organizer[./cda:templateId[@root='#{@template_oid}']]" + - "/cda:reference[@typeCode='REFR']/cda:externalDocument[@classCode='DOC']" + - "/cda:id[@root='2.16.840.1.113883.4.738']][1]/preceding-sibling::*)+1" - end - - #finds the node index of the extry that the specified setId is in, index is used if the same setId appears twice - def location_of_set_id(set_id,index) - "count(//cda:entry[cda:organizer[./cda:templateId[@root='#{@template_oid}']]" + - "/cda:reference[@typeCode='REFR']/cda:externalDocument[@classCode='DOC']" + - "/cda:setId[@root[contains(translate(.,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLOMNOPQRSTUVWXYZ')" + - ",'#{set_id}')]]][#{index}]/preceding-sibling::*)+1" - end - - - def measure_population_selector - "/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']]/cda:component" + - "/cda:observation[./cda:templateId[@root='2.16.840.1.113883.10.20.27.3.5']]/cda:reference" + - "/cda:externalObservation/cda:id/@root" - end - - def find_measure_node_for_population(id) - "/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:component/cda:observation[./cda:templateId[@root='2.16.840.1.113883.10.20.27.3.5']]/cda:reference" + - "/cda:externalObservation/cda:id[@root[contains(translate(.,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLOMNOPQRSTUVWXYZ')" + + end + + def measure_selector + '/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section/cda:entry' \ + "/cda:organizer[./cda:templateId[@root='#{@template_oid}']]/cda:reference[@typeCode='REFR']" \ + "/cda:externalDocument[@classCode='DOC']/cda:id[@root='2.16.840.1.113883.4.738']/@extension" + end + + # finds all of the setIds in the QRDA document + def neutral_measure_selector + '/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section/cda:entry' \ + "/cda:organizer[./cda:templateId[@root='#{@template_oid}']]/cda:reference[@typeCode='REFR']" \ + "/cda:externalDocument[@classCode='DOC']/cda:setId/@root" + end + + # finds the node index of the first entry element in the measure template + def first_entry + "count(//cda:entry[cda:organizer[./cda:templateId[@root='#{@template_oid}']]" \ + "/cda:reference[@typeCode='REFR']/cda:externalDocument[@classCode='DOC']" \ + "/cda:id[@root='2.16.840.1.113883.4.738']][1]/preceding-sibling::*)+1" + end + + # finds the node index of the extry that the specified setId is in, index is used if the same setId appears twice + def location_of_set_id(set_id, index) + "count(//cda:entry[cda:organizer[./cda:templateId[@root='#{@template_oid}']]" \ + "/cda:reference[@typeCode='REFR']/cda:externalDocument[@classCode='DOC']" \ + "/cda:setId[@root[contains(translate(.,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLOMNOPQRSTUVWXYZ')" \ + ",'#{set_id}')]]][#{index}]/preceding-sibling::*)+1" + end + + def measure_population_selector + '/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']]/cda:component" \ + "/cda:observation[./cda:templateId[@root='2.16.840.1.113883.10.20.27.3.5']]/cda:reference" \ + '/cda:externalObservation/cda:id/@root' + end + + def find_measure_node_for_population(id) + '/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:component/cda:observation[./cda:templateId[@root='2.16.840.1.113883.10.20.27.3.5']]/cda:reference" \ + "/cda:externalObservation/cda:id[@root[contains(translate(.,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLOMNOPQRSTUVWXYZ')" \ ",'#{id.upcase}')]]]" - end - end + end + end end - \ No newline at end of file