health-data-standards 2.2.1 → 3.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -0
- data/lib/health-data-standards.rb +55 -12
- data/lib/health-data-standards/export/c32.rb +7 -6
- data/lib/health-data-standards/export/cat_1.rb +18 -0
- data/lib/health-data-standards/export/ccda.rb +8 -8
- data/lib/health-data-standards/export/green_c32/entry.rb +8 -5
- data/lib/health-data-standards/export/green_c32/export_generator.rb +1 -1
- data/lib/health-data-standards/export/green_c32/record.rb +10 -7
- data/lib/health-data-standards/export/hdata/metadata.rb +7 -6
- data/lib/health-data-standards/export/helper/cat1_view_helper.rb +133 -0
- data/lib/health-data-standards/export/helper/gc32_view_helper.rb +39 -0
- data/lib/health-data-standards/export/helper/html_view_helper.rb +23 -0
- data/lib/health-data-standards/export/html.rb +22 -10
- data/lib/health-data-standards/export/qrda/entry_template_resolver.rb +31 -0
- data/lib/health-data-standards/export/qrda/hqmf-qrda-oids.json +638 -0
- data/lib/health-data-standards/export/rendering_context.rb +37 -3
- data/lib/health-data-standards/export/template_helper.rb +20 -23
- data/lib/health-data-standards/export/view_helper.rb +8 -42
- data/lib/health-data-standards/import/bundle/importer.rb +148 -0
- data/lib/health-data-standards/import/c32/care_goal_importer.rb +14 -26
- data/lib/health-data-standards/import/c32/condition_importer.rb +12 -51
- data/lib/health-data-standards/import/c32/immunization_importer.rb +9 -27
- data/lib/health-data-standards/import/c32/insurance_provider_importer.rb +24 -21
- data/lib/health-data-standards/import/c32/patient_importer.rb +27 -34
- data/lib/health-data-standards/import/cat1/diagnosis_active_importer.rb +19 -0
- data/lib/health-data-standards/import/cat1/diagnosis_inactive_importer.rb +19 -0
- data/lib/health-data-standards/import/cat1/diagnostic_study_order_importer.rb +24 -0
- data/lib/health-data-standards/import/cat1/encounter_order_importer.rb +24 -0
- data/lib/health-data-standards/import/cat1/entry_package.rb +25 -0
- data/lib/health-data-standards/import/cat1/gestational_age_importer.rb +42 -0
- data/lib/health-data-standards/import/cat1/lab_order_importer.rb +24 -0
- data/lib/health-data-standards/import/cat1/medication_dispensed_importer.rb +13 -0
- data/lib/health-data-standards/import/cat1/patient_importer.rb +104 -0
- data/lib/health-data-standards/import/cat1/procedure_intolerance_importer.rb +13 -0
- data/lib/health-data-standards/import/cat1/procedure_order_importer.rb +38 -0
- data/lib/health-data-standards/import/cat1/tobacco_use_importer.rb +19 -0
- data/lib/health-data-standards/import/ccda/allergy_importer.rb +2 -8
- data/lib/health-data-standards/import/ccda/care_goal_importer.rb +1 -1
- data/lib/health-data-standards/import/ccda/condition_importer.rb +2 -3
- data/lib/health-data-standards/import/ccda/encounter_importer.rb +2 -5
- data/lib/health-data-standards/import/ccda/immunization_importer.rb +1 -3
- data/lib/health-data-standards/import/ccda/insurance_provider_importer.rb +1 -1
- data/lib/health-data-standards/import/ccda/medical_equipment_importer.rb +2 -4
- data/lib/health-data-standards/import/ccda/medication_importer.rb +2 -4
- data/lib/health-data-standards/import/ccda/patient_importer.rb +1 -1
- data/lib/health-data-standards/import/ccda/procedure_importer.rb +2 -6
- data/lib/health-data-standards/import/ccda/result_importer.rb +2 -5
- data/lib/health-data-standards/import/ccda/vital_sign_importer.rb +2 -3
- data/lib/health-data-standards/import/cda/allergy_importer.rb +32 -0
- data/lib/health-data-standards/import/cda/condition_importer.rb +51 -0
- data/lib/health-data-standards/import/{c32 → cda}/encounter_importer.rb +9 -35
- data/lib/health-data-standards/import/cda/entry_finder.rb +22 -0
- data/lib/health-data-standards/import/{c32 → cda}/locatable_import_utils.rb +2 -3
- data/lib/health-data-standards/import/cda/medical_equipment_importer.rb +24 -0
- data/lib/health-data-standards/import/{c32 → cda}/medication_importer.rb +13 -15
- data/lib/health-data-standards/import/cda/narrative_reference_handler.rb +35 -0
- data/lib/health-data-standards/import/{c32 → cda}/organization_importer.rb +1 -1
- data/lib/health-data-standards/import/cda/procedure_importer.rb +35 -0
- data/lib/health-data-standards/import/{c32 → cda}/provider_importer.rb +4 -8
- data/lib/health-data-standards/import/cda/result_importer.rb +31 -0
- data/lib/health-data-standards/import/{c32 → cda}/section_importer.rb +26 -48
- data/lib/health-data-standards/import/{c32 → cda}/vital_sign_importer.rb +2 -3
- data/lib/health-data-standards/import/green_c32/section_importer.rb +8 -6
- data/lib/health-data-standards/import/provider_import_utils.rb +2 -2
- data/lib/health-data-standards/models/address.rb +1 -1
- data/lib/health-data-standards/models/condition.rb +7 -6
- data/lib/health-data-standards/models/cqm/bundle.rb +45 -0
- data/lib/health-data-standards/models/cqm/measure.rb +36 -0
- data/lib/health-data-standards/models/guarantor.rb +1 -0
- data/lib/health-data-standards/models/insurance_provider.rb +2 -0
- data/lib/health-data-standards/models/order_information.rb +2 -0
- data/lib/health-data-standards/models/record.rb +6 -5
- data/lib/health-data-standards/models/svs/value_set.rb +1 -1
- data/lib/health-data-standards/railtie.rb +11 -0
- data/lib/health-data-standards/tasks/bundle.rake +107 -0
- data/lib/health-data-standards/util/code_system_helper.rb +9 -1
- data/lib/hqmf-generator/attribute.xml.erb +11 -0
- data/lib/hqmf-generator/characteristic_criteria.xml.erb +21 -0
- data/lib/hqmf-generator/code.xml.erb +13 -0
- data/lib/hqmf-generator/condition_criteria.xml.erb +22 -0
- data/lib/hqmf-generator/derivation.xml.erb +6 -0
- data/lib/hqmf-generator/description.xml.erb +1 -0
- data/lib/hqmf-generator/document.xml.erb +63 -0
- data/lib/hqmf-generator/effective_time.xml.erb +4 -0
- data/lib/hqmf-generator/encounter_criteria.xml.erb +21 -0
- data/lib/hqmf-generator/field.xml.erb +28 -0
- data/lib/hqmf-generator/hqmf-generator.rb +292 -0
- data/lib/hqmf-generator/observation_criteria.xml.erb +25 -0
- data/lib/hqmf-generator/population_criteria.xml.erb +23 -0
- data/lib/hqmf-generator/precondition.xml.erb +14 -0
- data/lib/hqmf-generator/procedure_criteria.xml.erb +22 -0
- data/lib/hqmf-generator/reason.xml.erb +3 -0
- data/lib/hqmf-generator/reference.xml.erb +3 -0
- data/lib/hqmf-generator/source.xml.erb +6 -0
- data/lib/hqmf-generator/specific_occurrence.xml.erb +7 -0
- data/lib/hqmf-generator/subset.xml.erb +8 -0
- data/lib/hqmf-generator/substance_criteria.xml.erb +26 -0
- data/lib/hqmf-generator/supply_criteria.xml.erb +26 -0
- data/lib/hqmf-generator/template_id.xml.erb +5 -0
- data/lib/hqmf-generator/temporal_relationship.xml.erb +6 -0
- data/lib/hqmf-generator/value.xml.erb +24 -0
- data/lib/hqmf-generator/variable_criteria.xml.erb +12 -0
- data/lib/hqmf-model/attribute.rb +35 -0
- data/lib/hqmf-model/data_criteria.json +1123 -0
- data/lib/hqmf-model/data_criteria.rb +344 -0
- data/lib/hqmf-model/document.rb +178 -0
- data/lib/hqmf-model/population_criteria.rb +96 -0
- data/lib/hqmf-model/precondition.rb +91 -0
- data/lib/hqmf-model/types.rb +319 -0
- data/lib/hqmf-model/utilities.rb +52 -0
- data/lib/hqmf-parser.rb +56 -0
- data/lib/hqmf-parser/1.0/attribute.rb +68 -0
- data/lib/hqmf-parser/1.0/comparison.rb +34 -0
- data/lib/hqmf-parser/1.0/data_criteria.rb +92 -0
- data/lib/hqmf-parser/1.0/data_criteria_oid_xpath.json +91 -0
- data/lib/hqmf-parser/1.0/document.rb +203 -0
- data/lib/hqmf-parser/1.0/expression.rb +58 -0
- data/lib/hqmf-parser/1.0/observation.rb +61 -0
- data/lib/hqmf-parser/1.0/population_criteria.rb +75 -0
- data/lib/hqmf-parser/1.0/precondition.rb +89 -0
- data/lib/hqmf-parser/1.0/range.rb +65 -0
- data/lib/hqmf-parser/1.0/restriction.rb +160 -0
- data/lib/hqmf-parser/1.0/utilities.rb +41 -0
- data/lib/hqmf-parser/2.0/data_criteria.rb +319 -0
- data/lib/hqmf-parser/2.0/document.rb +165 -0
- data/lib/hqmf-parser/2.0/population_criteria.rb +53 -0
- data/lib/hqmf-parser/2.0/precondition.rb +44 -0
- data/lib/hqmf-parser/2.0/types.rb +223 -0
- data/lib/hqmf-parser/2.0/utilities.rb +30 -0
- data/lib/hqmf-parser/converter/pass1/data_criteria_converter.rb +252 -0
- data/lib/hqmf-parser/converter/pass1/document_converter.rb +185 -0
- data/lib/hqmf-parser/converter/pass1/population_criteria_converter.rb +165 -0
- data/lib/hqmf-parser/converter/pass1/precondition_converter.rb +173 -0
- data/lib/hqmf-parser/converter/pass1/precondition_extractor.rb +188 -0
- data/lib/hqmf-parser/converter/pass1/simple_data_criteria.rb +26 -0
- data/lib/hqmf-parser/converter/pass1/simple_operator.rb +89 -0
- data/lib/hqmf-parser/converter/pass1/simple_population_criteria.rb +10 -0
- data/lib/hqmf-parser/converter/pass1/simple_precondition.rb +51 -0
- data/lib/hqmf-parser/converter/pass1/simple_restriction.rb +64 -0
- data/lib/hqmf-parser/converter/pass2/comparison_converter.rb +112 -0
- data/lib/hqmf-parser/converter/pass2/operator_converter.rb +102 -0
- data/lib/hqmf-parser/parser.rb +54 -0
- data/lib/hqmf-parser/value_sets/value_set_parser.rb +241 -0
- data/lib/util/counter.rb +20 -0
- data/templates/{_allergies.c32.erb → c32/_allergies.c32.erb} +0 -0
- data/templates/{_allergies_no_current.c32.erb → c32/_allergies_no_current.c32.erb} +0 -0
- data/templates/{_care_goals.c32.erb → c32/_care_goals.c32.erb} +0 -0
- data/templates/{_code_with_reference.c32.erb → c32/_code_with_reference.c32.erb} +0 -0
- data/templates/{_conditions.c32.erb → c32/_conditions.c32.erb} +0 -0
- data/templates/{_conditions_no_current.c32.erb → c32/_conditions_no_current.c32.erb} +0 -0
- data/templates/{_encounters.c32.erb → c32/_encounters.c32.erb} +0 -0
- data/templates/{_immunizations.c32.erb → c32/_immunizations.c32.erb} +0 -0
- data/templates/{_medical_equipment.c32.erb → c32/_medical_equipment.c32.erb} +0 -0
- data/templates/{_medications.c32.erb → c32/_medications.c32.erb} +0 -0
- data/templates/{_medications_no_current.c32.erb → c32/_medications_no_current.c32.erb} +0 -0
- data/templates/{_narrative_block.c32.erb → c32/_narrative_block.c32.erb} +0 -0
- data/templates/{_procedures.c32.erb → c32/_procedures.c32.erb} +0 -0
- data/templates/{_results.c32.erb → c32/_results.c32.erb} +0 -0
- data/templates/{_social_history.c32.erb → c32/_social_history.c32.erb} +0 -0
- data/templates/{_vital_signs.c32.erb → c32/_vital_signs.c32.erb} +0 -0
- data/templates/{show.c32.erb → c32/show.c32.erb} +0 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.22.4.85.cat1.erb +18 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.1.cat1.erb +14 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.101.cat1.erb +25 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.103.cat1.erb +12 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.105.cat1.erb +60 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.11.cat1.erb +41 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.12.cat1.erb +50 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.13.cat1.erb +37 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.14.cat1.erb +35 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.17.cat1.erb +22 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.18.cat1.erb +21 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.2.cat1.erb +28 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.20.cat1.erb +20 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.22.cat1.erb +21 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.23.cat1.erb +71 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.28.cat1.erb +20 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.3.cat1.erb +24 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.31.cat1.erb +20 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.32.cat1.erb +15 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.34.cat1.erb +58 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.37.cat1.erb +20 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.38.cat1.erb +16 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.4.cat1.erb +27 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.40.cat1.erb +17 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.41.cat1.erb +38 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.42.cat1.erb +38 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.43.cat1.erb +24 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.44.cat1.erb +24 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.45.cat1.erb +26 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.46.cat1.erb +30 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.47.cat1.erb +26 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.51.cat1.erb +13 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.54.cat1.erb +16 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.55.cat1.erb +10 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.57.cat1.erb +19 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.59.cat1.erb +17 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.62.cat1.erb +36 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.63.cat1.erb +23 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.64.cat1.erb +29 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.66.cat1.erb +34 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.69.cat1.erb +23 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.7.cat1.erb +30 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.76.cat1.erb +32 -0
- data/templates/cat1/_measures.cat1.erb +66 -0
- data/templates/cat1/_medication_details.cat1.erb +9 -0
- data/templates/cat1/_ordinality.cat1.erb +4 -0
- data/templates/cat1/_patient_data.cat1.erb +14 -0
- data/templates/cat1/_reason.cat1.erb +16 -0
- data/templates/cat1/_record_target.cat1.erb +39 -0
- data/templates/cat1/_reporting_parameters.cat1.erb +24 -0
- data/templates/cat1/_result_value.cat1.erb +16 -0
- data/templates/cat1/show.cat1.erb +125 -0
- data/templates/{_address.gc32.erb → gc32/_address.gc32.erb} +1 -1
- data/templates/gc32/_advance_directive.gc32.erb +5 -0
- data/templates/gc32/_allergy.gc32.erb +12 -0
- data/templates/{_care_goal.gc32.erb → gc32/_care_goal.gc32.erb} +1 -1
- data/templates/gc32/_condition.gc32.erb +10 -0
- data/templates/gc32/_encounter.gc32.erb +28 -0
- data/templates/gc32/_entry.gc32.erb +3 -0
- data/templates/gc32/_entry_attributes.gc32.erb +10 -0
- data/templates/gc32/_immunization.gc32.erb +9 -0
- data/templates/gc32/_insurance_provider.gc32.erb +28 -0
- data/templates/gc32/_medical_equipment.gc32.erb +6 -0
- data/templates/gc32/_medication.gc32.erb +91 -0
- data/templates/{_name.gc32.erb → gc32/_name.gc32.erb} +0 -0
- data/templates/gc32/_organization.gc32.erb +10 -0
- data/templates/gc32/_person_attributes.gc32.erb +7 -0
- data/templates/gc32/_procedure.gc32.erb +9 -0
- data/templates/gc32/_provider.gc32.erb +9 -0
- data/templates/gc32/_result.gc32.erb +12 -0
- data/templates/gc32/_social_history.gc32.erb +6 -0
- data/templates/{_support.gc32.erb → gc32/_support.gc32.erb} +4 -3
- data/templates/gc32/_telecom.gc32.erb +1 -0
- data/templates/gc32/_vital_sign.gc32.erb +4 -0
- data/templates/{record.gc32.erb → gc32/record.gc32.erb} +26 -10
- data/templates/html/_entries_by_encounter.html.erb +2 -2
- data/templates/html/_entries_by_section.html.erb +1 -1
- data/templates/html/_entry.html.erb +16 -21
- data/templates/html/_header.html.erb +1 -1
- data/templates/html/_section.html.erb +1 -1
- data/templates/html/show.html.erb +23 -2
- data/templates/metadata.hdata.erb +3 -3
- metadata +282 -54
- data/lib/health-data-standards/import/c32/allergy_importer.rb +0 -47
- data/lib/health-data-standards/import/c32/medical_equipment_importer.rb +0 -45
- data/lib/health-data-standards/import/c32/procedure_importer.rb +0 -62
- data/lib/health-data-standards/import/c32/result_importer.rb +0 -56
- data/templates/_advance_directive.gc32.erb +0 -8
- data/templates/_allergy.gc32.erb +0 -23
- data/templates/_condition.gc32.erb +0 -9
- data/templates/_encounter.gc32.erb +0 -26
- data/templates/_entry.gc32.erb +0 -14
- data/templates/_immunization.gc32.erb +0 -11
- data/templates/_insurance_provider.gc32.erb +0 -0
- data/templates/_medical_equipment.gc32.erb +0 -7
- data/templates/_medication.gc32.erb +0 -72
- data/templates/_organization.gc32.erb +0 -10
- data/templates/_procedure.gc32.erb +0 -10
- data/templates/_provider.gc32.erb +0 -19
- data/templates/_result.gc32.erb +0 -16
- data/templates/_social_history.gc32.erb +0 -8
- data/templates/_telecom.gc32.erb +0 -1
- data/templates/_vital_sign.gc32.erb +0 -7
@@ -0,0 +1,53 @@
|
|
1
|
+
module HQMF2
|
2
|
+
# Represents an HQMF population criteria, also supports all the same methods as
|
3
|
+
# HQMF2::Precondition
|
4
|
+
class PopulationCriteria
|
5
|
+
|
6
|
+
include HQMF2::Utilities
|
7
|
+
|
8
|
+
attr_reader :preconditions, :id, :hqmf_id, :title, :type
|
9
|
+
|
10
|
+
# Create a new population criteria from the supplied HQMF entry
|
11
|
+
# @param [Nokogiri::XML::Element] the HQMF entry
|
12
|
+
def initialize(entry, doc)
|
13
|
+
@doc = doc
|
14
|
+
@entry = entry
|
15
|
+
@hqmf_id = attr_val('./*/cda:id/@extension')
|
16
|
+
@title = attr_val('./*/cda:code/cda:displayName/@value')
|
17
|
+
@type = attr_val('./*/cda:code/@code')
|
18
|
+
@preconditions = @entry.xpath('./*/cda:precondition[not(@nullFlavor)]', HQMF2::Document::NAMESPACES).collect do |precondition|
|
19
|
+
Precondition.new(precondition, @doc)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_human_readable_id(id)
|
24
|
+
@id = id
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return true of this precondition represents a conjunction with nested preconditions
|
28
|
+
# or false of this precondition is a reference to a data criteria
|
29
|
+
def conjunction?
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get the conjunction code, e.g. allTrue, allFalse
|
34
|
+
# @return [String] conjunction code
|
35
|
+
def conjunction_code
|
36
|
+
case @type
|
37
|
+
when HQMF::PopulationCriteria::IPP, HQMF::PopulationCriteria::DENOM, HQMF::PopulationCriteria::NUMER
|
38
|
+
HQMF::Precondition::ALL_TRUE
|
39
|
+
when HQMF::PopulationCriteria::DENEXCEP, HQMF::PopulationCriteria::DENEX
|
40
|
+
HQMF::Precondition::AT_LEAST_ONE_TRUE
|
41
|
+
else
|
42
|
+
raise "Unknown population type [#{@type}]"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_model
|
47
|
+
mps = preconditions.collect {|p| p.to_model}
|
48
|
+
HQMF::PopulationCriteria.new(id, hqmf_id, type, mps, title)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module HQMF2
|
2
|
+
|
3
|
+
class Precondition
|
4
|
+
|
5
|
+
include HQMF2::Utilities
|
6
|
+
|
7
|
+
attr_reader :preconditions, :reference
|
8
|
+
|
9
|
+
def initialize(entry, doc)
|
10
|
+
@doc = doc
|
11
|
+
@entry = entry
|
12
|
+
@preconditions = @entry.xpath('./*/cda:precondition', HQMF2::Document::NAMESPACES).collect do |precondition|
|
13
|
+
Precondition.new(precondition, @doc)
|
14
|
+
end
|
15
|
+
reference_def = @entry.at_xpath('./*/cda:id', HQMF2::Document::NAMESPACES)
|
16
|
+
if reference_def
|
17
|
+
@reference = Reference.new(reference_def)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return true of this precondition represents a conjunction with nested preconditions
|
22
|
+
# or false of this precondition is a reference to a data criteria
|
23
|
+
def conjunction?
|
24
|
+
@preconditions.length>0
|
25
|
+
end
|
26
|
+
|
27
|
+
# Get the conjunction code, e.g. allTrue, allFalse
|
28
|
+
# @return [String] conjunction code
|
29
|
+
def conjunction_code
|
30
|
+
if conjunction?
|
31
|
+
@entry.at_xpath('./*[1]', HQMF2::Document::NAMESPACES).name
|
32
|
+
else
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_model
|
38
|
+
pcs = preconditions.collect {|p| p.to_model}
|
39
|
+
mr = reference ? reference.to_model : nil
|
40
|
+
HQMF::Precondition.new(nil, pcs, mr, conjunction_code, false)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
module HQMF2
|
2
|
+
# Used to represent 'any value' in criteria that require a value be present but
|
3
|
+
# don't specify any restrictions on that value
|
4
|
+
class AnyValue
|
5
|
+
attr_reader :type
|
6
|
+
|
7
|
+
def initialize(type='ANYNonNull')
|
8
|
+
@type = type
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_model
|
12
|
+
HQMF::AnyValue.new(@type)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Represents a bound within a HQMF pauseQuantity, has a value, a unit and an
|
17
|
+
# inclusive/exclusive indicator
|
18
|
+
class Value
|
19
|
+
include HQMF2::Utilities
|
20
|
+
|
21
|
+
attr_reader :type, :unit, :value
|
22
|
+
|
23
|
+
def initialize(entry, default_type='PQ')
|
24
|
+
@entry = entry
|
25
|
+
@type = attr_val('./@xsi:type') || default_type
|
26
|
+
@unit = attr_val('./@unit')
|
27
|
+
@value = attr_val('./@value')
|
28
|
+
end
|
29
|
+
|
30
|
+
def inclusive?
|
31
|
+
case attr_val('./@inclusive')
|
32
|
+
when 'false'
|
33
|
+
false
|
34
|
+
else
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def derived?
|
40
|
+
case attr_val('./@nullFlavor')
|
41
|
+
when 'DER'
|
42
|
+
true
|
43
|
+
else
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def expression
|
49
|
+
if !derived?
|
50
|
+
nil
|
51
|
+
else
|
52
|
+
attr_val('./cda:expression/@value')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_model
|
57
|
+
HQMF::Value.new(type,unit,value,inclusive?,derived?,expression)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Represents a HQMF physical quantity which can have low and high bounds
|
62
|
+
class Range
|
63
|
+
include HQMF2::Utilities
|
64
|
+
attr_accessor :low, :high, :width
|
65
|
+
|
66
|
+
def initialize(entry, type=nil)
|
67
|
+
@type = type
|
68
|
+
@entry = entry
|
69
|
+
if @entry
|
70
|
+
@low = optional_value('./cda:low', default_bounds_type)
|
71
|
+
@high = optional_value('./cda:high', default_bounds_type)
|
72
|
+
@width = optional_value('./cda:width', 'PQ')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def type
|
77
|
+
@type || attr_val('./@xsi:type')
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_model
|
81
|
+
lm = low ? low.to_model : nil
|
82
|
+
hm = high ? high.to_model : nil
|
83
|
+
wm = width ? width.to_model : nil
|
84
|
+
HQMF::Range.new(type, lm, hm, wm)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def optional_value(xpath, type)
|
90
|
+
value_def = @entry.at_xpath(xpath, HQMF2::Document::NAMESPACES)
|
91
|
+
if value_def
|
92
|
+
Value.new(value_def, type)
|
93
|
+
else
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def default_bounds_type
|
99
|
+
case type
|
100
|
+
when 'IVL_TS'
|
101
|
+
'TS'
|
102
|
+
else
|
103
|
+
'PQ'
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Represents a HQMF effective time which is a specialization of a interval
|
109
|
+
class EffectiveTime < Range
|
110
|
+
def initialize(entry)
|
111
|
+
super
|
112
|
+
end
|
113
|
+
|
114
|
+
def type
|
115
|
+
'IVL_TS'
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Represents a HQMF CD value which has a code and codeSystem
|
120
|
+
class Coded
|
121
|
+
include HQMF2::Utilities
|
122
|
+
|
123
|
+
def initialize(entry)
|
124
|
+
@entry = entry
|
125
|
+
end
|
126
|
+
|
127
|
+
def type
|
128
|
+
attr_val('./@xsi:type') || 'CD'
|
129
|
+
end
|
130
|
+
|
131
|
+
def system
|
132
|
+
attr_val('./@codeSystem')
|
133
|
+
end
|
134
|
+
|
135
|
+
def code
|
136
|
+
attr_val('./@code')
|
137
|
+
end
|
138
|
+
|
139
|
+
def code_list_id
|
140
|
+
attr_val('./@valueSet')
|
141
|
+
end
|
142
|
+
|
143
|
+
def title
|
144
|
+
attr_val('./@displayName')
|
145
|
+
end
|
146
|
+
|
147
|
+
def value
|
148
|
+
code
|
149
|
+
end
|
150
|
+
|
151
|
+
def derived?
|
152
|
+
false
|
153
|
+
end
|
154
|
+
|
155
|
+
def unit
|
156
|
+
nil
|
157
|
+
end
|
158
|
+
|
159
|
+
def to_model
|
160
|
+
HQMF::Coded.new(type, system, code, code_list_id, title)
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
class SubsetOperator
|
166
|
+
include HQMF2::Utilities
|
167
|
+
|
168
|
+
attr_reader :type, :value
|
169
|
+
|
170
|
+
def initialize(entry)
|
171
|
+
@entry = entry
|
172
|
+
@type = attr_val('./cda:subsetCode/@code')
|
173
|
+
value_def = @entry.at_xpath('./*/cda:repeatNumber', HQMF2::Document::NAMESPACES)
|
174
|
+
if value_def
|
175
|
+
@value = HQMF2::Range.new(value_def, 'IVL_INT')
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def to_model
|
180
|
+
vm = value ? value.to_model : nil
|
181
|
+
HQMF::SubsetOperator.new(type, vm)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
class TemporalReference
|
186
|
+
include HQMF2::Utilities
|
187
|
+
|
188
|
+
attr_reader :type, :reference, :range
|
189
|
+
|
190
|
+
def initialize(entry)
|
191
|
+
@entry = entry
|
192
|
+
@type = attr_val('./@typeCode')
|
193
|
+
@reference = Reference.new(@entry.at_xpath('./*/cda:id', HQMF2::Document::NAMESPACES))
|
194
|
+
range_def = @entry.at_xpath('./cda:pauseQuantity', HQMF2::Document::NAMESPACES)
|
195
|
+
if range_def
|
196
|
+
@range = HQMF2::Range.new(range_def, 'IVL_PQ')
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def to_model
|
201
|
+
rm = range ? range.to_model : nil
|
202
|
+
HQMF::TemporalReference.new(type, reference.to_model, rm)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Represents a HQMF reference from a precondition to a data criteria
|
207
|
+
class Reference
|
208
|
+
include HQMF2::Utilities
|
209
|
+
|
210
|
+
def initialize(entry)
|
211
|
+
@entry = entry
|
212
|
+
end
|
213
|
+
|
214
|
+
def id
|
215
|
+
attr_val('./@extension')
|
216
|
+
end
|
217
|
+
|
218
|
+
def to_model
|
219
|
+
HQMF::Reference.new(id)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module HQMF2
|
2
|
+
module Utilities
|
3
|
+
|
4
|
+
include HQMF::Conversion::Utilities
|
5
|
+
|
6
|
+
# Utility function to handle optional attributes
|
7
|
+
# @param xpath an XPath that identifies an XML attribute
|
8
|
+
# @return the value of the attribute or nil if the attribute is missing
|
9
|
+
def attr_val(xpath)
|
10
|
+
Utilities::attr_val(@entry, xpath)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Utility function to handle optional attributes
|
14
|
+
# @param xpath an XPath that identifies an XML attribute
|
15
|
+
# @return the value of the attribute or nil if the attribute is missing
|
16
|
+
def self.attr_val(node, xpath)
|
17
|
+
attr = node.at_xpath(xpath, HQMF2::Document::NAMESPACES)
|
18
|
+
if attr
|
19
|
+
attr.value
|
20
|
+
else
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_xml
|
26
|
+
@entry.to_xml
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,252 @@
|
|
1
|
+
module HQMF
|
2
|
+
# Class representing an HQMF document
|
3
|
+
class DataCriteriaConverter
|
4
|
+
|
5
|
+
attr_reader :v1_data_criteria_by_id, :v2_data_criteria, :v2_data_criteria_to_delete, :measure_period_criteria, :measure_period_v1_keys, :specific_occurrences
|
6
|
+
|
7
|
+
def initialize(doc, measure_period)
|
8
|
+
@doc = doc
|
9
|
+
@v1_data_criteria_by_id = {}
|
10
|
+
@v2_data_criteria = []
|
11
|
+
@v2_data_criteria_to_delete = {}
|
12
|
+
@specific_occurrences = {}
|
13
|
+
@measure_period = measure_period
|
14
|
+
parse()
|
15
|
+
end
|
16
|
+
|
17
|
+
def final_v2_data_criteria
|
18
|
+
@v2_data_criteria.delete_if {|criteria| @v2_data_criteria_to_delete[criteria.id] }
|
19
|
+
end
|
20
|
+
|
21
|
+
# duplicates a data criteria. This is important because we may be modifying source data criteria like patient characteristic birthdate to add restrictions
|
22
|
+
# the restrictions added may be different for the numerator, denominator, different IPP_1, IPP_2, etc.
|
23
|
+
def duplicate_data_criteria(data_criteria, parent_id)
|
24
|
+
|
25
|
+
if (data_criteria.is_a? HQMF::Converter::SimpleDataCriteria and data_criteria.precondition_id == parent_id)
|
26
|
+
new_data_criteria = data_criteria
|
27
|
+
else
|
28
|
+
new_data_criteria = HQMF::Converter::SimpleDataCriteria.from_data_criteria(data_criteria)
|
29
|
+
new_data_criteria.assign_precondition(parent_id)
|
30
|
+
@v2_data_criteria << new_data_criteria
|
31
|
+
# we want to delete the original for data criteria that have been duplicated
|
32
|
+
@v2_data_criteria_to_delete[data_criteria.id] = true if !@v2_data_criteria_to_delete.keys.include? data_criteria.id
|
33
|
+
end
|
34
|
+
|
35
|
+
new_data_criteria
|
36
|
+
end
|
37
|
+
|
38
|
+
# make sure that if a data criteria is used as a target, that it is not deleted by someone else.
|
39
|
+
# this is required for birthdate in NQF0106
|
40
|
+
def validate_not_deleted(target)
|
41
|
+
@v2_data_criteria_to_delete[target] = false
|
42
|
+
end
|
43
|
+
|
44
|
+
# grouping data criteria are used to allow a single reference off of a temporal reference or subset operator
|
45
|
+
# grouping data criteria can reference either regular data criteria as children, or other grouping data criteria
|
46
|
+
def create_group_data_criteria(preconditions, type, value, parent_id, id, standard_category, qds_data_type)
|
47
|
+
extract_group_data_criteria_tree(HQMF::DataCriteria::UNION,preconditions, type, parent_id)
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_group_data_criteria(children, section, parent_id, derivation_operator)
|
51
|
+
|
52
|
+
criteria_ids = children.map(&:id)
|
53
|
+
# make sure nobody else is going to delete the criteria we've grouped
|
54
|
+
criteria_ids.each {|target| validate_not_deleted(target)}
|
55
|
+
|
56
|
+
id = "#{parent_id}_#{section}_#{HQMF::Counter.instance.next}"
|
57
|
+
title = "#{id}"
|
58
|
+
description = ""
|
59
|
+
definition = 'derived'
|
60
|
+
_display_name,_code_list_id,_status,_value,_field_values,_effective_time,_inline_code_list,_negation_code_list_id, = nil
|
61
|
+
_negation = false
|
62
|
+
|
63
|
+
group_criteria = HQMF::DataCriteria.new(id, title, _display_name, description, _code_list_id, criteria_ids, derivation_operator, definition, _status,
|
64
|
+
_value, _field_values, _effective_time, _inline_code_list,_negation,_negation_code_list_id,nil,nil,nil,nil)
|
65
|
+
|
66
|
+
@v2_data_criteria << group_criteria
|
67
|
+
|
68
|
+
group_criteria
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
# pull the children data criteria out of a set of preconditions
|
73
|
+
def extract_group_data_criteria_tree(conjunction, preconditions, type, parent_id)
|
74
|
+
|
75
|
+
children = []
|
76
|
+
preconditions.each do |precondition|
|
77
|
+
if (precondition.comparison?)
|
78
|
+
if (precondition.reference.id == HQMF::Document::MEASURE_PERIOD_ID)
|
79
|
+
children << measure_period_criteria
|
80
|
+
else
|
81
|
+
children << v2_data_criteria_by_id[precondition.reference.id]
|
82
|
+
end
|
83
|
+
else
|
84
|
+
converted_conjunction = convert_grouping_conjunction(precondition.conjunction_code)
|
85
|
+
children << extract_group_data_criteria_tree(converted_conjunction, precondition.preconditions, type, parent_id)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# if we have just one child element, just return it. An AND or OR of a single item is not useful.
|
90
|
+
if (children.size > 1)
|
91
|
+
build_group_data_criteria(children, type, parent_id, conjunction)
|
92
|
+
else
|
93
|
+
children.first
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
def convert_grouping_conjunction(conjunction)
|
99
|
+
case conjunction
|
100
|
+
when HQMF::Precondition::AT_LEAST_ONE_TRUE
|
101
|
+
HQMF::DataCriteria::UNION
|
102
|
+
when HQMF::Precondition::ALL_TRUE
|
103
|
+
HQMF::DataCriteria::XPRODUCT
|
104
|
+
else
|
105
|
+
'unknown'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# pull the children data criteria out of a set of preconditions
|
110
|
+
def self.extract_data_criteria(preconditions, data_criteria_converter)
|
111
|
+
flattened = []
|
112
|
+
preconditions.each do |precondition|
|
113
|
+
if (precondition.comparison?)
|
114
|
+
if (precondition.reference.id == HQMF::Document::MEASURE_PERIOD_ID)
|
115
|
+
flattened << data_criteria_converter.measure_period_criteria
|
116
|
+
else
|
117
|
+
flattened << data_criteria_converter.v2_data_criteria_by_id[precondition.reference.id]
|
118
|
+
end
|
119
|
+
else
|
120
|
+
flattened.concat(extract_data_criteria(precondition.preconditions,data_criteria_converter))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
flattened
|
124
|
+
end
|
125
|
+
|
126
|
+
def v2_data_criteria_by_id
|
127
|
+
criteria_by_id = {}
|
128
|
+
@v2_data_criteria.each do |criteria|
|
129
|
+
criteria_by_id[criteria.id] = criteria
|
130
|
+
end
|
131
|
+
criteria_by_id
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def parse()
|
137
|
+
@doc[:data_criteria].each do |key,criteria|
|
138
|
+
parsed_criteria = HQMF::DataCriteriaConverter.convert(key, criteria)
|
139
|
+
@v2_data_criteria << parsed_criteria
|
140
|
+
@v1_data_criteria_by_id[criteria[:id]] = parsed_criteria
|
141
|
+
@specific_occurrences[parsed_criteria.id] = criteria[:derived_from] != nil
|
142
|
+
end
|
143
|
+
create_measure_period_v1_data_criteria(@doc,@measure_period,@v1_data_criteria_by_id)
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.convert(key, criteria)
|
147
|
+
|
148
|
+
# @param [String] id
|
149
|
+
# @param [String] title
|
150
|
+
# @param [String] standard_category
|
151
|
+
# @param [String] qds_data_type
|
152
|
+
# @param [String] subset_code
|
153
|
+
# @param [String] code_list_id
|
154
|
+
# @param [String] property
|
155
|
+
# @param [String] type
|
156
|
+
# @param [String] status
|
157
|
+
# @param [boolean] negation
|
158
|
+
# @param [String] negation_code_list_id
|
159
|
+
# @param [Value|Range|Coded] value
|
160
|
+
# @param [Range] effective_time
|
161
|
+
# @param [Hash<String,String>] inline_code_list
|
162
|
+
|
163
|
+
id = convert_key(key)
|
164
|
+
title = criteria[:title]
|
165
|
+
title = title.match(/.*:\s+(.+)/)[1]
|
166
|
+
description = criteria[:description]
|
167
|
+
code_list_id = criteria[:code_list_id]
|
168
|
+
definition = criteria[:definition]
|
169
|
+
status = criteria[:status]
|
170
|
+
negation = criteria[:negation]
|
171
|
+
negation_code_list_id = criteria[:negation_code_list_id]
|
172
|
+
specific_occurrence = criteria[:specific_occurrence]
|
173
|
+
specific_occurrence_const = nil
|
174
|
+
|
175
|
+
# specific occurrences do not properly set the description, so we want to add the definition and status
|
176
|
+
if (specific_occurrence)
|
177
|
+
statusText = ", #{status.titleize}" if status
|
178
|
+
description = "#{definition.titleize}#{statusText}: #{description}"
|
179
|
+
specific_occurrence_const = (description.gsub(/\W/,' ').split.collect {|word| word.strip.upcase }).join '_'
|
180
|
+
end
|
181
|
+
|
182
|
+
value = nil # value is filled out by backfill_patient_characteristics for things like gender and by REFR restrictions
|
183
|
+
effective_time = nil # filled out by temporal reference code
|
184
|
+
temporal_references = # filled out by operator code
|
185
|
+
subset_operators = nil # filled out by operator code
|
186
|
+
children_criteria = nil # filled out by operator and temporal reference code
|
187
|
+
derivation_operator = nil # filled out by operator and temporal reference code
|
188
|
+
negation_code_list_id = nil # filled out by RSON restrictions
|
189
|
+
field_values = nil # field values are filled out by SUBJ and REFR restrictions
|
190
|
+
inline_code_list = nil # inline code list is only used in HQMF V2, so we can just pass in nil
|
191
|
+
display_name=nil
|
192
|
+
|
193
|
+
# transfers should be modeled as a field. The code_list_id of the transfer data criteria is cleared and the oid is added to a transfer field
|
194
|
+
# The definition of the data criteria is still transfer, but it is marked as an encounter using the patient api funciton.
|
195
|
+
if ['transfer_to', 'transfer_from'].include? definition
|
196
|
+
field_values ||= {}
|
197
|
+
field_values[definition.upcase] = HQMF::Coded.for_code_list(code_list_id, title)
|
198
|
+
code_list_id = nil
|
199
|
+
end
|
200
|
+
|
201
|
+
HQMF::DataCriteria.new(id, title, display_name, description, code_list_id, children_criteria, derivation_operator, definition, status,
|
202
|
+
value, field_values, effective_time, inline_code_list, negation, negation_code_list_id, temporal_references, subset_operators,specific_occurrence,specific_occurrence_const)
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
# this method creates V1 data criteria for the measurement period. These data criteria can be
|
208
|
+
# referenced properly within the restrictions
|
209
|
+
def create_measure_period_v1_data_criteria(doc,measure_period,v1_data_criteria_by_id)
|
210
|
+
|
211
|
+
attributes = doc[:attributes]
|
212
|
+
attributes.keys.each {|key| attributes[key.to_s] = attributes[key]}
|
213
|
+
|
214
|
+
measure_period_key = attributes['MEASUREMENT_PERIOD'][:id]
|
215
|
+
measure_start_key = attributes['MEASUREMENT_START_DATE'][:id]
|
216
|
+
measure_end_key = attributes['MEASUREMENT_END_DATE'][:id]
|
217
|
+
|
218
|
+
@measure_period_v1_keys = {measure_start: measure_start_key, measure_end: measure_end_key, measure_period: measure_period_key}
|
219
|
+
|
220
|
+
type = 'variable'
|
221
|
+
code_list_id,negation_code_list_id,property,status,field_values,effective_time,inline_code_list,children_criteria,derivation_operator,temporal_references,subset_operators=nil
|
222
|
+
|
223
|
+
#####
|
224
|
+
##
|
225
|
+
######### SET MEASURE PERIOD
|
226
|
+
##
|
227
|
+
#####
|
228
|
+
|
229
|
+
measure_period_id = HQMF::Document::MEASURE_PERIOD_ID
|
230
|
+
value = measure_period
|
231
|
+
measure_criteria = HQMF::DataCriteria.new(measure_period_id,measure_period_id,nil,measure_period_id,code_list_id,children_criteria,derivation_operator,measure_period_id,status,
|
232
|
+
value,field_values,effective_time,inline_code_list, false, nil, temporal_references,subset_operators,nil,nil)
|
233
|
+
|
234
|
+
# set the measure period data criteria for all measure period keys
|
235
|
+
v1_data_criteria_by_id[measure_period_key] = measure_criteria
|
236
|
+
v1_data_criteria_by_id[measure_start_key] = measure_criteria
|
237
|
+
v1_data_criteria_by_id[measure_end_key] = measure_criteria
|
238
|
+
@measure_period_criteria = measure_criteria
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
def self.title_from_description(title, description)
|
244
|
+
title.gsub(/^#{Regexp.escape(description).gsub('\\ ',':?,?\\ ')}:\s*/i,'')
|
245
|
+
end
|
246
|
+
|
247
|
+
def self.convert_key(key)
|
248
|
+
key.to_s.downcase.gsub('_', ' ').split(' ').map {|w| w.capitalize }.join('')
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
252
|
+
end
|