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.
Files changed (264) hide show
  1. data/Gemfile +5 -0
  2. data/lib/health-data-standards.rb +55 -12
  3. data/lib/health-data-standards/export/c32.rb +7 -6
  4. data/lib/health-data-standards/export/cat_1.rb +18 -0
  5. data/lib/health-data-standards/export/ccda.rb +8 -8
  6. data/lib/health-data-standards/export/green_c32/entry.rb +8 -5
  7. data/lib/health-data-standards/export/green_c32/export_generator.rb +1 -1
  8. data/lib/health-data-standards/export/green_c32/record.rb +10 -7
  9. data/lib/health-data-standards/export/hdata/metadata.rb +7 -6
  10. data/lib/health-data-standards/export/helper/cat1_view_helper.rb +133 -0
  11. data/lib/health-data-standards/export/helper/gc32_view_helper.rb +39 -0
  12. data/lib/health-data-standards/export/helper/html_view_helper.rb +23 -0
  13. data/lib/health-data-standards/export/html.rb +22 -10
  14. data/lib/health-data-standards/export/qrda/entry_template_resolver.rb +31 -0
  15. data/lib/health-data-standards/export/qrda/hqmf-qrda-oids.json +638 -0
  16. data/lib/health-data-standards/export/rendering_context.rb +37 -3
  17. data/lib/health-data-standards/export/template_helper.rb +20 -23
  18. data/lib/health-data-standards/export/view_helper.rb +8 -42
  19. data/lib/health-data-standards/import/bundle/importer.rb +148 -0
  20. data/lib/health-data-standards/import/c32/care_goal_importer.rb +14 -26
  21. data/lib/health-data-standards/import/c32/condition_importer.rb +12 -51
  22. data/lib/health-data-standards/import/c32/immunization_importer.rb +9 -27
  23. data/lib/health-data-standards/import/c32/insurance_provider_importer.rb +24 -21
  24. data/lib/health-data-standards/import/c32/patient_importer.rb +27 -34
  25. data/lib/health-data-standards/import/cat1/diagnosis_active_importer.rb +19 -0
  26. data/lib/health-data-standards/import/cat1/diagnosis_inactive_importer.rb +19 -0
  27. data/lib/health-data-standards/import/cat1/diagnostic_study_order_importer.rb +24 -0
  28. data/lib/health-data-standards/import/cat1/encounter_order_importer.rb +24 -0
  29. data/lib/health-data-standards/import/cat1/entry_package.rb +25 -0
  30. data/lib/health-data-standards/import/cat1/gestational_age_importer.rb +42 -0
  31. data/lib/health-data-standards/import/cat1/lab_order_importer.rb +24 -0
  32. data/lib/health-data-standards/import/cat1/medication_dispensed_importer.rb +13 -0
  33. data/lib/health-data-standards/import/cat1/patient_importer.rb +104 -0
  34. data/lib/health-data-standards/import/cat1/procedure_intolerance_importer.rb +13 -0
  35. data/lib/health-data-standards/import/cat1/procedure_order_importer.rb +38 -0
  36. data/lib/health-data-standards/import/cat1/tobacco_use_importer.rb +19 -0
  37. data/lib/health-data-standards/import/ccda/allergy_importer.rb +2 -8
  38. data/lib/health-data-standards/import/ccda/care_goal_importer.rb +1 -1
  39. data/lib/health-data-standards/import/ccda/condition_importer.rb +2 -3
  40. data/lib/health-data-standards/import/ccda/encounter_importer.rb +2 -5
  41. data/lib/health-data-standards/import/ccda/immunization_importer.rb +1 -3
  42. data/lib/health-data-standards/import/ccda/insurance_provider_importer.rb +1 -1
  43. data/lib/health-data-standards/import/ccda/medical_equipment_importer.rb +2 -4
  44. data/lib/health-data-standards/import/ccda/medication_importer.rb +2 -4
  45. data/lib/health-data-standards/import/ccda/patient_importer.rb +1 -1
  46. data/lib/health-data-standards/import/ccda/procedure_importer.rb +2 -6
  47. data/lib/health-data-standards/import/ccda/result_importer.rb +2 -5
  48. data/lib/health-data-standards/import/ccda/vital_sign_importer.rb +2 -3
  49. data/lib/health-data-standards/import/cda/allergy_importer.rb +32 -0
  50. data/lib/health-data-standards/import/cda/condition_importer.rb +51 -0
  51. data/lib/health-data-standards/import/{c32 → cda}/encounter_importer.rb +9 -35
  52. data/lib/health-data-standards/import/cda/entry_finder.rb +22 -0
  53. data/lib/health-data-standards/import/{c32 → cda}/locatable_import_utils.rb +2 -3
  54. data/lib/health-data-standards/import/cda/medical_equipment_importer.rb +24 -0
  55. data/lib/health-data-standards/import/{c32 → cda}/medication_importer.rb +13 -15
  56. data/lib/health-data-standards/import/cda/narrative_reference_handler.rb +35 -0
  57. data/lib/health-data-standards/import/{c32 → cda}/organization_importer.rb +1 -1
  58. data/lib/health-data-standards/import/cda/procedure_importer.rb +35 -0
  59. data/lib/health-data-standards/import/{c32 → cda}/provider_importer.rb +4 -8
  60. data/lib/health-data-standards/import/cda/result_importer.rb +31 -0
  61. data/lib/health-data-standards/import/{c32 → cda}/section_importer.rb +26 -48
  62. data/lib/health-data-standards/import/{c32 → cda}/vital_sign_importer.rb +2 -3
  63. data/lib/health-data-standards/import/green_c32/section_importer.rb +8 -6
  64. data/lib/health-data-standards/import/provider_import_utils.rb +2 -2
  65. data/lib/health-data-standards/models/address.rb +1 -1
  66. data/lib/health-data-standards/models/condition.rb +7 -6
  67. data/lib/health-data-standards/models/cqm/bundle.rb +45 -0
  68. data/lib/health-data-standards/models/cqm/measure.rb +36 -0
  69. data/lib/health-data-standards/models/guarantor.rb +1 -0
  70. data/lib/health-data-standards/models/insurance_provider.rb +2 -0
  71. data/lib/health-data-standards/models/order_information.rb +2 -0
  72. data/lib/health-data-standards/models/record.rb +6 -5
  73. data/lib/health-data-standards/models/svs/value_set.rb +1 -1
  74. data/lib/health-data-standards/railtie.rb +11 -0
  75. data/lib/health-data-standards/tasks/bundle.rake +107 -0
  76. data/lib/health-data-standards/util/code_system_helper.rb +9 -1
  77. data/lib/hqmf-generator/attribute.xml.erb +11 -0
  78. data/lib/hqmf-generator/characteristic_criteria.xml.erb +21 -0
  79. data/lib/hqmf-generator/code.xml.erb +13 -0
  80. data/lib/hqmf-generator/condition_criteria.xml.erb +22 -0
  81. data/lib/hqmf-generator/derivation.xml.erb +6 -0
  82. data/lib/hqmf-generator/description.xml.erb +1 -0
  83. data/lib/hqmf-generator/document.xml.erb +63 -0
  84. data/lib/hqmf-generator/effective_time.xml.erb +4 -0
  85. data/lib/hqmf-generator/encounter_criteria.xml.erb +21 -0
  86. data/lib/hqmf-generator/field.xml.erb +28 -0
  87. data/lib/hqmf-generator/hqmf-generator.rb +292 -0
  88. data/lib/hqmf-generator/observation_criteria.xml.erb +25 -0
  89. data/lib/hqmf-generator/population_criteria.xml.erb +23 -0
  90. data/lib/hqmf-generator/precondition.xml.erb +14 -0
  91. data/lib/hqmf-generator/procedure_criteria.xml.erb +22 -0
  92. data/lib/hqmf-generator/reason.xml.erb +3 -0
  93. data/lib/hqmf-generator/reference.xml.erb +3 -0
  94. data/lib/hqmf-generator/source.xml.erb +6 -0
  95. data/lib/hqmf-generator/specific_occurrence.xml.erb +7 -0
  96. data/lib/hqmf-generator/subset.xml.erb +8 -0
  97. data/lib/hqmf-generator/substance_criteria.xml.erb +26 -0
  98. data/lib/hqmf-generator/supply_criteria.xml.erb +26 -0
  99. data/lib/hqmf-generator/template_id.xml.erb +5 -0
  100. data/lib/hqmf-generator/temporal_relationship.xml.erb +6 -0
  101. data/lib/hqmf-generator/value.xml.erb +24 -0
  102. data/lib/hqmf-generator/variable_criteria.xml.erb +12 -0
  103. data/lib/hqmf-model/attribute.rb +35 -0
  104. data/lib/hqmf-model/data_criteria.json +1123 -0
  105. data/lib/hqmf-model/data_criteria.rb +344 -0
  106. data/lib/hqmf-model/document.rb +178 -0
  107. data/lib/hqmf-model/population_criteria.rb +96 -0
  108. data/lib/hqmf-model/precondition.rb +91 -0
  109. data/lib/hqmf-model/types.rb +319 -0
  110. data/lib/hqmf-model/utilities.rb +52 -0
  111. data/lib/hqmf-parser.rb +56 -0
  112. data/lib/hqmf-parser/1.0/attribute.rb +68 -0
  113. data/lib/hqmf-parser/1.0/comparison.rb +34 -0
  114. data/lib/hqmf-parser/1.0/data_criteria.rb +92 -0
  115. data/lib/hqmf-parser/1.0/data_criteria_oid_xpath.json +91 -0
  116. data/lib/hqmf-parser/1.0/document.rb +203 -0
  117. data/lib/hqmf-parser/1.0/expression.rb +58 -0
  118. data/lib/hqmf-parser/1.0/observation.rb +61 -0
  119. data/lib/hqmf-parser/1.0/population_criteria.rb +75 -0
  120. data/lib/hqmf-parser/1.0/precondition.rb +89 -0
  121. data/lib/hqmf-parser/1.0/range.rb +65 -0
  122. data/lib/hqmf-parser/1.0/restriction.rb +160 -0
  123. data/lib/hqmf-parser/1.0/utilities.rb +41 -0
  124. data/lib/hqmf-parser/2.0/data_criteria.rb +319 -0
  125. data/lib/hqmf-parser/2.0/document.rb +165 -0
  126. data/lib/hqmf-parser/2.0/population_criteria.rb +53 -0
  127. data/lib/hqmf-parser/2.0/precondition.rb +44 -0
  128. data/lib/hqmf-parser/2.0/types.rb +223 -0
  129. data/lib/hqmf-parser/2.0/utilities.rb +30 -0
  130. data/lib/hqmf-parser/converter/pass1/data_criteria_converter.rb +252 -0
  131. data/lib/hqmf-parser/converter/pass1/document_converter.rb +185 -0
  132. data/lib/hqmf-parser/converter/pass1/population_criteria_converter.rb +165 -0
  133. data/lib/hqmf-parser/converter/pass1/precondition_converter.rb +173 -0
  134. data/lib/hqmf-parser/converter/pass1/precondition_extractor.rb +188 -0
  135. data/lib/hqmf-parser/converter/pass1/simple_data_criteria.rb +26 -0
  136. data/lib/hqmf-parser/converter/pass1/simple_operator.rb +89 -0
  137. data/lib/hqmf-parser/converter/pass1/simple_population_criteria.rb +10 -0
  138. data/lib/hqmf-parser/converter/pass1/simple_precondition.rb +51 -0
  139. data/lib/hqmf-parser/converter/pass1/simple_restriction.rb +64 -0
  140. data/lib/hqmf-parser/converter/pass2/comparison_converter.rb +112 -0
  141. data/lib/hqmf-parser/converter/pass2/operator_converter.rb +102 -0
  142. data/lib/hqmf-parser/parser.rb +54 -0
  143. data/lib/hqmf-parser/value_sets/value_set_parser.rb +241 -0
  144. data/lib/util/counter.rb +20 -0
  145. data/templates/{_allergies.c32.erb → c32/_allergies.c32.erb} +0 -0
  146. data/templates/{_allergies_no_current.c32.erb → c32/_allergies_no_current.c32.erb} +0 -0
  147. data/templates/{_care_goals.c32.erb → c32/_care_goals.c32.erb} +0 -0
  148. data/templates/{_code_with_reference.c32.erb → c32/_code_with_reference.c32.erb} +0 -0
  149. data/templates/{_conditions.c32.erb → c32/_conditions.c32.erb} +0 -0
  150. data/templates/{_conditions_no_current.c32.erb → c32/_conditions_no_current.c32.erb} +0 -0
  151. data/templates/{_encounters.c32.erb → c32/_encounters.c32.erb} +0 -0
  152. data/templates/{_immunizations.c32.erb → c32/_immunizations.c32.erb} +0 -0
  153. data/templates/{_medical_equipment.c32.erb → c32/_medical_equipment.c32.erb} +0 -0
  154. data/templates/{_medications.c32.erb → c32/_medications.c32.erb} +0 -0
  155. data/templates/{_medications_no_current.c32.erb → c32/_medications_no_current.c32.erb} +0 -0
  156. data/templates/{_narrative_block.c32.erb → c32/_narrative_block.c32.erb} +0 -0
  157. data/templates/{_procedures.c32.erb → c32/_procedures.c32.erb} +0 -0
  158. data/templates/{_results.c32.erb → c32/_results.c32.erb} +0 -0
  159. data/templates/{_social_history.c32.erb → c32/_social_history.c32.erb} +0 -0
  160. data/templates/{_vital_signs.c32.erb → c32/_vital_signs.c32.erb} +0 -0
  161. data/templates/{show.c32.erb → c32/show.c32.erb} +0 -0
  162. data/templates/cat1/_2.16.840.1.113883.10.20.22.4.85.cat1.erb +18 -0
  163. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.1.cat1.erb +14 -0
  164. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.101.cat1.erb +25 -0
  165. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.103.cat1.erb +12 -0
  166. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.105.cat1.erb +60 -0
  167. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.11.cat1.erb +41 -0
  168. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.12.cat1.erb +50 -0
  169. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.13.cat1.erb +37 -0
  170. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.14.cat1.erb +35 -0
  171. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.17.cat1.erb +22 -0
  172. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.18.cat1.erb +21 -0
  173. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.2.cat1.erb +28 -0
  174. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.20.cat1.erb +20 -0
  175. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.22.cat1.erb +21 -0
  176. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.23.cat1.erb +71 -0
  177. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.28.cat1.erb +20 -0
  178. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.3.cat1.erb +24 -0
  179. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.31.cat1.erb +20 -0
  180. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.32.cat1.erb +15 -0
  181. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.34.cat1.erb +58 -0
  182. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.37.cat1.erb +20 -0
  183. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.38.cat1.erb +16 -0
  184. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.4.cat1.erb +27 -0
  185. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.40.cat1.erb +17 -0
  186. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.41.cat1.erb +38 -0
  187. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.42.cat1.erb +38 -0
  188. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.43.cat1.erb +24 -0
  189. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.44.cat1.erb +24 -0
  190. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.45.cat1.erb +26 -0
  191. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.46.cat1.erb +30 -0
  192. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.47.cat1.erb +26 -0
  193. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.51.cat1.erb +13 -0
  194. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.54.cat1.erb +16 -0
  195. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.55.cat1.erb +10 -0
  196. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.57.cat1.erb +19 -0
  197. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.59.cat1.erb +17 -0
  198. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.62.cat1.erb +36 -0
  199. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.63.cat1.erb +23 -0
  200. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.64.cat1.erb +29 -0
  201. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.66.cat1.erb +34 -0
  202. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.69.cat1.erb +23 -0
  203. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.7.cat1.erb +30 -0
  204. data/templates/cat1/_2.16.840.1.113883.10.20.24.3.76.cat1.erb +32 -0
  205. data/templates/cat1/_measures.cat1.erb +66 -0
  206. data/templates/cat1/_medication_details.cat1.erb +9 -0
  207. data/templates/cat1/_ordinality.cat1.erb +4 -0
  208. data/templates/cat1/_patient_data.cat1.erb +14 -0
  209. data/templates/cat1/_reason.cat1.erb +16 -0
  210. data/templates/cat1/_record_target.cat1.erb +39 -0
  211. data/templates/cat1/_reporting_parameters.cat1.erb +24 -0
  212. data/templates/cat1/_result_value.cat1.erb +16 -0
  213. data/templates/cat1/show.cat1.erb +125 -0
  214. data/templates/{_address.gc32.erb → gc32/_address.gc32.erb} +1 -1
  215. data/templates/gc32/_advance_directive.gc32.erb +5 -0
  216. data/templates/gc32/_allergy.gc32.erb +12 -0
  217. data/templates/{_care_goal.gc32.erb → gc32/_care_goal.gc32.erb} +1 -1
  218. data/templates/gc32/_condition.gc32.erb +10 -0
  219. data/templates/gc32/_encounter.gc32.erb +28 -0
  220. data/templates/gc32/_entry.gc32.erb +3 -0
  221. data/templates/gc32/_entry_attributes.gc32.erb +10 -0
  222. data/templates/gc32/_immunization.gc32.erb +9 -0
  223. data/templates/gc32/_insurance_provider.gc32.erb +28 -0
  224. data/templates/gc32/_medical_equipment.gc32.erb +6 -0
  225. data/templates/gc32/_medication.gc32.erb +91 -0
  226. data/templates/{_name.gc32.erb → gc32/_name.gc32.erb} +0 -0
  227. data/templates/gc32/_organization.gc32.erb +10 -0
  228. data/templates/gc32/_person_attributes.gc32.erb +7 -0
  229. data/templates/gc32/_procedure.gc32.erb +9 -0
  230. data/templates/gc32/_provider.gc32.erb +9 -0
  231. data/templates/gc32/_result.gc32.erb +12 -0
  232. data/templates/gc32/_social_history.gc32.erb +6 -0
  233. data/templates/{_support.gc32.erb → gc32/_support.gc32.erb} +4 -3
  234. data/templates/gc32/_telecom.gc32.erb +1 -0
  235. data/templates/gc32/_vital_sign.gc32.erb +4 -0
  236. data/templates/{record.gc32.erb → gc32/record.gc32.erb} +26 -10
  237. data/templates/html/_entries_by_encounter.html.erb +2 -2
  238. data/templates/html/_entries_by_section.html.erb +1 -1
  239. data/templates/html/_entry.html.erb +16 -21
  240. data/templates/html/_header.html.erb +1 -1
  241. data/templates/html/_section.html.erb +1 -1
  242. data/templates/html/show.html.erb +23 -2
  243. data/templates/metadata.hdata.erb +3 -3
  244. metadata +282 -54
  245. data/lib/health-data-standards/import/c32/allergy_importer.rb +0 -47
  246. data/lib/health-data-standards/import/c32/medical_equipment_importer.rb +0 -45
  247. data/lib/health-data-standards/import/c32/procedure_importer.rb +0 -62
  248. data/lib/health-data-standards/import/c32/result_importer.rb +0 -56
  249. data/templates/_advance_directive.gc32.erb +0 -8
  250. data/templates/_allergy.gc32.erb +0 -23
  251. data/templates/_condition.gc32.erb +0 -9
  252. data/templates/_encounter.gc32.erb +0 -26
  253. data/templates/_entry.gc32.erb +0 -14
  254. data/templates/_immunization.gc32.erb +0 -11
  255. data/templates/_insurance_provider.gc32.erb +0 -0
  256. data/templates/_medical_equipment.gc32.erb +0 -7
  257. data/templates/_medication.gc32.erb +0 -72
  258. data/templates/_organization.gc32.erb +0 -10
  259. data/templates/_procedure.gc32.erb +0 -10
  260. data/templates/_provider.gc32.erb +0 -19
  261. data/templates/_result.gc32.erb +0 -16
  262. data/templates/_social_history.gc32.erb +0 -8
  263. data/templates/_telecom.gc32.erb +0 -1
  264. 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