renalware-core 2.0.46 → 2.0.47

Sign up to get free protection for your applications and to get access to all the features.
Files changed (258) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -11
  3. data/app/assets/javascripts/renalware/clipboard_setup.js +37 -0
  4. data/app/assets/javascripts/renalware/navigation.js +3 -3
  5. data/app/assets/stylesheets/renalware/base/_variables.scss +2 -0
  6. data/app/assets/stylesheets/renalware/lib/_foundation_and_overrides.scss +1 -0
  7. data/app/assets/stylesheets/renalware/modules/_clipboard.scss +12 -0
  8. data/app/assets/stylesheets/renalware/modules/_patients.scss +9 -0
  9. data/app/assets/stylesheets/renalware/partials/_button.scss +2 -1
  10. data/app/assets/stylesheets/renalware/partials/_layout.scss +30 -2
  11. data/app/assets/stylesheets/renalware/partials/_navigation.scss +30 -21
  12. data/app/assets/stylesheets/renalware/partials/_tables.scss +18 -3
  13. data/app/controllers/renalware/accesses/plans_controller.rb +1 -0
  14. data/app/controllers/renalware/admissions/requests_controller.rb +1 -0
  15. data/app/controllers/renalware/clinical/dry_weights_controller.rb +1 -0
  16. data/app/controllers/renalware/concerns/cache_busting.rb +1 -1
  17. data/app/controllers/renalware/events/events_controller.rb +3 -0
  18. data/app/controllers/renalware/hd/diary_slots_controller.rb +1 -0
  19. data/app/controllers/renalware/hospitals/wards_controller.rb +1 -1
  20. data/app/controllers/renalware/letters/base_controller.rb +4 -0
  21. data/app/controllers/renalware/letters/formatted_letters_controller.rb +9 -4
  22. data/app/controllers/renalware/letters/letters_controller.rb +1 -0
  23. data/app/controllers/renalware/letters/printable_letters_controller.rb +47 -0
  24. data/app/controllers/renalware/medications/prescriptions_controller.rb +1 -1
  25. data/app/controllers/renalware/messaging/internal/messages_controller.rb +1 -0
  26. data/app/controllers/renalware/patients/primary_care_physician_controller.rb +2 -0
  27. data/app/controllers/renalware/session_timeout_controller.rb +2 -0
  28. data/app/documents/renalware/blood_pressure.rb +1 -0
  29. data/app/documents/renalware/dated_boolean_diagnosis.rb +1 -0
  30. data/app/helpers/renalware/application_helper.rb +1 -0
  31. data/app/helpers/renalware/drugs_helper.rb +1 -0
  32. data/app/helpers/renalware/form_helper.rb +1 -0
  33. data/app/helpers/renalware/hospitals_helper.rb +25 -0
  34. data/app/helpers/renalware/mdm_helper.rb +1 -0
  35. data/app/inputs/date_picker_input.rb +1 -0
  36. data/app/jobs/renalware/letters/calculate_page_count_job.rb +46 -0
  37. data/app/models/concerns/renalware/letters/letter_pathology.rb +1 -0
  38. data/app/models/concerns/renalware/patient_pathology_scopes.rb +1 -1
  39. data/app/models/concerns/renalware/patient_transplant_scopes.rb +17 -0
  40. data/app/models/concerns/renalware/patients_ransack_helper.rb +13 -11
  41. data/app/models/renalware/accesses/assessment.rb +1 -1
  42. data/app/models/renalware/accesses/procedure.rb +1 -1
  43. data/app/models/renalware/accesses/profile.rb +3 -3
  44. data/app/models/renalware/admissions/admission_search_form.rb +1 -0
  45. data/app/models/renalware/clinical/allergy_status_form.rb +1 -0
  46. data/app/models/renalware/clinical/body_composition.rb +1 -1
  47. data/app/models/renalware/clinical/dry_weight.rb +1 -1
  48. data/app/models/renalware/clinics/clinic_visit.rb +4 -1
  49. data/app/models/renalware/clinics/create_clinic_visit.rb +1 -0
  50. data/app/models/renalware/events/line_change_event_query.rb +1 -0
  51. data/app/models/renalware/feeds/files/practices/convert_xml_to_csv.rb +1 -0
  52. data/app/models/renalware/feeds/files/practices/country_map.rb +1 -0
  53. data/app/models/renalware/feeds/files/practices/xml_parser.rb +2 -0
  54. data/app/models/renalware/hd/generate_monthly_statistics_for_patient.rb +2 -0
  55. data/app/models/renalware/hd/mdm_patients_form.rb +1 -0
  56. data/app/models/renalware/hd/mdm_patients_query.rb +2 -5
  57. data/app/models/renalware/hd/modality_description.rb +1 -0
  58. data/app/models/renalware/hd/patient_listener.rb +1 -0
  59. data/app/models/renalware/hd/patient_statistics.rb +1 -0
  60. data/app/models/renalware/hd/patients_dialysing_by_schedule_query.rb +1 -0
  61. data/app/models/renalware/hd/preference_set.rb +1 -1
  62. data/app/models/renalware/hd/profile.rb +1 -1
  63. data/app/models/renalware/hd/revise_hd_profile.rb +2 -1
  64. data/app/models/renalware/hd/session.rb +1 -1
  65. data/app/models/renalware/hd/session/closed.rb +1 -0
  66. data/app/models/renalware/hd/session/dna.rb +1 -0
  67. data/app/models/renalware/hd/session_factory.rb +2 -0
  68. data/app/models/renalware/hd/sessions/auditable_session.rb +3 -0
  69. data/app/models/renalware/hd/sessions/save_session.rb +3 -0
  70. data/app/models/renalware/hd/update_rolling_patient_statistics.rb +1 -0
  71. data/app/models/renalware/hospitals/ward.rb +1 -0
  72. data/app/models/renalware/letters/contact.rb +1 -1
  73. data/app/models/renalware/letters/delivery/email_letter_to_practice.rb +1 -0
  74. data/app/models/renalware/letters/event.rb +1 -0
  75. data/app/models/renalware/letters/html_renderer.rb +3 -0
  76. data/app/models/renalware/letters/letter.rb +1 -1
  77. data/app/models/renalware/letters/letter_factory.rb +2 -0
  78. data/app/models/renalware/letters/part/clinical_observations.rb +1 -0
  79. data/app/models/renalware/letters/part/recent_pathology_results.rb +3 -0
  80. data/app/models/renalware/letters/pdf_letter_cache.rb +5 -4
  81. data/app/models/renalware/letters/pdf_renderer.rb +3 -3
  82. data/app/models/renalware/letters/primary_care_physician.rb +1 -0
  83. data/app/models/renalware/letters/printing/create_pdf_by_interleaving_address_sheet_and_letter_for_each_recipient.rb +139 -0
  84. data/app/models/renalware/letters/printing/duplex_interleaved_pdf_renderer.rb +39 -0
  85. data/app/models/renalware/letters/printing/pdf_combining.rb +64 -0
  86. data/app/models/renalware/letters/printing/printable_recipients.rb +20 -0
  87. data/app/models/renalware/letters/recipient.rb +7 -2
  88. data/app/models/renalware/letters/recipient_address_pdf_renderer.rb +26 -0
  89. data/app/models/renalware/letters/recipient_params_processor.rb +1 -0
  90. data/app/models/renalware/low_clearance/mdm_patients_query.rb +2 -0
  91. data/app/models/renalware/low_clearance/profile.rb +2 -1
  92. data/app/models/renalware/medications/prescription.rb +3 -1
  93. data/app/models/renalware/messaging/internal/message_form_builder.rb +1 -0
  94. data/app/models/renalware/modalities/change_patient_modality.rb +1 -0
  95. data/app/models/renalware/pathology/message_listener.rb +1 -0
  96. data/app/models/renalware/pathology/observation_requests_attributes_builder.rb +2 -0
  97. data/app/models/renalware/pathology/observations_grouped_by_date_query.rb +1 -0
  98. data/app/models/renalware/pathology/observations_jsonb_serializer.rb +1 -0
  99. data/app/models/renalware/pathology/requests/global_rule/observation_result.rb +1 -0
  100. data/app/models/renalware/pathology/requests/global_rule/prescription_drug.rb +1 -0
  101. data/app/models/renalware/pathology/requests/global_rule/prescription_drug_category.rb +1 -0
  102. data/app/models/renalware/pathology/requests/global_rule/prescription_drug_type.rb +1 -0
  103. data/app/models/renalware/pathology/requests/global_rule/request_result.rb +3 -0
  104. data/app/models/renalware/pathology/requests/global_rule_set.rb +2 -0
  105. data/app/models/renalware/pathology/requests/high_risk_rule_set.rb +5 -0
  106. data/app/models/renalware/patient.rb +14 -11
  107. data/app/models/renalware/patients/calculate_age.rb +47 -0
  108. data/app/models/renalware/patients/idempotent_create_patient.rb +1 -0
  109. data/app/models/renalware/patients/mdm_patients_query.rb +0 -2
  110. data/app/models/renalware/patients/patient_hospital_identifiers.rb +2 -0
  111. data/app/models/renalware/patients/worry.rb +1 -1
  112. data/app/models/renalware/pd/apd/available_overnight_volume.rb +2 -0
  113. data/app/models/renalware/pd/apd/calculate_volumes.rb +1 -0
  114. data/app/models/renalware/pd/apd/non_tidal_regime_calculations.rb +1 -0
  115. data/app/models/renalware/pd/apd/regime_calculations.rb +1 -0
  116. data/app/models/renalware/pd/apd/tidal_regime_calculations.rb +2 -0
  117. data/app/models/renalware/pd/mdm_patients_query.rb +2 -0
  118. data/app/models/renalware/problems/problem.rb +1 -1
  119. data/app/models/renalware/renal/profile.rb +1 -1
  120. data/app/models/renalware/renal/registry/preflight_checks/patients_query.rb +15 -2
  121. data/app/models/renalware/research/study_participant.rb +1 -0
  122. data/app/models/renalware/system/message.rb +2 -0
  123. data/app/models/renalware/system/update_user.rb +1 -2
  124. data/app/models/renalware/transplants/donation.rb +2 -1
  125. data/app/models/renalware/transplants/donor_followup.rb +2 -1
  126. data/app/models/renalware/transplants/donor_operation.rb +2 -1
  127. data/app/models/renalware/transplants/donor_workup.rb +2 -1
  128. data/app/models/renalware/transplants/recipient_followup.rb +2 -1
  129. data/app/models/renalware/transplants/recipient_operation.rb +2 -1
  130. data/app/models/renalware/transplants/recipient_workup.rb +2 -1
  131. data/app/models/renalware/transplants/registration.rb +2 -1
  132. data/app/models/renalware/ukrdc/send_patient.rb +1 -0
  133. data/app/policies/renalware/base_policy.rb +2 -0
  134. data/app/policies/renalware/clinics/clinic_visit_policy.rb +1 -0
  135. data/app/presenters/renalware/admissions/admission_presenter.rb +1 -0
  136. data/app/presenters/renalware/clinical/header_presenter.rb +1 -1
  137. data/app/presenters/renalware/dashboard/dashboard_presenter.rb +1 -1
  138. data/app/presenters/renalware/hd/profile_document_presenter.rb +1 -0
  139. data/app/presenters/renalware/hd/profile_presenter.rb +4 -0
  140. data/app/presenters/renalware/hd/session_access_presenter.rb +3 -0
  141. data/app/presenters/renalware/hd/session_presenter.rb +2 -0
  142. data/app/presenters/renalware/hd/station_presenter.rb +1 -0
  143. data/app/presenters/renalware/hd/unmet_preferences_presenter.rb +2 -0
  144. data/app/presenters/renalware/hospitals/ward_presenter.rb +21 -0
  145. data/app/presenters/renalware/letters/letter_presenter.rb +16 -5
  146. data/app/presenters/renalware/medications/prescription_presenter.rb +1 -0
  147. data/app/presenters/renalware/pathology/observation_set_presenter.rb +2 -0
  148. data/app/presenters/renalware/patient_presenter.rb +1 -0
  149. data/app/presenters/renalware/pd/peritonitis_episode_presenter.rb +1 -0
  150. data/app/presenters/renalware/reporting/audit_presenter.rb +1 -0
  151. data/app/presenters/renalware/system/users_presenter.rb +1 -0
  152. data/app/presenters/renalware/transplants/donor_dashboard_presenter.rb +1 -1
  153. data/app/presenters/renalware/transplants/wait_list_registration_presenter.rb +2 -0
  154. data/app/presenters/renalware/ukrdc/patient_presenter.rb +2 -0
  155. data/app/validators/renalware/letters/author_signature_validator.rb +1 -0
  156. data/app/validators/renalware/patients/blood_pressure_validator.rb +1 -0
  157. data/app/validators/renalware/patients/bm_stix_validator.rb +1 -0
  158. data/app/validators/renalware/patients/height_validator.rb +1 -0
  159. data/app/validators/renalware/patients/pulse_validator.rb +1 -0
  160. data/app/validators/renalware/patients/temperature_validator.rb +1 -0
  161. data/app/validators/renalware/patients/weight_validator.rb +1 -0
  162. data/app/values/renalware/address.rb +1 -0
  163. data/app/values/renalware/bmi.rb +1 -0
  164. data/app/views/renalware/admin/users/_filters.html.slim +0 -8
  165. data/app/views/renalware/admin/users/_tabs.html.slim +7 -0
  166. data/app/views/renalware/admin/users/index.html.slim +6 -2
  167. data/app/views/renalware/admissions/admissions/_filters.html.slim +1 -2
  168. data/app/views/renalware/admissions/admissions/_form.html.slim +1 -9
  169. data/app/views/renalware/admissions/admissions/index.html.slim +3 -1
  170. data/app/views/renalware/admissions/consults/_form.html.slim +3 -7
  171. data/app/views/renalware/admissions/consults/index.html.slim +3 -1
  172. data/app/views/renalware/api/ukrdc/patients/_patient.xml.builder +1 -0
  173. data/app/views/renalware/clinics/appointments/index.html.slim +16 -15
  174. data/app/views/renalware/clinics/clinic_visits/_table.html.slim +1 -1
  175. data/app/views/renalware/clinics/clinic_visits/_table_row.html.slim +10 -0
  176. data/app/views/renalware/clinics/visits/_table.html.slim +2 -2
  177. data/app/views/renalware/clinics/visits/_table_row.html.slim +10 -0
  178. data/app/views/renalware/clinics/visits/index.html.slim +2 -2
  179. data/app/views/renalware/deaths/index.html.slim +6 -1
  180. data/app/views/renalware/directory/people/index.html.slim +2 -1
  181. data/app/views/renalware/drugs/drugs/index.html.slim +2 -2
  182. data/app/views/renalware/events/types/{_header.html.slim → _header.html.slim.dead} +0 -0
  183. data/app/views/renalware/hd/cannulation_types/index.html.slim +3 -1
  184. data/app/views/renalware/hd/dialysers/index.html.slim +2 -1
  185. data/app/views/renalware/hd/mdm_patients/index.html.slim +3 -2
  186. data/app/views/renalware/hd/ongoing_sessions/show.html.slim +3 -1
  187. data/app/views/renalware/hd/sessions/_row.html.slim +11 -1
  188. data/app/views/renalware/hd/sessions/_thead.html.slim +1 -1
  189. data/app/views/renalware/hd/sessions/dna/_row.html.slim +11 -1
  190. data/app/views/renalware/hospitals/wards/_form.html.slim +1 -0
  191. data/app/views/renalware/hospitals/wards/index.html.slim +9 -6
  192. data/app/views/renalware/layouts/_non_patient.html.slim +16 -9
  193. data/app/views/renalware/letters/electronic_receipts/read.html.slim +6 -2
  194. data/app/views/renalware/letters/electronic_receipts/sent.html.slim +6 -2
  195. data/app/views/renalware/letters/electronic_receipts/unread.html.slim +6 -2
  196. data/app/views/renalware/letters/formatted_letters/_letter.html.slim +3 -2
  197. data/app/views/renalware/letters/letters/_additional_ccs.html.slim +1 -1
  198. data/app/views/renalware/letters/letters/_form.html.slim +2 -5
  199. data/app/views/renalware/letters/letters/_main_recipient.html.slim +2 -3
  200. data/app/views/renalware/letters/letters/edit.html.slim +1 -0
  201. data/app/views/renalware/letters/letters/new.html.slim +1 -1
  202. data/app/views/renalware/letters/lists/_letter.html.slim +14 -3
  203. data/app/views/renalware/letters/lists/_table.html.slim +2 -1
  204. data/app/views/renalware/letters/lists/show.html.slim +27 -25
  205. data/app/views/renalware/letters/printable_letters/_recipient_address_cover_sheet.html.slim +40 -0
  206. data/app/views/renalware/letters/printable_letters/show.pdf.slim +10 -0
  207. data/app/views/renalware/letters/shared/_electronic_cc_type_tabs.html.slim +6 -4
  208. data/app/views/renalware/low_clearance/mdm_patients/{_filters.html.slim → _tabs.html.slim} +2 -2
  209. data/app/views/renalware/mdm_patients/_table.html.slim +1 -1
  210. data/app/views/renalware/mdm_patients/index.html.slim +5 -5
  211. data/app/views/renalware/medications/drug_types/prescriptions/_filters.html.slim +1 -1
  212. data/app/views/renalware/medications/drug_types/prescriptions/index.html.slim +3 -1
  213. data/app/views/renalware/medications/prescriptions/_tables.html.slim +2 -2
  214. data/app/views/renalware/messaging/internal/receipts/read.html.slim +6 -2
  215. data/app/views/renalware/messaging/internal/receipts/sent.html.slim +6 -2
  216. data/app/views/renalware/messaging/internal/receipts/unread.html.slim +6 -2
  217. data/app/views/renalware/messaging/shared/_messages_type_tabs.html.slim +6 -4
  218. data/app/views/renalware/modalities/descriptions/{_header.html.slim → _header.html.slim.dead} +0 -0
  219. data/app/views/renalware/pathology/_navigation.html.slim +8 -7
  220. data/app/views/renalware/patients/_header.html.slim +3 -3
  221. data/app/views/renalware/patients/_index_header.html.slim +1 -1
  222. data/app/views/renalware/patients/patients/edit.html.slim +2 -1
  223. data/app/views/renalware/patients/patients/index.html.slim +3 -1
  224. data/app/views/renalware/patients/patients/new.html.slim +1 -2
  225. data/app/views/renalware/patients/primary_care_physicians/index.html.slim +2 -1
  226. data/app/views/renalware/patients/worryboard/show.html.slim +3 -1
  227. data/app/views/renalware/problems/problems/_archived_table.html.slim +1 -1
  228. data/app/views/renalware/problems/problems/index.html.slim +1 -1
  229. data/app/views/renalware/renal/aki_alerts/_filters.html.slim +8 -8
  230. data/app/views/renalware/renal/aki_alerts/edit.html.slim +1 -6
  231. data/app/views/renalware/renal/aki_alerts/index.html.slim +3 -1
  232. data/app/views/renalware/renal/registry_preflight_checks/_filters.html.slim +5 -0
  233. data/app/views/renalware/renal/registry_preflight_checks/_table.html.slim +4 -1
  234. data/app/views/renalware/renal/registry_preflight_checks/deaths.html.slim +5 -1
  235. data/app/views/renalware/renal/registry_preflight_checks/patients.html.slim +5 -1
  236. data/app/views/renalware/transplants/mdm_patients/{_filters.html.slim → _filters.html.slim.dead} +0 -0
  237. data/app/views/renalware/transplants/mdm_patients/_tabs.html.slim +8 -0
  238. data/app/views/renalware/transplants/wait_lists/show.html.slim +2 -1
  239. data/config/initializers/ransack.rb +11 -0
  240. data/config/initializers/renalware.rb +6 -1
  241. data/config/locales/renalware/low_clearance/low_clearance.en.yml +2 -2
  242. data/config/locales/renalware/transplants/mdm.en.yml +2 -2
  243. data/config/routes.rb +5 -0
  244. data/db/migrate/20180831134926_create_daily_reports.rb +5 -0
  245. data/db/migrate/20180907100545_add_page_count_to_letters.rb +5 -0
  246. data/db/migrate/20181001162513_add_active_to_hospital_wards.rb +5 -0
  247. data/lib/collection_presenter.rb +1 -0
  248. data/lib/core_extensions/active_record/sort.rb +1 -0
  249. data/lib/document/base.rb +1 -0
  250. data/lib/document/embedded.rb +1 -0
  251. data/lib/dropdown_button_item.rb +1 -0
  252. data/lib/renalware/remembered_preferences.rb +2 -0
  253. data/lib/renalware/version.rb +1 -1
  254. data/lib/sql/index_case_stmt.rb +1 -0
  255. data/lib/tasks/reporting.rake +1 -1
  256. data/spec/support/letters_spec_helper.rb +74 -2
  257. data/spec/support/matchers/have_pdf_page_text.rb +15 -0
  258. metadata +51 -32
@@ -13,6 +13,7 @@ module Renalware
13
13
 
14
14
  def call(limit: 1)
15
15
  return [] if event_type.nil?
16
+
16
17
  Event.for_patient(patient)
17
18
  .where(event_type_id: event_type.id)
18
19
  .order(date_time: :desc)
@@ -127,6 +127,7 @@ module Renalware
127
127
 
128
128
  if (PRACTICE_ROLES & org.roles).any?
129
129
  fail unless org.code&.length == 6
130
+
130
131
  practices_count += 1
131
132
  if org.skip
132
133
  Rails.logger.warn(
@@ -22,6 +22,7 @@ module Renalware
22
22
 
23
23
  def map(country)
24
24
  return if country.blank?
25
+
25
26
  if UK_COUNTRIES.include?(country.upcase.strip)
26
27
  Country.new(country: "United Kingdom", region: country.strip.titleize)
27
28
  else
@@ -76,6 +76,7 @@ module Renalware
76
76
 
77
77
  def for_element(name, &block)
78
78
  return unless self.name == name and is_start?
79
+
79
80
  self.instance_eval(&block)
80
81
  end
81
82
 
@@ -88,6 +89,7 @@ module Renalware
88
89
 
89
90
  @node.each do
90
91
  return if self.name == name and is_end? and @node.depth == depth
92
+
91
93
  self.instance_eval(&block)
92
94
  end
93
95
  end
@@ -20,6 +20,7 @@ module Renalware
20
20
 
21
21
  def create_patient_statistics(sessions)
22
22
  return unless sessions.any?
23
+
23
24
  stats = build_patient_statistics
24
25
  stats.hospital_unit = most_recently_used_hospital_unit(sessions)
25
26
  stats.assign_attributes(auditable_sessions(sessions).to_h)
@@ -73,6 +74,7 @@ module Renalware
73
74
  # }
74
75
  def to_h
75
76
  return {} if observation_set.blank?
77
+
76
78
  observation_set.values.select { |code, _| CODES.include?(code.to_s) }
77
79
  end
78
80
 
@@ -27,6 +27,7 @@ module Renalware
27
27
  # Convert e.g. "[1,2,3]" to [1,2,3]
28
28
  def schedule_definition_ids_array
29
29
  return if schedule_definition_ids.blank?
30
+
30
31
  schedule_definition_ids.scan(/\d+/).map(&:to_i)
31
32
  end
32
33
  end
@@ -25,13 +25,14 @@ module Renalware
25
25
  @search ||= begin
26
26
  HD::Patient
27
27
  .include(QueryablePatient)
28
+ .extending(PatientTransplantScopes)
28
29
  .merge(Accesses::Patient.with_current_plan)
29
30
  .merge(Accesses::Patient.with_profile)
30
- .merge(Transplants::Patient.with_registration_statuses)
31
31
  .eager_load(hd_profile: [:hospital_unit])
32
32
  .extending(ModalityScopes)
33
33
  .extending(PatientPathologyScopes)
34
34
  .with_current_pathology
35
+ .with_registration_statuses
35
36
  .with_current_modality_matching(MODALITY_NAMES)
36
37
  .search(params)
37
38
  end
@@ -59,10 +60,6 @@ module Renalware
59
60
  ransacker :current_access, type: :string do
60
61
  Arel.sql("access_types.name")
61
62
  end
62
-
63
- ransacker :transplant_registration_status do
64
- Arel.sql("transplant_registration_status_descriptions.name")
65
- end
66
63
  end
67
64
  end
68
65
  end
@@ -28,6 +28,7 @@ module Renalware
28
28
 
29
29
  def to_s
30
30
  return default_name if patient.blank? || unit_code.blank?
31
+
31
32
  "#{default_name} (#{unit_code})"
32
33
  end
33
34
 
@@ -12,6 +12,7 @@ module Renalware
12
12
  def nullify_significant_hd_profile_attributes(patient, by:)
13
13
  hd_profile = HD.cast_patient(patient).hd_profile
14
14
  return if hd_profile.nil?
15
+
15
16
  hd_profile.hospital_unit = nil
16
17
  hd_profile.schedule_definition = nil
17
18
  hd_profile.save_by!(by)
@@ -39,6 +39,7 @@ module Renalware
39
39
 
40
40
  def to_s
41
41
  return "Last #{session_count} sessions" if rolling?
42
+
42
43
  if year > 0 && month > 0
43
44
  Date.new(year, month).strftime("%b %Y")
44
45
  end
@@ -14,6 +14,7 @@ module Renalware
14
14
 
15
15
  def call
16
16
  return [] if schedule_definition_ids.empty?
17
+
17
18
  Patient
18
19
  .eager_load(hd_profile: [:hospital_unit, { schedule_definition: [:diurnal_period] }])
19
20
  .where(
@@ -12,7 +12,7 @@ module Renalware
12
12
  belongs_to :hospital_unit, class_name: "Hospitals::Unit"
13
13
  belongs_to :schedule_definition, foreign_key: "schedule_definition_id"
14
14
 
15
- has_paper_trail class_name: "Renalware::HD::Version"
15
+ has_paper_trail class_name: "Renalware::HD::Version", on: [:create, :update, :destroy]
16
16
 
17
17
  validates :patient, presence: true
18
18
  validates :entered_on, timeliness: { type: :date, allow_blank: true }
@@ -20,7 +20,7 @@ module Renalware
20
20
  belongs_to :schedule_definition, foreign_key: "schedule_definition_id"
21
21
 
22
22
  has_document class_name: "Renalware::HD::ProfileDocument"
23
- has_paper_trail class_name: "Renalware::HD::Version"
23
+ has_paper_trail class_name: "Renalware::HD::Version", on: [:create, :update, :destroy]
24
24
 
25
25
  validates :patient, presence: true
26
26
  validates :prescriber, presence: true
@@ -5,6 +5,7 @@ module Renalware
5
5
  class ReviseHDProfile
6
6
  def initialize(profile)
7
7
  raise(ArgumentError, "Cannot revise a new Profile") unless profile.persisted?
8
+
8
9
  @profile = profile
9
10
  end
10
11
 
@@ -12,8 +13,8 @@ module Renalware
12
13
  profile.assign_attributes(params)
13
14
  return true unless profile.changed?
14
15
  return false unless profile.valid?
15
- profile.restore_attributes
16
16
 
17
+ profile.restore_attributes
17
18
  profile.supersede!(params)
18
19
  end
19
20
 
@@ -29,7 +29,7 @@ module Renalware
29
29
  dependent: :destroy
30
30
  accepts_nested_attributes_for :prescription_administrations
31
31
 
32
- has_paper_trail class_name: "Renalware::HD::Version"
32
+ has_paper_trail class_name: "Renalware::HD::Version", on: [:create, :update, :destroy]
33
33
 
34
34
  before_create :assign_modality
35
35
  before_save :compute_duration
@@ -23,6 +23,7 @@ module Renalware
23
23
 
24
24
  def immutable?
25
25
  return true unless persisted?
26
+
26
27
  temporary_editing_window_has_elapsed?
27
28
  end
28
29
 
@@ -21,6 +21,7 @@ module Renalware
21
21
 
22
22
  def immutable?
23
23
  return true unless persisted?
24
+
24
25
  temporary_editing_window_has_elapsed?
25
26
  end
26
27
 
@@ -54,6 +54,7 @@ module Renalware
54
54
 
55
55
  def set_default_access(session) # rubocop:disable Naming/AccessorMethodName
56
56
  return if dna_session?
57
+
57
58
  accesses_patient = Renalware::Accesses.cast_patient(patient)
58
59
  if (profile = accesses_patient.current_profile)
59
60
  session.document.info.access_type = profile.type.name
@@ -63,6 +64,7 @@ module Renalware
63
64
 
64
65
  def build_prescription_administrations(session)
65
66
  return unless session.new_record?
67
+
66
68
  patient.prescriptions.to_be_administered_on_hd.map do |prescription|
67
69
  session.prescription_administrations.build(prescription: prescription)
68
70
  end
@@ -30,6 +30,7 @@ module Renalware
30
30
 
31
31
  def document
32
32
  return SessionDocument.new if dna?
33
+
33
34
  super || SessionDocument.new
34
35
  end
35
36
 
@@ -42,6 +43,7 @@ module Renalware
42
43
 
43
44
  def dialysis_minutes_shortfall_percentage
44
45
  return 0.0 if dialysis_minutes_shortfall == 0
46
+
45
47
  (dialysis_minutes_shortfall.to_f / prescribed_time.to_f) * 100.0
46
48
  end
47
49
 
@@ -55,6 +57,7 @@ module Renalware
55
57
 
56
58
  def weight_loss_as_percentage_of_body_weight
57
59
  return unless measured_dry_weight > 0
60
+
58
61
  (weight_loss / measured_dry_weight) * 100.0
59
62
  end
60
63
 
@@ -41,6 +41,7 @@ module Renalware
41
41
  def parse_params(params)
42
42
  @session_type = params.delete(:type)
43
43
  raise(ArgumentError, "Missing type in session params") if session_type.blank?
44
+
44
45
  params
45
46
  end
46
47
 
@@ -92,8 +93,10 @@ module Renalware
92
93
 
93
94
  def lookup_access_type_abbreviation(session)
94
95
  return unless session.document&.respond_to?(:info)
96
+
95
97
  access_type = Accesses::Type.find_by(name: session.document.info.access_type)
96
98
  return unless access_type
99
+
97
100
  session.document.info.access_type_abbreviation = access_type.abbreviation
98
101
  end
99
102
 
@@ -9,6 +9,7 @@ module Renalware
9
9
 
10
10
  def call
11
11
  return unless recent_sessions.any?
12
+
12
13
  stats = rolling_stats_for_this_patient
13
14
  stats.hospital_unit = most_recently_used_hospital_unit
14
15
  stats.assign_attributes(auditable_sessions.to_h)
@@ -11,6 +11,7 @@ module Renalware
11
11
  validates :name, presence: true, uniqueness: { scope: :hospital_unit_id }
12
12
 
13
13
  scope :ordered, -> { order(:name) }
14
+ scope :active, ->{ where(active: true) }
14
15
 
15
16
  def self.policy_class
16
17
  BasePolicy
@@ -14,7 +14,7 @@ module Renalware
14
14
  validates :other_description, presence: true, if: -> { unspecified_description? }
15
15
 
16
16
  delegate :salutation, to: :person
17
- delegate :address, :to_s, :family_name, to: :person
17
+ delegate :address, :to_s, :family_name, :full_name, to: :person
18
18
  delegate :name, to: :address, prefix: true
19
19
 
20
20
  accepts_nested_attributes_for :person
@@ -21,6 +21,7 @@ module Renalware
21
21
  # false: the patient does not have a practice or the practice has no email address
22
22
  def call
23
23
  return false unless email_letter_to_practice?
24
+
24
25
  email_letter_to_the_patients_practice
25
26
  true
26
27
  end
@@ -19,6 +19,7 @@ module Renalware
19
19
 
20
20
  def part_classes
21
21
  return clinical_part_classes if clinical?
22
+
22
23
  {}
23
24
  end
24
25
 
@@ -2,6 +2,9 @@
2
2
 
3
3
  module Renalware
4
4
  module Letters
5
+ # Note we cannot use a partial layout with render_to_string as it will always expect
6
+ # the location to be in views/layouts and its not possible in Rails 5.1 to specify
7
+ # another or absolute path
5
8
  class HTMLRenderer
6
9
  def call(letter)
7
10
  context = LettersController.new
@@ -56,7 +56,7 @@ module Renalware
56
56
  }
57
57
  singleton_class.send(:alias_method, :in_progress, :pending)
58
58
 
59
- scope :reverse, -> { order("#{effective_date_sort} asc") }
59
+ scope :reversed, -> { order("#{effective_date_sort} asc") }
60
60
  scope :ordered, -> { order("#{effective_date_sort} desc") }
61
61
  scope :with_letterhead, -> { includes(:letterhead) }
62
62
  scope :with_main_recipient, -> { includes(main_recipient: [:address, :addressee]) }
@@ -44,6 +44,7 @@ module Renalware
44
44
  # practice address but the GP's name as the salutation.
45
45
  def include_primary_care_physician_as_default_main_recipient
46
46
  return if letter.main_recipient.present?
47
+
47
48
  if patient.primary_care_physician.present? && patient.practice_id.present?
48
49
  letter.build_main_recipient(person_role: :primary_care_physician)
49
50
  else
@@ -68,6 +69,7 @@ module Renalware
68
69
  # the parent letter is saved.
69
70
  def build_electronic_ccs
70
71
  return if electronic_cc_recipient_ids.blank?
72
+
71
73
  electronic_cc_recipient_ids.reject(&:blank?).map do |user_id|
72
74
  letter.electronic_receipts.build(recipient_id: user_id)
73
75
  end
@@ -28,6 +28,7 @@ module Renalware
28
28
  OBSERVATION_ATTRS.each_with_object([]) do |observation, arr|
29
29
  value = send(observation)
30
30
  next if value.blank?
31
+
31
32
  arr << [
32
33
  label_for(observation),
33
34
  enum_text_or_raw_value_for(value),
@@ -18,6 +18,7 @@ module Renalware
18
18
  @results ||= begin
19
19
  snapshot = letter.pathology_snapshot.dup
20
20
  return if snapshot.blank?
21
+
21
22
  groups = group_snapshot_by_code_and_date(snapshot)
22
23
  format_groups_into_string(groups)
23
24
  end
@@ -47,6 +48,7 @@ module Renalware
47
48
  match = snapshot[obs_desc.code.to_sym]
48
49
  next if match.nil?
49
50
  next if match[:observed_at].nil?
51
+
50
52
  date = I18n.l(Date.parse(match[:observed_at]))
51
53
  dates[date] ||= {}
52
54
  dates[date][obs_desc.code] = match[:result]
@@ -75,6 +77,7 @@ module Renalware
75
77
 
76
78
  def format_code_and_result_string(code, result)
77
79
  return "(#{code} #{result})" if code.casecmp?("EGFR")
80
+
78
81
  "#{code} #{result}"
79
82
  end
80
83
  end
@@ -39,8 +39,8 @@ module Renalware
39
39
  class << self
40
40
  delegate :clear, to: :store
41
41
 
42
- def fetch(letter)
43
- store.fetch(cache_key_for(letter)) { yield }
42
+ def fetch(letter, **options)
43
+ store.fetch(cache_key_for(letter, **options)) { yield }
44
44
  end
45
45
 
46
46
  # Note the letter must be a LetterPresenter which has a #to_html method
@@ -48,10 +48,11 @@ module Renalware
48
48
  # html including surrounding layout with inline css and images. This way if the
49
49
  # layout changes or the image is changed for example, the cache for the pdf is no longer
50
50
  # valid and a new key and cache entry will be created.
51
- def cache_key_for(letter)
51
+ def cache_key_for(letter, **options)
52
52
  timestamp = letter&.updated_at&.strftime("%Y%m%d%H%M%S")
53
53
  pat_id = letter.patient.id
54
- "letter-pdf-#{letter.id}-#{pat_id}-#{timestamp}-#{Digest::MD5.hexdigest(letter.to_html)}"
54
+ "letter-pdf-#{letter.id}-#{pat_id}-#{timestamp}-" \
55
+ "#{Digest::MD5.hexdigest(letter.to_html(**options))}"
55
56
  end
56
57
 
57
58
  def cache_path
@@ -17,12 +17,12 @@ module Renalware
17
17
  encoding: "UTF-8"
18
18
  }.freeze
19
19
 
20
- def self.call(letter)
20
+ def self.call(letter, **options)
21
21
  unless letter.respond_to?(:to_html)
22
22
  letter = LetterPresenterFactory.new(letter)
23
23
  end
24
- PdfLetterCache.fetch(letter) do
25
- WickedPdf.new.pdf_from_string(letter.to_html, OPTIONS)
24
+ PdfLetterCache.fetch(letter, **options) do
25
+ WickedPdf.new.pdf_from_string(letter.to_html(**options), OPTIONS)
26
26
  end
27
27
  end
28
28
  end
@@ -10,6 +10,7 @@ module Renalware
10
10
 
11
11
  def cc_on_letter?(letter)
12
12
  return false unless letter.patient.assigned_to_primary_care_physician?(self)
13
+
13
14
  letter.main_recipient.patient? || letter.main_recipient.contact?
14
15
  end
15
16
  end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/letters"
4
+ require "attr_extras"
5
+
6
+ module Renalware
7
+ module Letters
8
+ module Printing
9
+ # Build PDF for printing. We are targetting an envelope stuffer so there will be an address
10
+ # cover sheet in front of each letter.
11
+ # So for a letter where the patient is the main recipient, and we CC to the GP (no practice
12
+ # email address so snail mailing) and 1 extra CC (a contact) the output will look like
13
+ # this:
14
+ #
15
+ # 1. Patient address cover sheet
16
+ # 2. Letter
17
+ # 3. GP address cover sheet
18
+ # 2. Letter
19
+ # 5. Contact address cover sheet
20
+ # 2. Letter
21
+ #
22
+ # Note however the we always pad each item (address sheet, letter) with blank pages to make
23
+ # sure duplex printing does not cause the next page recipient's content to be rendered on the
24
+ # back of the ast page of the previous letter for example/
25
+ #
26
+ # Compiles print content for all input letters and outputs the merged PDF to output_file.
27
+ # Allows batch printing if you want to print say all 2 page letters together.
28
+ # The caller may choose for example to send the output_file could be sent back to the browser
29
+ # for manual printing using (eg using send_file) or copy the merged PDF to a folder for
30
+ # automated printing.
31
+ class CreatePdfByInterleavingAddressSheetAndLetterForEachRecipient
32
+ pattr_initialize [:letters!, :output_file!]
33
+
34
+ def call
35
+ # There is a choice of two methods of building the merged PDF. See pros and cons.
36
+ # The other is UsingSeparatePdfForEachLetterSection.
37
+ UsingOnePdfForAllLetterSections.new(
38
+ letters: letters,
39
+ output_file: output_file
40
+ ).call
41
+ end
42
+
43
+ # For each letter, uses whkhtmltopdf to build the entire print content, ie all address
44
+ # sheets and required instance of the letter are created as one fresh pdf (no cache will be
45
+ # used).
46
+ #
47
+ # Pros:
48
+ # - Quicker: For each letter only one PDF is generated, and although the generation is slow
49
+ # as it does not use the previously cached letter PDF, and it has to render all the letter
50
+ # sections, including the letter itself, for every recipient, its stil faster then
51
+ # rendering separate PDFs fo each section.
52
+ # Cons:
53
+ # - wastes some resources because it does not use the cache and renders same letter
54
+ # multiple times in the views
55
+ # - cannot be 100% sure the number of pages in the final PDF will be extactly what we
56
+ # predict, so requires a check to load the pdf into a reader and validate the page_count
57
+ class UsingOnePdfForAllLetterSections
58
+ include PdfCombining
59
+ pattr_initialize [:letters!, :output_file!]
60
+
61
+ def call
62
+ in_a_temporary_folder do |dir|
63
+ Array(letters).each do |letter|
64
+ if letter.page_count.to_i < 1
65
+ raise(ArgumentError, "letter.page_count not set on letter.id=#{letter.id}")
66
+ end
67
+ letter_filename = create_letter_pdf_in(dir, letter)
68
+ files << letter_filename
69
+ end
70
+ combine_multiple_pdfs_into_one(dir, output_file)
71
+ # For debuging:
72
+ # FileUtils.cp output_file, "/Users/tim/Desktop/x.pdf"
73
+ # `open /Users/tim/Desktop/x.pdf`
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def create_letter_pdf_in(dir, letter)
80
+ filename = "letter_#{letter.id}.pdf"
81
+ path = dir.join(filename)
82
+ File.open(path, "wb") { |file| file.write(DuplexInterleavedPdfRenderer.call(letter)) }
83
+ filename
84
+ end
85
+ end
86
+
87
+ # This approach generates a PDF for each address cover sheet and each
88
+ # letter. It shells to ghostscript to merge the PDFs together in
89
+ # the right order.
90
+ #
91
+ # Pros:
92
+ # - uses the letter PDF already in the cache from when we rendered it to deduce the page
93
+ # count after the letter was archived
94
+ # - uses very little memory as PDF concatenation done on disk
95
+ # - concatentation itslef reasonable fast
96
+ #
97
+ # Cons:
98
+ # - slow because it shells to wkhtmltopdf for each cover sheet, and these are unklikely to
99
+ # be found in tha cache. So for main recip and 2 CCs it wil call wkhtmltopdf 3 times to
100
+ # render the address cover sheets, and the letter PDF should be pulled from cache so that
101
+ # is quick at least.
102
+ #
103
+ class UsingSeparatePdfForEachLetterSection
104
+ include PdfCombining
105
+ pattr_initialize [:letters!, :output_file!]
106
+
107
+ def call
108
+ in_a_temporary_folder do |dir|
109
+ Array(letters).each do |letter|
110
+ letter_filename = create_letter_pdf_in(dir, letter)
111
+ PrintableRecipients.for(letter).each do |recipient|
112
+ files << create_cover_sheet_for(recipient, letter, dir)
113
+ files << letter_filename
114
+ end
115
+ end
116
+ combine_multiple_pdfs_into_one(dir, output_file)
117
+ end
118
+ end
119
+
120
+ private
121
+
122
+ def create_letter_pdf_in(dir, letter)
123
+ filename = "letter_#{letter.id}.pdf"
124
+ path = dir.join(filename)
125
+ File.open(path, "wb") { |file| file.write(PdfRenderer.call(letter)) }
126
+ filename
127
+ end
128
+
129
+ def create_cover_sheet_for(recipient, _letter, dir)
130
+ filename = "recipient_#{recipient.id}.pdf"
131
+ path = dir.join(filename)
132
+ File.open(path, "wb") { |file| file.write(RecipientAddressPdfRenderer.call(recipient)) }
133
+ filename
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end