cqm-parsers 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +29 -0
  3. data/README.md +21 -0
  4. data/Rakefile +19 -0
  5. data/lib/ext/code.rb +10 -0
  6. data/lib/ext/data_element.rb +24 -0
  7. data/lib/hqmf-model/attribute.rb +63 -0
  8. data/lib/hqmf-model/data_criteria.rb +467 -0
  9. data/lib/hqmf-model/document.rb +253 -0
  10. data/lib/hqmf-model/population_criteria.rb +102 -0
  11. data/lib/hqmf-model/precondition.rb +94 -0
  12. data/lib/hqmf-model/types.rb +457 -0
  13. data/lib/hqmf-model/utilities.rb +52 -0
  14. data/lib/hqmf-parser.rb +116 -0
  15. data/lib/hqmf-parser/1.0/attribute.rb +121 -0
  16. data/lib/hqmf-parser/1.0/comparison.rb +34 -0
  17. data/lib/hqmf-parser/1.0/data_criteria.rb +92 -0
  18. data/lib/hqmf-parser/1.0/document.rb +195 -0
  19. data/lib/hqmf-parser/1.0/expression.rb +60 -0
  20. data/lib/hqmf-parser/1.0/observation.rb +61 -0
  21. data/lib/hqmf-parser/1.0/population_criteria.rb +75 -0
  22. data/lib/hqmf-parser/1.0/precondition.rb +90 -0
  23. data/lib/hqmf-parser/1.0/range.rb +76 -0
  24. data/lib/hqmf-parser/1.0/restriction.rb +162 -0
  25. data/lib/hqmf-parser/1.0/utilities.rb +55 -0
  26. data/lib/hqmf-parser/2.0/data_criteria.rb +372 -0
  27. data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_base_extract.rb +80 -0
  28. data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_definition_from_template_or_type_extract.rb +201 -0
  29. data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_post_processing.rb +85 -0
  30. data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_specific_occurrences_and_source_data_criteria_extract.rb +117 -0
  31. data/lib/hqmf-parser/2.0/document.rb +304 -0
  32. data/lib/hqmf-parser/2.0/document_helpers/doc_population_helper.rb +173 -0
  33. data/lib/hqmf-parser/2.0/document_helpers/doc_utilities.rb +131 -0
  34. data/lib/hqmf-parser/2.0/field_value_helper.rb +251 -0
  35. data/lib/hqmf-parser/2.0/population_criteria.rb +134 -0
  36. data/lib/hqmf-parser/2.0/precondition.rb +73 -0
  37. data/lib/hqmf-parser/2.0/source_data_criteria_helper.rb +112 -0
  38. data/lib/hqmf-parser/2.0/types.rb +448 -0
  39. data/lib/hqmf-parser/2.0/utilities.rb +45 -0
  40. data/lib/hqmf-parser/2.0/value_set_helper.rb +104 -0
  41. data/lib/hqmf-parser/converter/pass1/data_criteria_converter.rb +257 -0
  42. data/lib/hqmf-parser/converter/pass1/document_converter.rb +133 -0
  43. data/lib/hqmf-parser/converter/pass1/population_criteria_converter.rb +185 -0
  44. data/lib/hqmf-parser/converter/pass1/precondition_converter.rb +173 -0
  45. data/lib/hqmf-parser/converter/pass1/precondition_extractor.rb +201 -0
  46. data/lib/hqmf-parser/converter/pass1/simple_data_criteria.rb +26 -0
  47. data/lib/hqmf-parser/converter/pass1/simple_operator.rb +89 -0
  48. data/lib/hqmf-parser/converter/pass1/simple_population_criteria.rb +10 -0
  49. data/lib/hqmf-parser/converter/pass1/simple_precondition.rb +51 -0
  50. data/lib/hqmf-parser/converter/pass1/simple_restriction.rb +64 -0
  51. data/lib/hqmf-parser/converter/pass2/comparison_converter.rb +112 -0
  52. data/lib/hqmf-parser/converter/pass2/operator_converter.rb +102 -0
  53. data/lib/hqmf-parser/cql/data_criteria.rb +57 -0
  54. data/lib/hqmf-parser/cql/data_criteria_helpers/dc_definition_from_template_or_type_extract.rb +79 -0
  55. data/lib/hqmf-parser/cql/data_criteria_helpers/dc_post_processing.rb +43 -0
  56. data/lib/hqmf-parser/cql/document.rb +78 -0
  57. data/lib/hqmf-parser/cql/document_helpers/doc_population_helper.rb +124 -0
  58. data/lib/hqmf-parser/cql/value_set_helper.rb +103 -0
  59. data/lib/hqmf-parser/parser.rb +100 -0
  60. data/lib/qrda-export/catI-r5/qrda1_r5.rb +125 -0
  61. data/lib/qrda-export/helper/cat_1_view_helper.rb +142 -0
  62. data/lib/qrda-export/helper/code_system_helper.rb +77 -0
  63. data/lib/qrda-export/helper/date_helper.rb +81 -0
  64. data/lib/qrda-import/base-importers/demographics_importer.rb +47 -0
  65. data/lib/qrda-import/base-importers/medication_importer.rb +22 -0
  66. data/lib/qrda-import/base-importers/section_importer.rb +196 -0
  67. data/lib/qrda-import/cda_identifier.rb +19 -0
  68. data/lib/qrda-import/data-element-importers/adverse_event_importer.rb +23 -0
  69. data/lib/qrda-import/data-element-importers/allergy_intolerance_importer.rb +21 -0
  70. data/lib/qrda-import/data-element-importers/assessment_performed_importer.rb +23 -0
  71. data/lib/qrda-import/data-element-importers/communication_from_patient_to_provider_importer.rb +18 -0
  72. data/lib/qrda-import/data-element-importers/communication_from_provider_to_patient_importer.rb +18 -0
  73. data/lib/qrda-import/data-element-importers/communication_from_provider_to_provider_importer.rb +20 -0
  74. data/lib/qrda-import/data-element-importers/device_applied_importer.rb +23 -0
  75. data/lib/qrda-import/data-element-importers/device_order_importer.rb +18 -0
  76. data/lib/qrda-import/data-element-importers/diagnosis_importer.rb +23 -0
  77. data/lib/qrda-import/data-element-importers/diagnostic_study_order_importer.rb +20 -0
  78. data/lib/qrda-import/data-element-importers/diagnostic_study_performed_importer.rb +30 -0
  79. data/lib/qrda-import/data-element-importers/encounter_order_importer.rb +20 -0
  80. data/lib/qrda-import/data-element-importers/encounter_performed_importer.rb +41 -0
  81. data/lib/qrda-import/data-element-importers/immunization_administered_importer.rb +18 -0
  82. data/lib/qrda-import/data-element-importers/intervention_order_importer.rb +18 -0
  83. data/lib/qrda-import/data-element-importers/intervention_performed_importer.rb +22 -0
  84. data/lib/qrda-import/data-element-importers/laboratory_test_order_importer.rb +20 -0
  85. data/lib/qrda-import/data-element-importers/laboratory_test_performed_importer.rb +28 -0
  86. data/lib/qrda-import/data-element-importers/medication_active_importer.rb +17 -0
  87. data/lib/qrda-import/data-element-importers/medication_administered_importer.rb +17 -0
  88. data/lib/qrda-import/data-element-importers/medication_discharge_importer.rb +19 -0
  89. data/lib/qrda-import/data-element-importers/medication_dispensed_importer.rb +19 -0
  90. data/lib/qrda-import/data-element-importers/medication_order_importer.rb +16 -0
  91. data/lib/qrda-import/data-element-importers/patient_characteristic_expired.rb +21 -0
  92. data/lib/qrda-import/data-element-importers/physical_exam_performed_importer.rb +26 -0
  93. data/lib/qrda-import/data-element-importers/procedure_order_importer.rb +26 -0
  94. data/lib/qrda-import/data-element-importers/procedure_performed_importer.rb +34 -0
  95. data/lib/qrda-import/data-element-importers/substance_administered_importer.rb +16 -0
  96. data/lib/qrda-import/entry_finder.rb +20 -0
  97. data/lib/qrda-import/entry_package.rb +16 -0
  98. data/lib/qrda-import/narrative_reference_handler.rb +33 -0
  99. data/lib/qrda-import/patient_importer.rb +105 -0
  100. data/lib/util/code_system_helper.rb +76 -0
  101. data/lib/util/counter.rb +20 -0
  102. data/lib/util/hqmf_template_helper.rb +39 -0
  103. metadata +340 -0
@@ -0,0 +1,45 @@
1
+ module HQMF2
2
+ # Module containing parser helper functions
3
+ module Utilities
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
+ return attr.value if attr
19
+ end
20
+
21
+ def to_xml
22
+ @entry.to_xml
23
+ end
24
+
25
+ # General helper for stripping '-' and ',' into '_' for processable ids
26
+ def strip_tokens(value)
27
+ return nil if value.nil?
28
+ stripped = value.gsub(/[^0-9a-z]/i, '_')
29
+ # Prefix digits with 'prefix_' to prevent JS syntax errors
30
+ stripped.gsub(/^[0-9]/, "prefix_#{value[0]}")
31
+ end
32
+
33
+ # Class that generates incremental ids
34
+ class IdGenerator
35
+ def initialize
36
+ @current_id = 0
37
+ end
38
+
39
+ def next_id
40
+ @current_id += 1
41
+ @current_id
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,104 @@
1
+ module HQMF2
2
+ # Class containing value set information
3
+ class ValueSetHelper
4
+ # rubocop:disable Metrics/LineLength
5
+ VALUESET_MAP = {
6
+ '2.16.840.1.113883.10.20.28.3.7' => { valueset_path: './*/cda:value', result_path: nil },
7
+ '2.16.840.1.113883.10.20.28.3.8' => { valueset_path: './*/cda:code', result_path: nil },
8
+ '2.16.840.1.113883.10.20.28.3.9' => { valueset_path: './*/cda:code', result_path: nil },
9
+ '2.16.840.1.113883.10.20.28.3.10' => { valueset_path: './*/cda:code', result_path: nil },
10
+ '2.16.840.1.113883.10.20.28.3.11' => { valueset_path: "./*/cda:participation[@typeCode='PRD']/cda:role[@classCode='MANU']/cda:playingDevice[@classCode='DEV']/cda:code", result_path: nil },
11
+ '2.16.840.1.113883.10.20.28.3.12' => { valueset_path: "./*/cda:participation[@typeCode='PRD']/cda:role[@classCode='MANU']/cda:playingDevice[@classCode='DEV']/cda:code", result_path: nil },
12
+ '2.16.840.1.113883.10.20.28.3.13' => { valueset_path: "./*/cda:participation[@typeCode='DEV']/cda:role[@classCode='MANU']/cda:playingDevice[@classCode='DEV']/cda:code", result_path: nil },
13
+ '2.16.840.1.113883.10.20.28.3.14' => { valueset_path: "./*/cda:participation[@typeCode='PRD']/cda:role[@classCode='MANU']/cda:playingDevice[@classCode='DEV']/cda:code", result_path: nil },
14
+ '2.16.840.1.113883.10.20.28.3.15' => { valueset_path: "./*/cda:participation[@typeCode='DEV']/cda:role[@classCode='MANU']/cda:playingDevice[@classCode='DEV']/cda:code", result_path: nil },
15
+ '2.16.840.1.113883.10.20.28.3.16' => { valueset_path: "./*/cda:participation[@typeCode='DEV']/cda:role[@classCode='MANU']/cda:playingDevice[@classCode='DEV']/cda:code", result_path: nil },
16
+ '2.16.840.1.113883.10.20.28.3.1' => { valueset_path: './*/cda:value', result_path: nil },
17
+ '2.16.840.1.113883.10.20.28.3.17' => { valueset_path: './*/cda:value', result_path: nil },
18
+ '2.16.840.1.113883.10.20.28.3.18' => { valueset_path: './*/cda:value', result_path: nil },
19
+ '2.16.840.1.113883.10.20.28.3.19' => { valueset_path: './*/cda:value', result_path: nil },
20
+ '2.16.840.1.113883.10.20.28.3.20' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:observationCriteria/cda:code", result_path: nil },
21
+ '2.16.840.1.113883.10.20.28.3.21' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:observationCriteria/cda:code", result_path: nil },
22
+ '2.16.840.1.113883.10.20.28.3.22' => { valueset_path: './*/cda:code', result_path: nil },
23
+ '2.16.840.1.113883.10.20.28.3.23' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
24
+ '2.16.840.1.113883.10.20.28.3.24' => { valueset_path: './*/cda:code', result_path: nil },
25
+ '2.16.840.1.113883.10.20.28.3.26' => { valueset_path: './*/cda:code', result_path: nil },
26
+ '2.16.840.1.113883.10.20.28.3.27' => { valueset_path: './*/cda:code', result_path: nil },
27
+ '2.16.840.1.113883.10.20.28.3.5' => { valueset_path: './*/cda:code', result_path: nil },
28
+ '2.16.840.1.113883.10.20.28.3.28' => { valueset_path: './*/cda:code', result_path: nil },
29
+ '2.16.840.1.113883.10.20.28.3.29' => { valueset_path: './*/cda:code', result_path: nil },
30
+ '2.16.840.1.113883.10.20.28.3.30' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
31
+ '2.16.840.1.113883.10.20.28.3.31' => { valueset_path: './*/cda:code', result_path: nil },
32
+ '2.16.840.1.113883.10.20.28.3.33' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS' and @inversionInd='true']/cda:procedureCriteria/cda:code", result_path: nil },
33
+ '2.16.840.1.113883.10.20.28.3.34' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:actCriteria/cda:code", result_path: nil },
34
+ '2.16.840.1.113883.10.20.28.3.35' => { valueset_path: './*/cda:code', result_path: nil },
35
+ '2.16.840.1.113883.10.20.28.3.36' => { valueset_path: './*/cda:code', result_path: "./*/cda:outboundRelationship[@typeCode='REFR']//cda:code[@code='394617004']/../cda:value" },
36
+ '2.16.840.1.113883.10.20.28.3.37' => { valueset_path: './*/cda:code', result_path: nil },
37
+ '2.16.840.1.113883.10.20.28.3.39' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:observationCriteria/cda:code", result_path: nil },
38
+ '2.16.840.1.113883.10.20.28.3.40' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:observationCriteria/cda:code", result_path: nil },
39
+ '2.16.840.1.113883.10.20.28.3.41' => { valueset_path: './*/cda:code', result_path: nil },
40
+ '2.16.840.1.113883.10.20.28.3.42' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
41
+ '2.16.840.1.113883.10.20.28.3.43' => { valueset_path: './*/cda:code', result_path: nil },
42
+ '2.16.840.1.113883.10.20.28.3.44' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role/cda:playingMaterial[@classCode='MMAT']/cda:code", result_path: nil },
43
+ '2.16.840.1.113883.10.20.28.3.45' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingManufacturedMaterial[@classCode='MMAT']/cda:code", result_path: nil },
44
+ '2.16.840.1.113883.10.20.28.3.46' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingEntity[@classCode='MMAT']/cda:code", result_path: nil },
45
+ '2.16.840.1.113883.10.20.28.3.47' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingEntity[@classCode='MMAT']/cda:code", result_path: nil },
46
+ '2.16.840.1.113883.10.20.28.3.48' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingManufacturedMaterial[@classCode='MMAT']/cda:code", result_path: nil },
47
+ '2.16.840.1.113883.10.20.28.3.49' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingMaterial[@classCode='MMAT']/cda:code", result_path: nil },
48
+ '2.16.840.1.113883.10.20.28.3.50' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingMaterial[@classCode='MMAT']/cda:code", result_path: nil },
49
+ '2.16.840.1.113883.10.20.28.3.51' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingMaterial[@classCode='MMAT']/cda:code", result_path: nil },
50
+ '2.16.840.1.113883.10.20.28.3.52' => { valueset_path: './*/cda:value', result_path: nil },
51
+ '2.16.840.1.113883.10.20.28.3.53' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
52
+ '2.16.840.1.113883.10.20.28.3.6' => { valueset_path: './*/cda:value', result_path: nil },
53
+ '2.16.840.1.113883.10.20.28.3.54' => { valueset_path: nil, result_path: nil },
54
+ '2.16.840.1.113883.10.20.28.3.56' => { valueset_path: './*/cda:value', result_path: nil },
55
+ '2.16.840.1.113883.10.20.28.3.57' => { valueset_path: './*/cda:value', result_path: nil },
56
+ '2.16.840.1.113883.10.20.28.3.58' => { valueset_path: './*/cda:value', result_path: nil },
57
+ '2.16.840.1.113883.10.20.28.3.59' => { valueset_path: './*/cda:value', result_path: nil },
58
+ '2.16.840.1.113883.10.20.28.3.55' => { valueset_path: './*/cda:value', result_path: nil },
59
+ '2.16.840.1.113883.10.20.28.3.86' => { valueset_path: './*/cda:value', result_path: nil },
60
+ '2.16.840.1.113883.10.20.28.3.61' => { valueset_path: './*/cda:value', result_path: nil },
61
+ '2.16.840.1.113883.10.20.28.3.62' => { valueset_path: './*/cda:value', result_path: "./*/cda:outboundRelationship[@typeCode='REFR']//cda:code[@code='394617004']/../cda:value" },
62
+ '2.16.840.1.113883.10.20.28.3.63' => { valueset_path: './*/cda:value', result_path: nil },
63
+ '2.16.840.1.113883.10.20.28.3.64' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS' and @inversionInd='true']/cda:procedureCriteria/cda:code", result_path: nil },
64
+ '2.16.840.1.113883.10.20.28.3.65' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS' and @inversionInd='true']/cda:procedureCriteria/cda:code", result_path: nil },
65
+ '2.16.840.1.113883.10.20.28.3.66' => { valueset_path: './*/cda:code', result_path: nil },
66
+ '2.16.840.1.113883.10.20.28.3.67' => { valueset_path: './*/cda:code', result_path: "./*/cda:outboundRelationship[@typeCode='REFR']//cda:code[@code='394617004']/../cda:value" },
67
+ '2.16.840.1.113883.10.20.28.3.68' => { valueset_path: './*/cda:code', result_path: nil },
68
+ '2.16.840.1.113883.10.20.28.3.70' => { valueset_path: './*/cda:value', result_path: nil },
69
+ '2.16.840.1.113883.10.20.28.3.71' => { valueset_path: "./*/cda:participation/cda:role[@classCode='ASSIGNED']/cda:playingDevice[@classCode='DEV' and @determinerCode='KIND']/cda:code", result_path: nil },
70
+ '2.16.840.1.113883.10.20.28.3.87' => { valueset_path: './*/cda:value', result_path: nil },
71
+ '2.16.840.1.113883.10.20.28.3.72' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
72
+ '2.16.840.1.113883.10.20.28.3.93' => { valueset_path: './*/cda:value', result_path: nil },
73
+ '2.16.840.1.113883.10.20.28.3.73' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='ADMM']/cda:playingMaterial[@classCode='MAT' and @determinerCode='KIND']/cda:code", result_path: nil },
74
+ '2.16.840.1.113883.10.20.28.3.74' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='ADMM']/cda:playingMaterial[@classCode='MAT' and @determinerCode='KIND']/cda:code", result_path: nil },
75
+ '2.16.840.1.113883.10.20.28.3.75' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='ADMM']/cda:playingMaterial[@classCode='MAT' and @determinerCode='KIND']/cda:code", result_path: nil },
76
+ '2.16.840.1.113883.10.20.28.3.76' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='ADMM']/cda:playingMaterial[@classCode='MAT' and @determinerCode='KIND']/cda:code", result_path: nil },
77
+ '2.16.840.1.113883.10.20.28.3.77' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='ADMM']/cda:playingMaterial[@classCode='MAT' and @determinerCode='KIND']/cda:code", result_path: nil },
78
+ '2.16.840.1.113883.10.20.28.3.78' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingMaterial[@classCode='MMAT' and @determinerCode='KIND']/cda:code", result_path: nil },
79
+ '2.16.840.1.113883.10.20.28.3.79' => { valueset_path: './*/cda:value', result_path: nil },
80
+ '2.16.840.1.113883.10.20.28.3.80' => { valueset_path: './*/cda:value', result_path: nil },
81
+ '2.16.840.1.113883.10.20.28.3.81' => { valueset_path: './*/cda:value', result_path: nil },
82
+ '2.16.840.1.113883.10.20.28.3.82' => { valueset_path: './*/cda:value', result_path: nil },
83
+ '2.16.840.1.113883.10.20.28.3.84' => { valueset_path: "./*/cda:participation[@typeCode='ORG']/cda:role[@classCode='LOCE']/cda:code", result_path: nil },
84
+ '2.16.840.1.113883.10.20.28.3.85' => { valueset_path: "./*/cda:participation[@typeCode='ORG']/cda:role[@classCode='LOCE']/cda:code", result_path: nil },
85
+ '2.16.840.1.113883.10.20.28.3.110' => { valueset_path: './*/cda:value', result_path: nil },
86
+ '2.16.840.1.113883.10.20.28.3.111' => { valueset_path: './*/cda:value', result_path: nil },
87
+ '2.16.840.1.113883.10.20.28.3.112' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role/cda:playingManufacturedMaterial[@classCode='MMAT']/cda:code", result_path: nil },
88
+ '2.16.840.1.113883.10.20.28.3.113' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role/cda:playingManufacturedMaterial[@classCode='MMAT']/cda:code", result_path: nil },
89
+ '2.16.840.1.113883.10.20.28.3.114' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role/cda:playingEntity[@classCode='MMAT']/cda:code", result_path: nil },
90
+ '2.16.840.1.113883.10.20.28.3.115' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role/cda:playingMaterial[@classCode='MMAT']/cda:code", result_path: nil },
91
+ '2.16.840.1.113883.10.20.28.3.116' => { valueset_path: './*/cda:value', result_path: nil },
92
+ '2.16.840.1.113883.10.20.28.3.117' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
93
+ '2.16.840.1.113883.10.20.28.3.118' => { valueset_path: './*/cda:code', result_path: nil },
94
+ '2.16.840.1.113883.10.20.28.3.119' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingEntity[@classCode='MMAT']/cda:code", result_path: nil },
95
+ '2.16.840.1.113883.10.20.28.3.120' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingEntity[@classCode='MMAT']/cda:code", result_path: nil }
96
+
97
+ }
98
+ # rubocop:enable Metrics/LineLength
99
+
100
+ def self.get_mapping_for_template(template)
101
+ VALUESET_MAP[template]
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,257 @@
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
+ if status
178
+ statusText = ", #{status.titleize}"
179
+ elsif definition == 'laboratory_test'
180
+ # laboratory_test without a status is actually a Result
181
+ statusText = ", Result"
182
+ end
183
+ description = "#{definition.titleize}#{statusText}: #{description}"
184
+ specific_occurrence_const = (description.gsub(/\W/,' ').split.collect {|word| word.strip.upcase }).join '_'
185
+ end
186
+
187
+ value = nil # value is filled out by backfill_patient_characteristics for things like gender and by REFR restrictions
188
+ effective_time = nil # filled out by temporal reference code
189
+ temporal_references = # filled out by operator code
190
+ subset_operators = nil # filled out by operator code
191
+ children_criteria = nil # filled out by operator and temporal reference code
192
+ derivation_operator = nil # filled out by operator and temporal reference code
193
+ negation_code_list_id = nil # filled out by RSON restrictions
194
+ field_values = nil # field values are filled out by SUBJ and REFR restrictions
195
+ inline_code_list = nil # inline code list is only used in HQMF V2, so we can just pass in nil
196
+ display_name=nil
197
+
198
+ # 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
199
+ # The definition of the data criteria is still transfer, but it is marked as an encounter using the patient api funciton.
200
+ if ['transfer_to', 'transfer_from'].include? definition
201
+ field_values ||= {}
202
+ field_values[definition.upcase] = HQMF::Coded.for_code_list(code_list_id, title)
203
+ code_list_id = nil
204
+ end
205
+
206
+ HQMF::DataCriteria.new(id, title, display_name, description, code_list_id, children_criteria, derivation_operator, definition, status,
207
+ value, field_values, effective_time, inline_code_list, negation, negation_code_list_id, temporal_references, subset_operators,specific_occurrence,specific_occurrence_const)
208
+
209
+ end
210
+
211
+
212
+ # this method creates V1 data criteria for the measurement period. These data criteria can be
213
+ # referenced properly within the restrictions
214
+ def create_measure_period_v1_data_criteria(doc,measure_period,v1_data_criteria_by_id)
215
+
216
+ attributes = doc[:attributes]
217
+ attributes.keys.each {|key| attributes[key.to_s] = attributes[key]}
218
+
219
+ measure_period_key = attributes['MEASUREMENT_PERIOD'][:id]
220
+ measure_start_key = attributes['MEASUREMENT_START_DATE'][:id]
221
+ measure_end_key = attributes['MEASUREMENT_END_DATE'][:id]
222
+
223
+ @measure_period_v1_keys = {measure_start: measure_start_key, measure_end: measure_end_key, measure_period: measure_period_key}
224
+
225
+ type = 'variable'
226
+ 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
227
+
228
+ #####
229
+ ##
230
+ ######### SET MEASURE PERIOD
231
+ ##
232
+ #####
233
+
234
+ measure_period_id = HQMF::Document::MEASURE_PERIOD_ID
235
+ value = measure_period
236
+ 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,
237
+ value,field_values,effective_time,inline_code_list, false, nil, temporal_references,subset_operators,nil,nil)
238
+
239
+ # set the measure period data criteria for all measure period keys
240
+ v1_data_criteria_by_id[measure_period_key] = measure_criteria
241
+ v1_data_criteria_by_id[measure_start_key] = measure_criteria
242
+ v1_data_criteria_by_id[measure_end_key] = measure_criteria
243
+ @measure_period_criteria = measure_criteria
244
+
245
+ end
246
+
247
+
248
+ def self.title_from_description(title, description)
249
+ title.gsub(/^#{Regexp.escape(description).gsub('\\ ',':?,?\\ ')}:\s*/i,'')
250
+ end
251
+
252
+ def self.convert_key(key)
253
+ key.to_s.downcase.gsub('_', ' ').split(' ').map {|w| w.capitalize }.join('')
254
+ end
255
+
256
+ end
257
+ end
@@ -0,0 +1,133 @@
1
+ module HQMF
2
+ # Class for converting an HQMF 1.0 representation to an HQMF 2.0 representation
3
+ class DocumentConverter
4
+
5
+ BIRTHTIME_CODE_LIST = {'LOINC'=>['21112-8']}
6
+
7
+ def self.convert(json, codes)
8
+
9
+ title = json[:title]
10
+ description = json[:description]
11
+
12
+ # TODO: Investigate why we never use json[:attributes]
13
+ metadata = json[:metadata]
14
+ metadata.keys.each {|key| metadata[key.to_s] = metadata[key]; metadata.delete(key.to_sym)}
15
+ id = metadata["NQF_ID_NUMBER"][:value] if metadata["NQF_ID_NUMBER"]
16
+ emeasure_id = metadata['EMEASURE_IDENTIFIER'][:value] if metadata['EMEASURE_IDENTIFIER']
17
+ attributes = parse_attributes(metadata)
18
+ hqmf_id = json[:hqmf_id]
19
+ hqmf_set_id = json[:hqmf_set_id]
20
+ hqmf_version_number = json[:hqmf_version_number]
21
+ cms_id = "CMS#{emeasure_id}v#{hqmf_version_number}"
22
+
23
+ measure_period = parse_measure_period(json)
24
+ @data_criteria_converter = DataCriteriaConverter.new(json, measure_period)
25
+
26
+ # source data criteria are the original unmodified v2 data criteria
27
+ source_data_criteria = []
28
+ @data_criteria_converter.v2_data_criteria.each {|criteria| source_data_criteria << criteria}
29
+
30
+ # PASS 1
31
+ @population_criteria_converter = PopulationCriteriaConverter.new(json, @data_criteria_converter)
32
+ population_criteria = @population_criteria_converter.population_criteria
33
+
34
+ # PASS 2
35
+ comparison_converter = HQMF::ComparisonConverter.new(@data_criteria_converter)
36
+ comparison_converter.convert_comparisons(population_criteria)
37
+
38
+ # PASS 3
39
+ # specific_occurrence_converter = HQMF::SpecificOccurrenceConverter.new(@data_criteria_converter)
40
+ # specific_occurrence_converter.convert_specific_occurrences(population_criteria)
41
+
42
+ data_criteria = @data_criteria_converter.final_v2_data_criteria
43
+
44
+ populations = @population_criteria_converter.sub_measures
45
+
46
+ doc = HQMF::Document.new(id, hqmf_id, hqmf_set_id, hqmf_version_number, cms_id, title, description, population_criteria, data_criteria, source_data_criteria, attributes, measure_period, populations)
47
+
48
+ HQMF::DocumentConverter.validate(doc, codes) if codes
49
+
50
+ doc
51
+
52
+ end
53
+
54
+ private
55
+
56
+ def self.parse_attributes(metadata)
57
+ attributes = []
58
+ metadata.keys.each do |key|
59
+ attributes << HQMF::Attribute.from_json(metadata[key])
60
+ end
61
+ attributes
62
+ end
63
+
64
+ def self.parse_measure_period(json)
65
+
66
+ # Create a new HQMF::EffectiveTime
67
+ # @param [Value] low
68
+ # @param [Value] high
69
+ # @param [Value] width
70
+ # ----------
71
+ # Create a new HQMF::Value
72
+ # @param [String] type
73
+ # @param [String] unit
74
+ # @param [String] value
75
+ # @param [String] inclusive
76
+ # @param [String] derived
77
+ # @param [String] expression
78
+
79
+ low = HQMF::Value.new('TS',nil, '201201010000',nil, nil, nil)
80
+ high = HQMF::Value.new('TS',nil,'201212312359',nil, nil, nil)
81
+ width = HQMF::Value.new('PQ','a','1',nil, nil, nil)
82
+
83
+ # puts ('need to figure out a way to make dates dynamic')
84
+
85
+ HQMF::EffectiveTime.new(low,high,width)
86
+ end
87
+
88
+ def self.validate(document,codes)
89
+ puts "\t(#{document.id})document is nil!!!!!!!!!!!" unless document
90
+ puts "\t(#{document.id})codes are nil!!!!!!!!!!!" unless codes
91
+ return unless document and codes
92
+
93
+ referenced_oids = document.all_data_criteria.map(&:code_list_id).compact.uniq
94
+
95
+ referenced_oids.each do |oid|
96
+ value_set = codes[oid]
97
+ puts "\tDC (#{document.id},#{document.title}): referenced OID could not be found #{oid}" unless value_set
98
+ end
99
+
100
+ oid_values = document.all_data_criteria.select {|dc| dc.value != nil and dc.value.type == 'CD'}
101
+
102
+ if oid_values.size > 0
103
+ referenced_oids = (oid_values.map {|dc| dc.value.code_list_id }).compact.uniq
104
+ referenced_oids.each do |oid|
105
+ value_set = codes[oid]
106
+ puts "\tVALUE (#{document.id},#{document.title}): referenced OID could not be found #{oid}" unless value_set
107
+ end
108
+ end
109
+
110
+
111
+ oid_negation = document.all_data_criteria.select {|dc| dc.negation_code_list_id != nil}
112
+ if oid_negation.size > 0
113
+ referenced_oids = (oid_negation.map {|dc| dc.negation_code_list_id}).compact.uniq
114
+ referenced_oids.each do |oid|
115
+ value_set = codes[oid]
116
+ puts "\tNEGATION (#{document.id},#{document.title}): referenced OID could not be found #{oid}" unless value_set
117
+ end
118
+ end
119
+
120
+ oid_fields = document.all_data_criteria.select {|dc| dc.field_values != nil}
121
+ if oid_fields.size > 0
122
+ referenced_oids = (oid_fields.map{|dc| dc.field_values.map {|key,field| puts "field: #{key} is nil" unless field || key.match(/DATETIME/); field.code_list_id if field != nil and field.type == 'CD'}}).flatten.compact.uniq
123
+ referenced_oids.each do |oid|
124
+ value_set = codes[oid]
125
+ puts "\tFIELDS (#{document.id},#{document.title}): referenced OID could not be found #{oid}" unless value_set
126
+ end
127
+ end
128
+
129
+ end
130
+
131
+
132
+ end
133
+ end