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,185 @@
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
+ metadata = json[:metadata]
13
+ metadata.keys.each {|key| metadata[key.to_s] = metadata[key]; metadata.delete(key.to_sym)}
14
+
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
+ backfill_patient_characteristics_with_codes(doc, codes)
49
+
50
+ HQMF::DocumentConverter.validate(doc, codes)
51
+
52
+ doc
53
+
54
+ end
55
+
56
+ private
57
+
58
+ def self.parse_attributes(metadata)
59
+ attributes = []
60
+ metadata.keys.each do |key|
61
+ attribute_hash = metadata[key]
62
+ code = attribute_hash[:code]
63
+ value = attribute_hash[:value]
64
+ unit = attribute_hash[:unit]
65
+ name = attribute_hash[:name]
66
+ attributes << HQMF::Attribute.new(key,code,value,unit,name)
67
+ end
68
+ attributes
69
+ end
70
+
71
+
72
+ # patient characteristics data criteria such as GENDER require looking at the codes to determine if the
73
+ # measure is interested in Males or Females. This process is awkward, and thus is done as a separate
74
+ # step after the document has been converted.
75
+ def self.backfill_patient_characteristics_with_codes(doc, codes)
76
+
77
+ [].concat(doc.all_data_criteria).concat(doc.source_data_criteria).each do |data_criteria|
78
+ if (data_criteria.type == :characteristic and !data_criteria.property.nil?)
79
+ if (codes)
80
+ value_set = codes[data_criteria.code_list_id]
81
+ puts "\tno value set for unknown patient characteristic: #{data_criteria.id}" unless value_set
82
+ else
83
+ puts "\tno code set to back fill: #{data_criteria.title}"
84
+ next
85
+ end
86
+
87
+ if (data_criteria.property == :gender)
88
+ key = value_set.keys[0]
89
+ data_criteria.value = HQMF::Coded.new('CD','Administrative Sex',value_set[key].first)
90
+ else
91
+ data_criteria.inline_code_list = value_set
92
+ end
93
+
94
+ elsif (data_criteria.type == :characteristic)
95
+ if (codes)
96
+ value_set = codes[data_criteria.code_list_id]
97
+ if (value_set)
98
+ # this is looking for a birthdate characteristic that is set as a generic characteristic but points to a loinc code set
99
+ if (value_set['LOINC'] and value_set['LOINC'].first == '21112-8')
100
+ data_criteria.definition = 'patient_characteristic_birthdate'
101
+ end
102
+ # this is looking for a gender characteristic that is set as a generic characteristic
103
+ gender_key = (value_set.keys.select {|set| set == 'Administrative Sex' || set == 'AdministrativeSex'}).first
104
+ if (gender_key and ['M','F'].include? value_set[gender_key].first)
105
+ data_criteria.definition = 'patient_characteristic_gender'
106
+ data_criteria.value = HQMF::Coded.new('CD','Gender',value_set[gender_key].first)
107
+ end
108
+ end
109
+ end
110
+
111
+ end
112
+ end
113
+ end
114
+
115
+
116
+ def self.parse_measure_period(json)
117
+
118
+ # Create a new HQMF::EffectiveTime
119
+ # @param [Value] low
120
+ # @param [Value] high
121
+ # @param [Value] width
122
+ # ----------
123
+ # Create a new HQMF::Value
124
+ # @param [String] type
125
+ # @param [String] unit
126
+ # @param [String] value
127
+ # @param [String] inclusive
128
+ # @param [String] derived
129
+ # @param [String] expression
130
+
131
+ low = HQMF::Value.new('TS',nil, '201201010000',nil, nil, nil)
132
+ high = HQMF::Value.new('TS',nil,'201212312359',nil, nil, nil)
133
+ width = HQMF::Value.new('PQ','a','1',nil, nil, nil)
134
+
135
+ # puts ('need to figure out a way to make dates dynamic')
136
+
137
+ HQMF::EffectiveTime.new(low,high,width)
138
+ end
139
+
140
+ def self.validate(document,codes)
141
+ puts "\t(#{document.id})document is nil!!!!!!!!!!!" unless document
142
+ puts "\t(#{document.id})codes are nil!!!!!!!!!!!" unless codes
143
+ return unless document and codes
144
+
145
+ referenced_oids = document.all_data_criteria.map(&:code_list_id).compact.uniq
146
+
147
+ referenced_oids.each do |oid|
148
+ value_set = codes[oid]
149
+ puts "\tDC (#{document.id},#{document.title}): referenced OID could not be found #{oid}" unless value_set
150
+ end
151
+
152
+ oid_values = document.all_data_criteria.select {|dc| dc.value != nil and dc.value.type == 'CD'}
153
+
154
+ if oid_values.size > 0
155
+ referenced_oids = (oid_values.map {|dc| dc.value.code_list_id }).compact.uniq
156
+ referenced_oids.each do |oid|
157
+ value_set = codes[oid]
158
+ puts "\tVALUE (#{document.id},#{document.title}): referenced OID could not be found #{oid}" unless value_set
159
+ end
160
+ end
161
+
162
+
163
+ oid_negation = document.all_data_criteria.select {|dc| dc.negation_code_list_id != nil}
164
+ if oid_negation.size > 0
165
+ referenced_oids = (oid_negation.map {|dc| dc.negation_code_list_id}).compact.uniq
166
+ referenced_oids.each do |oid|
167
+ value_set = codes[oid]
168
+ puts "\tNEGATION (#{document.id},#{document.title}): referenced OID could not be found #{oid}" unless value_set
169
+ end
170
+ end
171
+
172
+ oid_fields = document.all_data_criteria.select {|dc| dc.field_values != nil}
173
+ if oid_fields.size > 0
174
+ 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
175
+ referenced_oids.each do |oid|
176
+ value_set = codes[oid]
177
+ puts "\tFIELDS (#{document.id},#{document.title}): referenced OID could not be found #{oid}" unless value_set
178
+ end
179
+ end
180
+
181
+ end
182
+
183
+
184
+ end
185
+ end
@@ -0,0 +1,165 @@
1
+ module HQMF
2
+ # Class for converting an HQMF 1.0 representation to an HQMF 2.0 representation
3
+ class PopulationCriteriaConverter
4
+
5
+ attr_reader :sub_measures
6
+
7
+ def initialize(doc, data_criteria_converter)
8
+ @doc = doc
9
+ @data_criteria_converter = data_criteria_converter
10
+ @population_criteria_by_id = {}
11
+ @population_criteria_by_key = {}
12
+ @population_reference = {}
13
+ parse()
14
+ build_sub_measures()
15
+ end
16
+
17
+ def population_criteria
18
+ @population_criteria_by_key.values
19
+ end
20
+
21
+ private
22
+
23
+ def build_sub_measures()
24
+ @sub_measures = []
25
+ ipps = @population_criteria_by_id.select {|key, value| value.type == HQMF::PopulationCriteria::IPP}
26
+ denoms = @population_criteria_by_id.select {|key, value| value.type == HQMF::PopulationCriteria::DENOM}
27
+ nums = @population_criteria_by_id.select {|key, value| value.type == HQMF::PopulationCriteria::NUMER}
28
+ msrpopls = @population_criteria_by_id.select {|key, value| value.type == HQMF::PopulationCriteria::MSRPOPL}
29
+ observs = @population_criteria_by_id.select {|key, value| value.type == HQMF::PopulationCriteria::OBSERV}
30
+ excls = @population_criteria_by_id.select {|key, value| value.type == HQMF::PopulationCriteria::DENEX}
31
+ denexcs = @population_criteria_by_id.select {|key, value| value.type == HQMF::PopulationCriteria::DENEXCEP}
32
+
33
+ if (ipps.size<=1 and denoms.size<=1 and nums.size<=1 and excls.size<=1 and denexcs.size<=1 and msrpopls.size<=1 and observs.size<=1 )
34
+ sub_measure = {}
35
+
36
+ sub_measure[HQMF::PopulationCriteria::IPP] = HQMF::PopulationCriteria::IPP if ipps.size > 0
37
+ sub_measure[HQMF::PopulationCriteria::DENOM] = HQMF::PopulationCriteria::DENOM if denoms.size > 0
38
+ sub_measure[HQMF::PopulationCriteria::NUMER] = HQMF::PopulationCriteria::NUMER if nums.size > 0
39
+ sub_measure[HQMF::PopulationCriteria::DENEXCEP] = HQMF::PopulationCriteria::DENEXCEP if denexcs.size > 0
40
+ sub_measure[HQMF::PopulationCriteria::DENEX] = HQMF::PopulationCriteria::DENEX if excls.size > 0
41
+ sub_measure[HQMF::PopulationCriteria::MSRPOPL] = HQMF::PopulationCriteria::MSRPOPL if msrpopls.size > 0
42
+ sub_measure[HQMF::PopulationCriteria::OBSERV] = HQMF::PopulationCriteria::OBSERV if observs.size > 0
43
+
44
+ @sub_measures << sub_measure
45
+ else
46
+
47
+ nums.each do |num_id, num|
48
+ @sub_measures << {HQMF::PopulationCriteria::NUMER => num.id}
49
+ end
50
+ msrpopls.each do |popl_id, popl|
51
+ @sub_measures << {HQMF::PopulationCriteria::MSRPOPL => popl.id}
52
+ end
53
+ apply_to_submeasures(@sub_measures, HQMF::PopulationCriteria::DENOM, denoms.values)
54
+ apply_to_submeasures(@sub_measures, HQMF::PopulationCriteria::IPP, ipps.values)
55
+
56
+ apply_to_submeasures(@sub_measures, HQMF::PopulationCriteria::DENEX, excls.values, HQMF::PopulationCriteria::IPP, get_unmatched_population_keys(ipps, excls))
57
+ apply_to_submeasures(@sub_measures, HQMF::PopulationCriteria::DENEXCEP, denexcs.values, HQMF::PopulationCriteria::DENOM, get_unmatched_population_keys(denoms, denexcs))
58
+
59
+ apply_to_submeasures(@sub_measures, HQMF::PopulationCriteria::OBSERV, observs.values)
60
+
61
+ keep = []
62
+
63
+ @sub_measures.each do |sub|
64
+
65
+ value = sub
66
+ HQMF::PopulationCriteria::ALL_POPULATION_CODES.each do |type|
67
+ key = sub[type]
68
+ if (key)
69
+ reference_id = @population_reference[key]
70
+ reference = @population_criteria_by_id[reference_id] if reference_id
71
+ if (reference)
72
+ criteria = @population_criteria_by_key[sub[reference.type]]
73
+ value['stratification'] = criteria.stratification_id if criteria.stratification_id
74
+ value = nil if (sub[reference.type] != reference.id and criteria.stratification_id.nil?)
75
+ end
76
+ end
77
+ end
78
+ keep << value if (value)
79
+ end
80
+
81
+ keep.each_with_index do |sub, i|
82
+ sub['title'] = "Population #{i+1}"
83
+ sub['id'] = "Population#{i+1}"
84
+ end
85
+
86
+ @sub_measures = keep
87
+
88
+ end
89
+ end
90
+
91
+ # source are things like exceptions or exclusions, target are IPP, or denom
92
+ # we want to find any denoms or IPPs that do not have exceptions or exclusions
93
+ def get_unmatched_population_keys(target, source)
94
+ return [] if target.length == source.length
95
+ all_target_keys = target.values.map(&:id)
96
+ with_ref_keys = source.values.map(&:id).map {|key| @population_criteria_by_id[@population_reference[key]].id if @population_criteria_by_id[@population_reference[key]]}
97
+ # if we have a population without a reference, we cannot trust the results.
98
+ return [] if with_ref_keys.include? nil
99
+ all_target_keys - with_ref_keys
100
+ end
101
+
102
+ # create a copy of each submeasre adding on the new values of the given type
103
+ # skip the unpaired values. Unpaired values are denominators without exclusions or populations without exceptions
104
+ def apply_to_submeasures(subs, type, values, unpaired_type=nil, unpaired_keys=[])
105
+ new_subs = []
106
+ subs.each do |sub|
107
+ # this unless prevents us from forcing an exclusion or excepion onto a measure that has a submeasure without
108
+ # an exclusion or exception, but other populations with an exclusion or excepion.
109
+ unless unpaired_keys.include? sub[unpaired_type]
110
+ # duplicate each new value if it is set, otherwise set this key on each submeasure.
111
+ values.each do |value|
112
+ if (sub[type] and sub[type] != value.id)
113
+ tmp = {}
114
+ HQMF::PopulationCriteria::ALL_POPULATION_CODES.each do |key|
115
+ tmp[key] = sub[key] if sub[key]
116
+ end
117
+ sub = tmp
118
+ new_subs << sub
119
+ end
120
+ sub[type] = value.id
121
+ end
122
+ end
123
+ end
124
+ subs.concat(new_subs)
125
+ end
126
+
127
+ def find_sub_measures(type, value)
128
+ found = []
129
+ @sub_measures.each do |sub_measure|
130
+ found << sub_measure if sub_measure[type] and sub_measure[type] == value.id
131
+ end
132
+ found
133
+ end
134
+
135
+ def parse
136
+ @doc[:logic].each do |key,criteria|
137
+ @population_criteria_by_key[key] = convert(key.to_s, criteria)
138
+ end
139
+ end
140
+
141
+ def convert(key, population_criteria)
142
+
143
+ # @param [String] id
144
+ # @param [Array#Precondition] preconditions
145
+
146
+ preconditions = HQMF::PreconditionConverter.parse_preconditions(population_criteria[:preconditions],@data_criteria_converter)
147
+ hqmf_id = population_criteria[:hqmf_id] || population_criteria[:id]
148
+ id = population_criteria[:id]
149
+ type = population_criteria[:code]
150
+ reference = population_criteria[:reference]
151
+ title = population_criteria[:title]
152
+
153
+ criteria = HQMF::Converter::SimplePopulationCriteria.new(key, hqmf_id, type, preconditions, title)
154
+ # mark the 2.0 simple population criteria as a stratification... this allows us to create the cartesian product for this in the populations
155
+ criteria.stratification_id = population_criteria[:stratification_id]
156
+
157
+ @population_criteria_by_id[id] = criteria
158
+ @population_reference[key] = reference
159
+
160
+ criteria
161
+
162
+ end
163
+
164
+ end
165
+ end
@@ -0,0 +1,173 @@
1
+ module HQMF
2
+ # Class for converting an HQMF 1.0 representation to an HQMF 2.0 representation
3
+ class PreconditionConverter
4
+
5
+ def self.parse_preconditions(source,data_criteria_converter)
6
+
7
+ # preconditions = []
8
+ # source.each do |precondition|
9
+ # preconditions << HQMF::PreconditionConverter.parse_precondition(precondition,data_criteria_converter)
10
+ # end
11
+ #
12
+ # preconditions
13
+
14
+ parse_and_merge_preconditions(source,data_criteria_converter)
15
+ end
16
+
17
+ # converts a precondtion to a hqmf model
18
+ def self.parse_precondition(precondition,data_criteria_converter)
19
+
20
+ # grab child preconditions, and parse recursively
21
+ preconditions = parse_and_merge_preconditions(precondition[:preconditions],data_criteria_converter) if precondition[:preconditions] || []
22
+
23
+ preconditions_from_restrictions = HQMF::PreconditionExtractor.extract_preconditions_from_restrictions(precondition[:restrictions], data_criteria_converter)
24
+
25
+ # driv preconditions are preconditions that are the children of an expression
26
+ driv_preconditions = []
27
+ preconditions_from_restrictions.delete_if {|element| driv_preconditions << element if element.is_a? HQMF::Converter::SimpleRestriction and element.operator.type == 'DRIV'}
28
+
29
+ apply_restrictions_to_comparisons(preconditions, preconditions_from_restrictions) unless preconditions_from_restrictions.empty?
30
+
31
+ conjunction_code = convert_logical_conjunction(precondition[:conjunction])
32
+
33
+ if (precondition[:expression])
34
+ # this is for things like COUNT
35
+ type = precondition[:expression][:type]
36
+ operator = HQMF::Converter::SimpleOperator.new(HQMF::Converter::SimpleOperator.find_category(type), type, HQMF::Converter::SimpleOperator.parse_value(precondition[:expression][:value]))
37
+ children = []
38
+ if driv_preconditions and !driv_preconditions.empty?
39
+ children = driv_preconditions.map(&:preconditions).flatten
40
+ end
41
+
42
+ reference = nil
43
+ # take the conjunction code from the parent precondition
44
+
45
+ restriction = HQMF::Converter::SimpleRestriction.new(operator, nil, children)
46
+
47
+ comparison_precondition = HQMF::Converter::SimplePrecondition.new(nil,[restriction],reference,conjunction_code, false)
48
+ comparison_precondition.klass = HQMF::Converter::SimplePrecondition::COMPARISON
49
+ comparison_precondition.subset_comparison = true
50
+ preconditions << comparison_precondition
51
+ end
52
+
53
+ reference = nil
54
+
55
+ negation = precondition[:negation]
56
+
57
+
58
+ if (precondition[:comparison])
59
+ preconditions ||= []
60
+ comparison_precondition = HQMF::PreconditionExtractor.convert_comparison_to_precondition(precondition[:comparison],data_criteria_converter)
61
+ preconditions << comparison_precondition
62
+ end
63
+
64
+
65
+ if (precondition[:subset])
66
+ # this is for things like FIRST on preconditions
67
+ type = precondition[:subset]
68
+ operator = HQMF::Converter::SimpleOperator.new(HQMF::Converter::SimpleOperator.find_category(type), type, nil)
69
+ children = preconditions
70
+
71
+ reference = nil
72
+ # take the conjunction code from the parent precondition
73
+
74
+ restriction = HQMF::Converter::SimpleRestriction.new(operator, nil, children)
75
+
76
+ comparison_precondition = HQMF::Converter::SimplePrecondition.new(nil,[restriction],reference,conjunction_code, false)
77
+ comparison_precondition.klass = HQMF::Converter::SimplePrecondition::COMPARISON
78
+ preconditions = [comparison_precondition]
79
+ end
80
+
81
+
82
+ HQMF::Converter::SimplePrecondition.new(nil,preconditions,reference,conjunction_code, negation)
83
+
84
+ end
85
+
86
+ def self.get_comparison_preconditions(preconditions)
87
+ comparisons = []
88
+ preconditions.each do |precondition|
89
+ if (precondition.comparison? and !precondition.subset_comparison)
90
+ comparisons << precondition
91
+ elsif(precondition.has_preconditions?)
92
+ comparisons.concat(get_comparison_preconditions(precondition.preconditions))
93
+ else
94
+ raise "precondition with no comparison or children... not valid"
95
+ end
96
+ end
97
+ comparisons
98
+ end
99
+
100
+ def self.apply_restrictions_to_comparisons(preconditions, restrictions)
101
+ comparisons = get_comparison_preconditions(preconditions)
102
+ raise "no comparisons to apply restriction to" if comparisons.empty?
103
+ comparisons.each do |comparison|
104
+ comparison.preconditions.concat(restrictions)
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+
111
+ def self.parse_and_merge_preconditions(source,data_criteria_converter)
112
+ return [] unless source and source.size > 0
113
+ preconditions_by_conjunction = {}
114
+ source.each do |precondition|
115
+ parsed = HQMF::PreconditionConverter.parse_precondition(precondition,data_criteria_converter)
116
+ preconditions_by_conjunction[parsed.conjunction_code] ||= []
117
+ preconditions_by_conjunction[parsed.conjunction_code] << parsed
118
+ end
119
+
120
+ merge_precondtion_conjunction_groups(preconditions_by_conjunction)
121
+ end
122
+
123
+ def self.merge_precondtion_conjunction_groups(preconditions_by_conjunction)
124
+ joined = []
125
+ preconditions_by_conjunction.each do |conjunction_code, preconditions|
126
+ sub_conditions = []
127
+ negated_conditions = []
128
+ preconditions.each do |precondition|
129
+ unless (precondition.negation)
130
+ sub_conditions.concat precondition.preconditions if precondition.preconditions
131
+ else
132
+ negated_conditions.concat precondition.preconditions if precondition.preconditions
133
+ end
134
+ end
135
+
136
+ if (!sub_conditions.empty?)
137
+ # if we have negated conditions, add them to a new precondition with an inverted conjunction on a negated precondition
138
+ # the reason we invert the conjunction is because we are turning
139
+ # AND: NOT X
140
+ # AND: NOT Y
141
+ # into
142
+ # NOT: X OR Y
143
+ # (i.e, demorgan's law)
144
+ if (!negated_conditions.empty?)
145
+ inverted_conjunction_code = HQMF::Precondition::INVERSIONS[conjunction_code]
146
+ sub_conditions << HQMF::Converter::SimplePrecondition.new(nil,negated_conditions,nil,inverted_conjunction_code, true)
147
+ end
148
+ joined << HQMF::Converter::SimplePrecondition.new(nil,sub_conditions,nil,conjunction_code, false)
149
+ elsif (!negated_conditions.empty?)
150
+ # invert conjunction based on demorgan's law
151
+ inverted_conjunction_code = HQMF::Precondition::INVERSIONS[conjunction_code]
152
+ joined << HQMF::Converter::SimplePrecondition.new(nil,negated_conditions,nil,inverted_conjunction_code, true)
153
+ end
154
+
155
+ end
156
+ joined
157
+ end
158
+
159
+ def self.convert_logical_conjunction(code)
160
+ case code
161
+ when 'OR'
162
+ HQMF::Precondition::AT_LEAST_ONE_TRUE
163
+ when 'AND'
164
+ HQMF::Precondition::ALL_TRUE
165
+ else
166
+ raise "unsupported logical conjunction code conversion: #{code}"
167
+ end
168
+
169
+ end
170
+
171
+
172
+ end
173
+ end