health-data-standards 2.2.1 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -1,14 +1,48 @@
1
- require 'ostruct'
2
-
3
1
  module HealthDataStandards
4
2
  module Export
3
+ # Used to actually render stuff. A RenderingContext needs to be set up with
4
+ # a template helper and may be provided with extensions.
5
+ #
6
+ # :call-seq:
7
+ # template_helper = HealthDataStandards::Export::TemplateHelper.new('cat1', 'cat1')
8
+ # rendering_context = HealthDataStandards::Export::RenderingContext.new
9
+ # rendering_context.template_helper = template_helper
10
+ # rendering_context.extensions = [HealthDataStandards::Export::Helper::Cat1ViewHelper]
11
+ #
12
+ # In this case, a RenderingContext is being set up to generate Category 1 files. It is
13
+ # passed a template helper to finds the correct ERb templates to render from. It is also
14
+ # given an extension. This is just a Ruby Module which will have its methods exposed to
15
+ # the templates. RenderingContext will assume that extensions is an Array and will include
16
+ # multiple extensions if more than one is provided.
5
17
  class RenderingContext < OpenStruct
6
- include TemplateHelper
7
18
  include ViewHelper
19
+ attr_accessor :template_helper, :extensions
8
20
 
9
21
  def my_binding
10
22
  binding
11
23
  end
24
+
25
+ def render(params)
26
+ erb = nil
27
+ if params[:template]
28
+ erb = @template_helper.template(params[:template])
29
+ elsif params[:partial]
30
+ erb = @template_helper.partial(params[:partial])
31
+ end
32
+
33
+ locals = params[:locals]
34
+ locals ||= {}
35
+ rendering_context = RenderingContext.new(locals)
36
+ rendering_context.template_helper = @template_helper
37
+ if @extensions.present?
38
+ rendering_context.extensions = @extensions
39
+ @extensions.each do |extension|
40
+ rendering_context.extend(extension)
41
+ end
42
+ end
43
+ eruby = Erubis::EscapedEruby.new(erb) # TODO: cache these
44
+ eruby.result(rendering_context.my_binding)
45
+ end
12
46
  end
13
47
  end
14
48
  end
@@ -1,9 +1,20 @@
1
1
  module HealthDataStandards
2
2
  module Export
3
- module TemplateHelper
4
- attr_accessor :template_format
5
- attr_accessor :template_subdir
6
- attr_accessor :template_directory
3
+ # Class that finds ERb templates. Here is how it can be configured:
4
+ # [template_format] What format (C32, CCDA, etc) are we looking for. This will cause
5
+ # the TemplateHelper to look for template_name.template_format.erb
6
+ # [template_subdir] The sub directory where templates live. If none is provided, it
7
+ # will look for templates in the root of the template_directory
8
+ # [template_directory] The root directory to look in for templates. By default, it
9
+ # is in the template folder of this gem. It can be handy to
10
+ # provide a different directory if you want to use this class
11
+ # outside of the HDS gem
12
+ class TemplateHelper
13
+ def initialize(template_format, template_subdir = nil, template_directory = nil)
14
+ @template_format = template_format
15
+ @template_directory = template_directory
16
+ @template_subdir = template_subdir
17
+ end
7
18
 
8
19
  def template_root
9
20
  @template_directory ||= File.dirname(__FILE__)
@@ -15,31 +26,17 @@ module HealthDataStandards
15
26
  end
16
27
  end
17
28
 
29
+ # Returns the raw ERb for the template_name provided. This method will look in
30
+ # template_directory/template_subdir/template_name.template_format.erb
18
31
  def template(template_name)
19
- File.read(File.join(template_root, "#{template_name}.#{self.template_format}.erb"))
32
+ File.read(File.join(template_root, "#{template_name}.#{@template_format}.erb"))
20
33
  end
21
34
 
35
+ # Basically the same template, but prepends an underscore to the template name
36
+ # to mimic the Rails convention for template fragments
22
37
  def partial(partial_name)
23
38
  template("_#{partial_name}")
24
39
  end
25
-
26
- def render(params)
27
- erb = nil
28
- if params[:template]
29
- erb = template(params[:template])
30
- elsif params[:partial]
31
- erb = partial(params[:partial])
32
- end
33
-
34
- locals = params[:locals]
35
- locals ||= {}
36
- rendering_context = RenderingContext.new(locals)
37
- rendering_context.template_format = self.template_format
38
- rendering_context.template_subdir = self.template_subdir
39
- rendering_context.template_directory = self.template_directory
40
- eruby = Erubis::EscapedEruby.new(erb)
41
- eruby.result(rendering_context.my_binding)
42
- end
43
40
  end
44
41
  end
45
42
  end
@@ -4,18 +4,22 @@ module HealthDataStandards
4
4
  def code_display(entry, options={})
5
5
  options['tag_name'] ||= 'code'
6
6
  options['attribute'] ||= :codes
7
+ options['exclude_null_flavor'] ||= false
7
8
  code_string = nil
8
9
  preferred_code = entry.preferred_code(options['preferred_code_sets'], options['attribute'])
9
10
  if preferred_code
10
11
  code_system_oid = HealthDataStandards::Util::CodeSystemHelper.oid_for_code_system(preferred_code['code_set'])
11
12
  code_string = "<#{options['tag_name']} code=\"#{preferred_code['code']}\" codeSystem=\"#{code_system_oid}\" #{options['extra_content']}>"
12
13
  else
13
- code_string = "<#{options['tag_name']} nullFlavor=\"UNK\" #{options['extra_content']}>"
14
+ code_string = "<#{options['tag_name']} "
15
+ code_string += "nullFlavor=\"UNK\" " unless options["exclude_null_flavor"]
16
+ code_string += "#{options['extra_content']}>"
14
17
  end
15
18
 
16
- code_string += "<originalText>#{ERB::Util.html_escape entry.description}</originalText>" if entry.respond_to?(:description)
17
19
 
18
- if entry.respond_to?(:translation_codes)
20
+
21
+ if options["attribute"] == :codes && entry.respond_to?(:translation_codes)
22
+ code_string += "<originalText>#{ERB::Util.html_escape entry.description}</originalText>" if entry.respond_to?(:description)
19
23
  entry.translation_codes(options['preferred_code_sets']).each do |translation|
20
24
  code_string += "<translation code=\"#{translation['code']}\" codeSystem=\"#{HealthDataStandards::Util::CodeSystemHelper.oid_for_code_system(translation['code_set'])}\"/>\n"
21
25
  end
@@ -24,20 +28,7 @@ module HealthDataStandards
24
28
  code_string += "</#{options['tag_name']}>"
25
29
  code_string
26
30
  end
27
-
28
- def gc32_effective_time(entry)
29
- if entry.time
30
- "<effectiveTime value=\"#{Time.at(entry.time).to_formatted_s(:number)}\" />"
31
- elsif entry.start_time || entry.end_time
32
- time = "<effectiveTime>"
33
- time += "<start value=\"#{Time.at(entry.start_time).to_formatted_s(:number)}\" />" if entry.start_time
34
- time += "<end value=\"#{Time.at(entry.end_time).to_formatted_s(:number)}\" />" if entry.end_time
35
- time += "</effectiveTime>"
36
- else
37
- "<effectiveTime />"
38
- end
39
- end
40
-
31
+
41
32
  def status_code_for(entry)
42
33
  case entry.status.to_s.downcase
43
34
  when 'active'
@@ -58,16 +49,6 @@ module HealthDataStandards
58
49
  end
59
50
  end
60
51
 
61
-
62
- def quantity_display(value, tag_name="value")
63
- return unless value
64
- if value.respond_to?(:scalar)
65
- "<#{tag_name} value=\"#{value.scalar}\" units=\"#{value.units}\" />"
66
- else
67
- "<#{tag_name} value=\"#{value['value']}\" units=\"#{value['unit']}\" />"
68
- end
69
- end
70
-
71
52
  def time_if_not_nil(*args)
72
53
  args.compact.map {|t| Time.at(t).utc}.first
73
54
  end
@@ -84,21 +65,6 @@ module HealthDataStandards
84
65
  return ["true","false"].include? (str || "").downcase
85
66
  end
86
67
 
87
- def decode_hqmf_section(section, oid)
88
- if oid
89
- HealthDataStandards::Util::HQMFTemplateHelper.definition_for_template_id(oid)['definition'].pluralize.to_sym
90
- else
91
- section
92
- end
93
- end
94
- def decode_hqmf_status(status, oid)
95
- if oid
96
- HealthDataStandards::Util::HQMFTemplateHelper.definition_for_template_id(oid)['status']
97
- else
98
- status
99
- end
100
- end
101
-
102
68
  def convert_field_to_hash(field, codes)
103
69
  if (codes.is_a? Hash)
104
70
  clean_hash = {}
@@ -0,0 +1,148 @@
1
+ module HealthDataStandards
2
+ module Import
3
+ module Bundle
4
+
5
+ class Importer
6
+ COLLECTION_NAMES = ["bundles", "records", "measures", "selected_measures", "patient_cache", "query_cache", "system.js"]
7
+ DEFAULTS = {clear_db: false,
8
+ type: nil,
9
+ delete_existing: false,
10
+ update_measures: true,
11
+ clear_collections: COLLECTION_NAMES
12
+ }
13
+ # Import a quality bundle into the database. This includes metadata, measures, test patients, supporting JS libraries, and expected results.
14
+ #
15
+ # @param [File] zip The bundle zip file.
16
+ # @param [String] Type of measures to import, either 'ep', 'eh' or nil for all
17
+ # @param [Boolean] keep_existing If true, delete all current collections related to patients and measures.
18
+ def self.import(zip, options={})
19
+ options = DEFAULTS.merge(options)
20
+ bundle_versions = Hash[* HealthDataStandards::CQM::Bundle.where({}).collect{|b| [b._id, b.version]}.flatten]
21
+ # Unpack content from the bundle.
22
+ bundle_contents = unpack_bundle_contents(zip, options[:type])
23
+ bundle = HealthDataStandards::CQM::Bundle.new( JSON.parse(bundle_contents[:bundle]))
24
+
25
+ if bundle_versions.invert[bundle.version] && !(options[:delete_existing] || options[:clear_db])
26
+ raise "A bundle with version #{bundle.version} already exists in the database. "
27
+ end
28
+
29
+ drop_collections(COLLECTION_NAMES+(options[:clear_collections]||[])) if options[:clear_db]
30
+ HealthDataStandards::CQM::Bundle.where({:version => bundle.version}).each {|b| b.delete}
31
+ # Store all JS libraries.
32
+ bundle_contents[:extensions].each do |key, contents|
33
+ save_system_js_fn(key, contents)
34
+ end
35
+
36
+ # Store the bundle metadata.
37
+
38
+
39
+ unless bundle.save
40
+ raise bundle.errors.full_messages.join(",")
41
+ end
42
+
43
+ bundle_id = bundle.id
44
+
45
+
46
+ # Store all measures.
47
+ bundle_contents[:measures].each do |key, contents|
48
+ json = JSON.parse(contents, {:max_nesting => 100})
49
+ measure = json.clone
50
+ # measure = HealthDataStandards::CQM::Measure.new(json)
51
+ measure['bundle_id'] = bundle_id
52
+ Mongoid.default_session["measures"].insert(measure)
53
+
54
+
55
+ if options[:update_measures]
56
+ Mongoid.default_session["measures"].where({hqmf_id: measure["hqmf_id"], sub_id: measure["sub_id"]}).each do |m|
57
+ b = HealthDataStandards::CQM::Bundle.find(m["bundle_id"])
58
+ if b.version < bundle.version
59
+ m.merge!(json)
60
+ Mongoid.default_session["measures"].where({"_id" => m["_id"]}).update(m)
61
+ end
62
+
63
+ end
64
+ end
65
+ end
66
+
67
+
68
+ bundle_contents[:patients].each do |key, contents|
69
+ patient = Record.new( JSON.parse(contents, {:max_nesting => 100}))
70
+ patient['bundle_id'] = bundle_id
71
+ patient.save
72
+ end
73
+
74
+ bundle_contents[:valuesets].each do |key, contents|
75
+ json = JSON.parse(contents, {:max_nesting => 100})
76
+ vs = HealthDataStandards::SVS::ValueSet.new(json)
77
+ vs['bundle_id'] = bundle_id
78
+ vs.save
79
+ end
80
+
81
+ # Store the expected results into the query and patient caches.
82
+ bundle_contents[:results].each do |name, contents|
83
+ collection = name == "by_patient" ? "patient_cache" : "query_cache"
84
+ contents = JSON.parse(contents, {:max_nesting => 100})
85
+
86
+ contents.each {|document|
87
+ document['bundle_id'] = bundle_id
88
+ Mongoid.default_session[collection].insert(document)
89
+ }
90
+
91
+ end
92
+
93
+ bundle
94
+ end
95
+
96
+ # Delete a list of collections. By default, this function drops all of collections related to measures and patients.
97
+ #
98
+ # @param [Array] collection_names Optionally, an array of collection names to be dropped.
99
+ def self.drop_collections(collection_names=[])
100
+ collection_names = COLLECTION_NAMES if collection_names.empty?
101
+ collection_names.each {|collection| Mongoid.default_session[collection].drop}
102
+ end
103
+
104
+ # Save a javascript function into Mongo's system.js collection for measure execution.
105
+ #
106
+ # @param [String] name The name by which the function will be referred.
107
+ # @param [String] fn The body of the function being saved.
108
+ def self.save_system_js_fn(name, fn)
109
+ fn = "function () {\n #{fn} \n }"
110
+ Mongoid.default_session['system.js'].find('_id' => name).upsert(
111
+ {
112
+ "_id" => name,
113
+ "value" => Moped::BSON::Code.new(fn)
114
+ }
115
+ )
116
+ end
117
+
118
+ # A utility function for finding files in a bundle. Strip a file path of it's extension and just give the filename.
119
+ #
120
+ # @param [String] original A file path.
121
+ # @param [String] extension A file extension.
122
+ # @return The filename at the end of the original String path with the extension removed. e.g. "/boo/urns.html" -> "urns"
123
+ def self.entry_key(original, extension)
124
+ original.split('/').last.gsub(".#{extension}", '')
125
+ end
126
+
127
+ def self.unpack_bundle_contents(zip, type = nil)
128
+ bundle_contents = { bundle: nil, measures: {}, patients: {}, extensions: {}, results: {}, valuesets: {} }
129
+ Zip::ZipFile.open(zip.path) do |zipfile|
130
+ zipfile.entries.each do |entry|
131
+ bundle_contents[:bundle] = zipfile.read(entry.name) if entry.name.include? "bundle"
132
+ if type.nil? || entry.name.match(Regexp.new("/#{type}/"))
133
+ bundle_contents[:measures][entry_key(entry.name, "json")] = zipfile.read(entry.name) if entry.name.match /^measures.*\.json$/
134
+ bundle_contents[:patients][entry_key(entry.name, "json")] = zipfile.read(entry.name) if entry.name.match /^patients.*\.json$/ # Only need to import one of the formats
135
+ bundle_contents[:results][entry_key(entry.name,"json")] = zipfile.read(entry.name) if entry.name.match /^results.*\.json/
136
+ end
137
+ bundle_contents[:extensions][entry_key(entry.name,"js")] = zipfile.read(entry.name) if entry.name.match /^library_functions.*\.js/
138
+
139
+ bundle_contents[:valuesets][entry_key(entry.name,"json")] = zipfile.read(entry.name) if entry.name.match /^value_sets.*\.json/
140
+ end
141
+ end
142
+ bundle_contents
143
+ end
144
+ end
145
+
146
+ end
147
+ end
148
+ end
@@ -1,38 +1,26 @@
1
1
  module HealthDataStandards
2
2
  module Import
3
3
  module C32
4
- class CareGoalImporter < SectionImporter
4
+ class CareGoalImporter < CDA::SectionImporter
5
5
 
6
- def initialize
7
- @entry_xpath = "//cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.124']/cda:entry/cda:*[cda:templateId/@root='2.16.840.1.113883.10.20.1.25']"
6
+ def initialize(entry_finder=CDA::EntryFinder.new("//cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.124']/cda:entry/cda:*[cda:templateId/@root='2.16.840.1.113883.10.20.1.25']"))
7
+ super(entry_finder)
8
8
  end
9
9
 
10
- def create_entries(doc, id_map = {})
11
- goal_list = []
12
- goal_elements = doc.xpath(@entry_xpath)
13
- goal_elements.each do |goal_element|
14
-
15
- importer = case goal_element.name
16
- when "observation" then ResultImporter.new
17
- when "supply" then MedicalEquipment.new
18
- when "substanceAdministration" then MedicationImporter.new
19
- when "encounter" then EncounterImporter.new
20
- when "procedure" then ProcedureImporter.new
21
- else SectionImporter.new(nil) #don't need entry xpath, since we already have the entry
10
+ def create_entry(goal_element, nrh = CDA::NarrativeReferenceHandler.new)
11
+ importer = case goal_element.name
12
+ when "observation" then CDA::ResultImporter.new
13
+ when "supply" then CDA::MedicalEquipment.new
14
+ when "substanceAdministration" then CDA::MedicationImporter.new
15
+ when "encounter" then CDA::EncounterImporter.new
16
+ when "procedure" then CDA::ProcedureImporter.new
17
+ else CDA::SectionImporter.new(nil) #don't need entry xpath, since we already have the entry
22
18
  end
23
19
 
24
- entry = importer.create_entry(goal_element, id_map={})
25
- extract_negation(goal_element, entry)
20
+ entry = importer.create_entry(goal_element, nrh)
21
+ extract_negation(goal_element, entry)
26
22
 
27
-
28
-
29
- if @check_for_usable
30
- goal_list << entry if entry.usable?
31
- else
32
- goal_list << entry
33
- end
34
- end
35
- goal_list
23
+ entry
36
24
  end
37
25
  end
38
26
  end
@@ -1,68 +1,29 @@
1
1
  module HealthDataStandards
2
2
  module Import
3
3
  module C32
4
- class ConditionImporter < SectionImporter
4
+ class ConditionImporter < CDA::ConditionImporter
5
5
 
6
6
  def initialize
7
- @entry_xpath = "//cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.103']/cda:entry/cda:act/cda:entryRelationship/cda:observation"
8
- @code_xpath = "./cda:value"
9
- @status_xpath = "./cda:entryRelationship/cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.1.50']/cda:value"
10
- @ordinality_xpath = "./cda:priorityCode"
11
- @description_xpath = "./cda:text/cda:reference[@value]"
12
- @provider_xpath = "./cda:act[cda:templateId/@root='2.16.840.1.113883.10.20.1.27']/cda:performer"
13
- @cod_xpath = "./cda:entryRelationship[@typeCode='CAUS']/cda:observation/cda:code[@code='419620001']"
14
- @priority_xpath = "../cda:sequenceNumber"
7
+ super
8
+ @death_xpath = "./cda:entryRelationship[@typeCode='CAUS']/cda:observation"
9
+ @cod_xpath = "#{@death_xpath}/cda:code[@code='419620001']"
10
+ @time_of_death_xpath = "#{@death_xpath}/cda:effectiveTime/@value"
15
11
  end
16
12
 
17
- def create_entries(doc, id_map = {})
18
- @id_map = id_map
19
- condition_list = []
20
- entry_elements = doc.xpath(@entry_xpath)
21
-
22
- entry_elements.each do |entry_element|
23
- condition = Condition.new
24
-
25
- extract_codes(entry_element, condition)
26
- extract_dates(entry_element, condition)
27
- extract_status(entry_element, condition)
28
- extract_ordinality(entry_element, condition)
29
- extract_description(entry_element, condition, id_map)
30
- extract_cause_of_death(entry_element, condition) if @cod_xpath
31
- extract_type(entry_element, condition)
32
- extract_negation(entry_element, condition)
33
- extract_priority(entry_element, condition)
34
-
35
- if @provider_xpath
36
- entry_element.xpath(@provider_xpath).each do |provider_element|
37
- condition.treating_provider << import_actor(provider_element)
38
- end
39
- end
40
-
41
- condition_list << condition
42
- end
43
-
44
- condition_list
13
+ def create_entry(entry_element, nrh = CDA::NarrativeReferenceHandler.new)
14
+ condition = super
15
+ extract_cause_of_death(entry_element, condition)
16
+ extract_type(entry_element, condition)
17
+ condition
45
18
  end
46
19
 
47
20
  private
48
21
 
49
- def extract_ordinality(parent_element, entry)
50
- ordinality_element = parent_element.at_xpath(@ordinality_xpath)
51
- if ordinality_element
52
- entry.ordinality = {CodeSystemHelper.code_system_for(ordinality_element['codeSystem']) => [ordinality_element['code']]}
53
- end
54
- end
55
-
56
22
  def extract_cause_of_death(entry_element, condition)
57
23
  cod = entry_element.at_xpath(@cod_xpath)
58
24
  condition.cause_of_death = cod.present?
59
- end
60
-
61
- def extract_priority(entry_element, condition)
62
- priority_element = entry_element.at_xpath(@priority_xpath)
63
- if priority_element
64
- condition.priority = priority_element['value'].to_i
65
- end
25
+ time_of_death = entry_element.at_xpath(@time_of_death_xpath)
26
+ condition.time_of_death = HL7Helper.timestamp_to_integer(time_of_death.text) if time_of_death
66
27
  end
67
28
 
68
29
  def extract_type(entry_element, condition)