renalware-core 2.0.113 → 2.0.115

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 (194) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +0 -2
  3. data/app/assets/javascripts/renalware/application.js.erb +0 -1
  4. data/app/assets/javascripts/renalware/feed_only_inputs.js.erb +26 -0
  5. data/app/assets/javascripts/renalware/feeds.js +50 -0
  6. data/app/assets/javascripts/renalware/person_ajax_search.js +43 -0
  7. data/app/assets/stylesheets/renalware/application.scss +0 -1
  8. data/app/assets/stylesheets/renalware/modules/_admin.scss +7 -0
  9. data/app/assets/stylesheets/renalware/modules/_pathology.scss +1 -1
  10. data/app/assets/stylesheets/renalware/modules/_patients.scss +6 -0
  11. data/app/assets/stylesheets/renalware/modules/_snippets.scss +2 -2
  12. data/app/assets/stylesheets/renalware/partials/_forms.scss +5 -1
  13. data/app/assets/stylesheets/renalware/partials/_layout.scss +4 -0
  14. data/app/{views/renalware/patients/_side_menu.html.slim → components/renalware/patients/side_menu_component.html.slim} +0 -0
  15. data/app/components/renalware/patients/side_menu_component.rb +18 -0
  16. data/app/controllers/renalware/clinical/body_compositions_controller.rb +6 -0
  17. data/app/controllers/renalware/feeds/hl7_test_messages_controller.rb +60 -0
  18. data/app/controllers/renalware/hd/mdm_patients_controller.rb +16 -6
  19. data/app/controllers/renalware/hd/scheduling/diaries_controller.rb +91 -0
  20. data/app/controllers/renalware/hd/scheduling/diary_slots_controller.rb +169 -0
  21. data/app/controllers/renalware/hd/session_forms/batches_controller.rb +1 -1
  22. data/app/controllers/renalware/pathology/observation_requests_controller.rb +23 -4
  23. data/app/controllers/renalware/pathology/requests/requests_controller.rb +0 -4
  24. data/app/controllers/renalware/patients/abridgements_controller.rb +39 -0
  25. data/app/controllers/renalware/pd/regimes_controller.rb +26 -0
  26. data/app/controllers/renalware/problems/problems_controller.rb +2 -2
  27. data/app/helpers/renalware/application_helper.rb +9 -0
  28. data/app/helpers/renalware/article_helper.rb +18 -0
  29. data/app/helpers/renalware/form_helper.rb +3 -2
  30. data/app/helpers/renalware/users_helper.rb +5 -1
  31. data/app/javascript/controllers/clipboard_controller.js +16 -0
  32. data/app/javascript/packs/renalware_core.js +20 -0
  33. data/app/jobs/feed_job.rb +1 -3
  34. data/app/jobs/hl7_message_example.yml +2 -2
  35. data/app/models/renalware/feeds/files/practice_memberships/import_job.rb +3 -0
  36. data/app/models/renalware/feeds/files/primary_care_physicians/import_job.rb +2 -0
  37. data/app/models/renalware/feeds/hl7_message.rb +57 -6
  38. data/app/models/renalware/feeds/hl7_test_form.rb +10 -0
  39. data/app/models/renalware/feeds/hl7_test_message.rb +11 -0
  40. data/app/models/renalware/feeds/message_parser.rb +6 -2
  41. data/app/models/renalware/feeds/message_processor.rb +44 -32
  42. data/app/models/renalware/feeds/persist_message.rb +9 -3
  43. data/app/models/renalware/feeds.rb +2 -0
  44. data/app/models/renalware/hd/scheduling/archive_arguments.rb +19 -0
  45. data/app/models/renalware/hd/scheduling/diary.rb +54 -0
  46. data/app/models/renalware/hd/scheduling/diary_housekeeping_job.rb +84 -0
  47. data/app/models/renalware/hd/scheduling/diary_range.rb +69 -0
  48. data/app/models/renalware/hd/scheduling/diary_slot.rb +88 -0
  49. data/app/models/renalware/hd/scheduling/find_or_create_diary_by_week_query.rb +52 -0
  50. data/app/models/renalware/hd/scheduling/find_or_create_master_diary.rb +27 -0
  51. data/app/models/renalware/hd/scheduling/master_diary.rb +35 -0
  52. data/app/models/renalware/hd/scheduling/weekly_diary.rb +47 -0
  53. data/app/models/renalware/pathology/message_listener.rb +2 -2
  54. data/app/models/renalware/pathology/requests/global_rule/latest_crf_older_than_weeks.rb +38 -0
  55. data/app/models/renalware/pathology/requests/global_rule/patient_is_diabetic.rb +1 -1
  56. data/app/models/renalware/patient.rb +1 -1
  57. data/app/models/renalware/patients/abridgement.rb +18 -0
  58. data/app/models/renalware/patients/abridgement_search_form.rb +12 -0
  59. data/app/models/renalware/patients/ingestion/command.rb +25 -0
  60. data/app/models/renalware/patients/ingestion/command_factory.rb +135 -0
  61. data/app/models/renalware/patients/ingestion/commands/add_or_update_patient.rb +65 -0
  62. data/app/models/renalware/patients/ingestion/message_listener.rb +21 -0
  63. data/app/models/renalware/patients/ingestion/message_mapper.rb +26 -0
  64. data/app/models/renalware/patients/ingestion/message_mappers/patient.rb +73 -0
  65. data/app/models/renalware/patients/ingestion/update_master_patient_index.rb +60 -0
  66. data/app/models/renalware/patients/ingestion/update_master_patient_index_job.rb.dead +20 -0
  67. data/app/models/renalware/patients.rb +4 -3
  68. data/app/models/renalware/problems/problem.rb +6 -2
  69. data/app/models/renalware/ukrdc/incoming/paths.rb +6 -7
  70. data/app/policies/renalware/admin/devops_policy.rb +18 -0
  71. data/app/policies/renalware/hd/{diary_policy.rb → scheduling/diary_policy.rb} +3 -1
  72. data/app/presenters/renalware/clinical/profile_presenter.rb +2 -1
  73. data/app/presenters/renalware/directory/person_auto_complete_presenter.rb +1 -1
  74. data/app/presenters/renalware/hd/scheduling/diary_presenter.rb +84 -0
  75. data/app/presenters/renalware/hd/scheduling/diary_slot_presenter.rb +74 -0
  76. data/app/presenters/renalware/hd/scheduling/null_slot.rb +42 -0
  77. data/app/presenters/renalware/pd/dashboard_presenter.rb +3 -2
  78. data/app/views/renalware/accesses/procedures/_form.html.slim +5 -1
  79. data/app/views/renalware/accesses/procedures/_list.html.slim +11 -4
  80. data/app/views/renalware/accesses/procedures/show.html.slim +17 -19
  81. data/app/views/renalware/addresses/_form.html.slim +17 -12
  82. data/app/views/renalware/admin/cache/show.html.slim +24 -0
  83. data/app/views/renalware/clinical/body_compositions/_list.html.slim +7 -43
  84. data/app/views/renalware/clinical/body_compositions/_table.html.slim +42 -0
  85. data/app/views/renalware/clinical/body_compositions/index.html.slim +9 -0
  86. data/app/views/renalware/clinical/dry_weights/_list.html.slim +2 -8
  87. data/app/views/renalware/clinical/dry_weights/_table.html.slim +7 -0
  88. data/app/views/renalware/clinical/profiles/show.html.slim +4 -2
  89. data/app/views/renalware/feeds/hl7_test_messages/create.js.erb +10 -0
  90. data/app/views/renalware/feeds/hl7_test_messages/new.slim +18 -0
  91. data/app/views/renalware/hd/mdm_patients/_filters.html.slim +0 -11
  92. data/app/views/renalware/hd/mdm_patients/_page_actions.html.slim +29 -0
  93. data/app/views/renalware/hd/mdm_patients/index.html.slim +7 -4
  94. data/app/views/renalware/hd/{diaries → scheduling/diaries}/_page_actions.html.slim +4 -4
  95. data/app/views/renalware/hd/{diaries → scheduling/diaries}/_table.html.slim +0 -0
  96. data/app/views/renalware/hd/{diaries → scheduling/diaries}/_weekly_diary.html.slim +1 -1
  97. data/app/views/renalware/hd/{diaries → scheduling/diaries}/edit.html.slim +6 -6
  98. data/app/views/renalware/hd/{diaries → scheduling/diaries}/index.html.slim +0 -0
  99. data/app/views/renalware/hd/{diaries → scheduling/diaries}/show.pdf.slim +0 -0
  100. data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/_form.html.slim +15 -6
  101. data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/_slot.html.slim +2 -2
  102. data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/_tab.html.slim +1 -1
  103. data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/create.js.erb +1 -1
  104. data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/destroy.js.erb +1 -1
  105. data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/edit.html.slim +2 -2
  106. data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/new.html.slim +0 -0
  107. data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/new.js.erb +0 -0
  108. data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/show.js.erb +0 -0
  109. data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/update.js.erb +1 -1
  110. data/app/views/renalware/hospitals/units/index.html.slim +2 -2
  111. data/app/views/renalware/layouts/_patient.html.slim +2 -1
  112. data/app/views/renalware/letters/contacts/_form.html.slim +10 -13
  113. data/app/views/renalware/letters/letters/_form.html.slim +2 -9
  114. data/app/views/renalware/navigation/_developer.html.slim +4 -0
  115. data/app/views/renalware/navigation/_menu.html.slim +2 -0
  116. data/app/views/renalware/pathology/observation_requests/_filters.html.slim +17 -0
  117. data/app/views/renalware/pathology/observation_requests/index.html.slim +2 -1
  118. data/app/views/renalware/patients/_layout.html.slim +1 -1
  119. data/app/views/renalware/patients/_side_menu.html.slim.dead +7 -0
  120. data/app/views/renalware/patients/abridgements/_abridgement.html.slim +8 -0
  121. data/app/views/renalware/patients/abridgements/_table.html.slim +12 -0
  122. data/app/views/renalware/patients/abridgements/index.html.slim +22 -0
  123. data/app/views/renalware/patients/alerts/create.json +3 -0
  124. data/app/views/renalware/patients/patients/_form.html.slim +36 -17
  125. data/app/views/renalware/patients/patients/edit.html.slim +1 -1
  126. data/app/views/renalware/patients/patients/new.html.slim +1 -1
  127. data/app/views/renalware/patients/side_menu/_actions.html.slim +2 -0
  128. data/app/views/renalware/pd/_apd_regimes.html.slim +1 -1
  129. data/app/views/renalware/pd/_capd_regimes.html.slim +1 -1
  130. data/app/views/renalware/pd/dashboards/show/_apd_regimes.html.slim +11 -2
  131. data/app/views/renalware/pd/dashboards/show/_capd_regimes.html.slim +11 -2
  132. data/app/views/renalware/pd/regimes/index.html.slim +7 -0
  133. data/app/views/renalware/renal/profiles/_form.html.slim +7 -24
  134. data/app/views/renalware/shared/documents/_binary_marker_input.html.slim +2 -2
  135. data/app/views/renalware/shared/documents/_blood_group_input.html.slim +4 -2
  136. data/app/views/renalware/transplants/registrations/_form.html.slim +22 -17
  137. data/config/locales/renalware/clinical/body_composition.yml +6 -3
  138. data/config/locales/renalware/clinical/dry_weight.en.yml +4 -3
  139. data/config/locales/renalware/hd/diary_slots.en.yml +3 -2
  140. data/config/locales/renalware/letters/contact.en.yml +0 -2
  141. data/config/routes/feeds.rb +5 -0
  142. data/config/routes/hd.rb +11 -7
  143. data/config/routes/letters.rb +0 -5
  144. data/config/routes/patients.rb +1 -0
  145. data/config/routes/pd.rb +2 -2
  146. data/config/routes/renal.rb +0 -5
  147. data/config/routes.rb +1 -0
  148. data/config/webpack/development.js +5 -0
  149. data/config/webpack/environment.js +3 -0
  150. data/config/webpack/production.js +5 -0
  151. data/config/webpack/test.js +5 -0
  152. data/config/webpacker.yml +103 -0
  153. data/db/functions/hd_diary_archive_elapsed_master_slots_v01.sql +40 -0
  154. data/db/migrate/20190322120025_create_feed_hl7_test_messages.rb +12 -0
  155. data/db/migrate/20190325134823_create_patients_master_index.rb +24 -0
  156. data/db/migrate/20190327100851_add_handled_to_feed_messages.rb +5 -0
  157. data/db/migrate/20191012121433_add_consultant_to_users.rb +7 -0
  158. data/db/migrate/20191018143635_create_hd_diary_matrix_view.rb +7 -0
  159. data/db/migrate/20191018144917_create_fn_to_archive_master_slots.rb +9 -0
  160. data/db/views/hd_diary_matrix_v01.sql +38 -0
  161. data/lib/core_extensions/active_record/sort.rb +27 -9
  162. data/lib/renalware/configuration.rb +3 -0
  163. data/lib/renalware/engine.rb +31 -0
  164. data/lib/renalware/version.rb +1 -1
  165. data/lib/renalware/week_period.rb +8 -7
  166. data/lib/renalware.rb +13 -1
  167. data/lib/tasks/hd.rake +7 -0
  168. data/lib/tasks/pathology.rake +1 -1
  169. data/lib/tasks/renalware.rake +89 -4
  170. data/spec/factories/hd/scheduling/diaries.rb +18 -0
  171. data/spec/factories/hd/{slots.rb → scheduling/slots.rb} +1 -1
  172. data/spec/factories/patients/abridgements.rb +9 -0
  173. data/spec/support/hl7_helpers.rb +13 -0
  174. data/spec/support/pages/letters/form.rb +4 -1
  175. metadata +126 -50
  176. data/app/assets/javascripts/renalware/auto_complete.js +0 -63
  177. data/app/controllers/renalware/hd/diaries_controller.rb +0 -89
  178. data/app/controllers/renalware/hd/diary_slots_controller.rb +0 -175
  179. data/app/controllers/renalware/letters/descriptions_controller.rb +0 -22
  180. data/app/controllers/renalware/renal/prd_descriptions_controller.rb +0 -15
  181. data/app/models/renalware/hd/archive_yesterdays_slots_job.rb +0 -69
  182. data/app/models/renalware/hd/diary.rb +0 -41
  183. data/app/models/renalware/hd/diary_slot.rb +0 -83
  184. data/app/models/renalware/hd/find_or_create_diary_by_week_query.rb +0 -50
  185. data/app/models/renalware/hd/find_or_create_master_diary.rb +0 -26
  186. data/app/models/renalware/hd/master_diary.rb +0 -32
  187. data/app/models/renalware/hd/weekly_diary.rb +0 -45
  188. data/app/models/renalware/renal/prd_descriptions/search_query.rb +0 -35
  189. data/app/presenters/renalware/hd/diary_presenter.rb +0 -79
  190. data/app/presenters/renalware/hd/diary_slot_presenter.rb +0 -72
  191. data/app/presenters/renalware/hd/null_slot.rb +0 -40
  192. data/app/views/renalware/renal/prd_descriptions/search.json.jbuilder +0 -6
  193. data/lib/test_support/autocomplete_helpers.rb +0 -14
  194. data/spec/factories/hd/diaries.rb +0 -14
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/patients"
4
+
5
+ module Renalware
6
+ module Patients
7
+ module Ingestion
8
+ # Responsible for making commands to process messages based on the
9
+ # message type.
10
+ #
11
+ class CommandFactory
12
+ def for(message)
13
+ case message.action
14
+ when :add_person_information then make_add_patient(message)
15
+ when :update_person_information then make_update_patient(message)
16
+ # when :admit_patient then make_admit_patient(message)
17
+ # when :merge_patient then make_merge_patient(message)
18
+ # when :update_admission then make_update_admission(message)
19
+ # when :cancel_admission then make_cancel_admission(message)
20
+ # when :transfer_patient then make_transfer_patient(message)
21
+ # when :discharge_patient then make_discharge_patient(message)
22
+ # when :cancel_discharge then make_cancel_discharge(message)
23
+ # when :add_consultant then make_add_consultant(message)
24
+ else :no_matching_command
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ # def make_add_patient_with_finder(message)
31
+ # CommandWithFinder.new(
32
+ # make_add_patient(message),
33
+ # message, finder: Finder::Patient.new
34
+ # )
35
+ # end
36
+
37
+ # def make_add_patient_with_finder(message)
38
+ # CommandWithFinder.new(
39
+ # make_add_patient(message),
40
+ # message, finder: Finder::Patient.new
41
+ # )
42
+ # end
43
+
44
+ def make_add_patient(message)
45
+ Commands::AddOrUpdatePatient.new(message)
46
+ end
47
+
48
+ def make_update_patient(message)
49
+ Commands::AddOrUpdatePatient.new(message)
50
+ end
51
+
52
+ # def make_merge_patient(message)
53
+ # Commands::MergePatient.new(message,
54
+ # major_patient_finder: make_patient_finder_with_add_if_missing,
55
+ # minor_patient_finder: make_minor_patient_finder_with_add_if_missing)
56
+ # end
57
+
58
+ # def make_admit_patient(message)
59
+ # Commands::AdmitPatient.new(message,
60
+ # patient_finder: make_patient_finder_with_add_if_missing)
61
+ # end
62
+
63
+ # def make_update_admission(message)
64
+ # Commands::UpdateAdmission.new(message,
65
+ # admission_finder: make_admission_finder_with_logging_if_missing)
66
+ # end
67
+
68
+ # def make_cancel_admission(message)
69
+ # Commands::CancelAdmission.new(message,
70
+ # admission_finder: make_admission_finder_with_logging_if_missing)
71
+ # end
72
+
73
+ # def make_transfer_patient(message)
74
+ # Commands::TransferPatient.new(message,
75
+ # admission_finder: make_admission_finder_with_admit_if_missing)
76
+ # end
77
+
78
+ # def make_discharge_patient(message)
79
+ # Commands::DischargePatient.new(message,
80
+ # admission_finder: make_admission_finder_with_logging_if_missing)
81
+ # end
82
+
83
+ # def make_cancel_discharge(message)
84
+ # Commands::CancelDischarge.new(message)
85
+ # end
86
+
87
+ # def make_add_consultant(message)
88
+ # Commands::AddConsultant.new(message)
89
+ # end
90
+
91
+ # def make_admission_finder_with_logging_if_missing
92
+ # MissingAdmissionLogger.new(make_admission_finder)
93
+ # end
94
+
95
+ # def make_admission_finder_with_admit_if_missing
96
+ # MissingRecordHandler.new(make_admission_finder,
97
+ # policy: make_missing_admission_handler)
98
+ # end
99
+
100
+ # def make_admission_finder
101
+ # Finder::Admission.new
102
+ # end
103
+
104
+ # def make_patient_finder_with_add_if_missing
105
+ # MissingRecordHandler.new(Finder::Patient.new,
106
+ # policy: make_missing_patient_handler)
107
+ # end
108
+
109
+ # def make_minor_patient_finder_with_add_if_missing
110
+ # missing_patient_handler = lambda { |message|
111
+ # Commands::AddPatient.new(
112
+ # message,
113
+ # mapper_factory: MessageMappers::MinorPatient
114
+ # ).call
115
+ # }
116
+
117
+ # MissingRecordHandler.new(Finder::MinorPatient.new,
118
+ # policy: missing_patient_handler)
119
+ # end
120
+
121
+ # def make_missing_patient_handler
122
+ # lambda { |message|
123
+ # make_add_patient(message).call
124
+ # }
125
+ # end
126
+
127
+ # def make_missing_admission_handler
128
+ # lambda { |message|
129
+ # make_admit_patient(message).call
130
+ # }
131
+ # end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/patients/ingestion/command"
4
+
5
+ module Renalware
6
+ module Patients
7
+ module Ingestion
8
+ module Commands
9
+ # We could be here as a results of 2 scenarios:
10
+ # - ADT28 add patient
11
+ # - ADT31 update patient
12
+ #
13
+ # In both circumstances our action is the same:
14
+ # 1. If the patient exists as a Renalware::Patient, update their details
15
+ # 2. Find or create a Patients::Abridgement for this patient
16
+ # 3. Update their abridgement info, including their Renalware::Patient id if one exists
17
+ #
18
+ # So basically we are maintaining patient data in 2 places:
19
+ # a) in Renalware::Patient and its associated address and demographics tables
20
+ # b) in Renalware::Patients::Abrigement, which is an abbreviated version of the patient's
21
+ # details (name, numbers, DOB ect), enabling us to find and import a new patient by
22
+ # taking the abridgement, finding all previously received ADT and ORU etc feed_messages
23
+ # for the patient and replaying those messages to build the patient with their
24
+ # and pathology. If a patient is already a Renalware::Patient then in theory we do
25
+ # not need to maintain an abridgement for them, but for consistentency we do; it has
26
+ # advantages when trying to add a new patient in the Renalware UI - e.g. it can show you
27
+ # that that patient is already added because there is a patient_id on the abridgement.
28
+ class AddOrUpdatePatient < Command
29
+ def initialize(message, mapper_factory: MessageMappers::Patient)
30
+ @mapper_factory = mapper_factory
31
+
32
+ super(message)
33
+ end
34
+
35
+ attr_reader :mapper_factory
36
+
37
+ def call
38
+ # For now skip updating the patient, just update the index ie create/update a
39
+ # patient abridgement
40
+ # update_patient_if_exists
41
+ UpdateMasterPatientIndex.new(message).call
42
+ end
43
+
44
+ private
45
+
46
+ def update_patient_if_exists
47
+ patient = find_patient
48
+ return if patient.blank?
49
+
50
+ patient = mapper_factory.new(message, patient).fetch
51
+ patient.by = SystemUser.find
52
+ patient.save!
53
+ patient
54
+ end
55
+
56
+ def find_patient
57
+ Patient.find_by(
58
+ local_patient_id: message.patient_identification.internal_id
59
+ )
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/patients"
4
+
5
+ #
6
+ # When subscribed to HL7 `message_arrived` messages, gets notified of incoming HL7 messages
7
+ #
8
+ module Renalware
9
+ module Patients
10
+ module Ingestion
11
+ class MessageListener
12
+ def adt_message_arrived(hl7_message:, **)
13
+ return unless hl7_message.adt?
14
+
15
+ command = Command.for(hl7_message)
16
+ command.call
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renalware
4
+ module Patients
5
+ module Ingestion
6
+ # Responsible for mapping attributes from a message to attributes
7
+ # to domain models.
8
+ #
9
+ class MessageMapper
10
+ def initialize(message)
11
+ @message = message
12
+ end
13
+
14
+ attr_reader :message
15
+
16
+ def fetch
17
+ raise NotImplementedError
18
+ end
19
+
20
+ # def source
21
+ # Message.for(message)
22
+ # end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/patients"
4
+
5
+ module Renalware
6
+ module Patients
7
+ module Ingestion
8
+ module MessageMappers
9
+ # Given an HL7 message that instructs us to update a patient (eg ADT^A31) or add a new one
10
+ # (ADT^A28) we parse the patient level information from the HL7 message and update or create
11
+ # the patient.
12
+ class Patient < MessageMapper
13
+ delegate :patient_identification, to: :message
14
+ delegate :address, to: :patient_identification
15
+
16
+ def initialize(message, patient = nil)
17
+ @patient = patient || ::Renalware::Patient.new
18
+
19
+ super(message)
20
+ end
21
+
22
+ attr_reader :patient
23
+
24
+ def fetch
25
+ map_attributes
26
+ patient
27
+ end
28
+
29
+ private
30
+
31
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
32
+ def map_attributes
33
+ patient.attributes = {
34
+ local_patient_id: patient_identification.internal_id,
35
+ nhs_number: patient_identification.external_id,
36
+ given_name: patient_identification.given_name,
37
+ family_name: patient_identification.family_name,
38
+ suffix: patient_identification.suffix,
39
+ title: patient_identification.title,
40
+ born_on: Time.zone.parse(patient_identification.dob)&.to_date,
41
+ died_on: Time.zone.parse(patient_identification.death_date)&.to_date,
42
+ sex: patient_identification.sex,
43
+ practice: find_practice(message.practice_code) || patient.practice,
44
+ primary_care_physician: find_primary_care_physician(message.gp_code)
45
+ }
46
+ patient.build_current_address if patient.current_address.blank?
47
+ patient.current_address.attributes = {
48
+ street_1: address[0],
49
+ street_2: address[1],
50
+ street_3: nil,
51
+ town: address[2],
52
+ county: address[3],
53
+ postcode: address.last
54
+ }
55
+ end
56
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
57
+
58
+ # If for some reason we cannot find the new practice (perhaps we have not imported it yet)
59
+ # then be sure to leave the patient's current practice unchanged.
60
+ def find_practice(code)
61
+ Patients::Practice.find_by(code: code) || patient.practice
62
+ end
63
+
64
+ # If for some reason we cannot find the new gp (perhaps we have not imported them yet)
65
+ # then be sure to leave the patient's current gp unchanged.
66
+ def find_primary_care_physician(code)
67
+ Patients::PrimaryCarePhysician.find_by(code: code) || patient.primary_care_physician
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "renalware/feeds"
4
+ require "attr_extras"
5
+
6
+ module Renalware
7
+ module Patients
8
+ module Ingestion
9
+ class UpdateMasterPatientIndex
10
+ pattr_initialize :hl7_message
11
+ attr_reader :rw_patient
12
+ delegate :patient_identification, to: :hl7_message
13
+
14
+ def self.call(hl7_message)
15
+ new(hl7_message).call
16
+ end
17
+
18
+ def call
19
+ return unless hl7_message.adt?
20
+
21
+ @rw_patient = find_patient_in_renalware
22
+ update_or_create_abridged_patient
23
+ # update_primary_care_physician
24
+ # update_practice
25
+ end
26
+
27
+ private
28
+
29
+ def find_patient_in_renalware
30
+ ::Renalware::Patient.find_by(
31
+ local_patient_id: patient_identification.internal_id
32
+ ) || NullObject.instance
33
+ end
34
+
35
+ # rubocop:disable Metrics/AbcSize
36
+ def update_or_create_abridged_patient
37
+ find_or_initialize_abridged_patient.update!(
38
+ given_name: patient_identification.given_name,
39
+ family_name: patient_identification.family_name,
40
+ sex: patient_identification.sex,
41
+ title: patient_identification.title,
42
+ suffix: patient_identification.suffix,
43
+ born_on: patient_identification.born_on,
44
+ died_at: patient_identification.died_at,
45
+ patient_id: rw_patient.id,
46
+ practice_code: hl7_message.practice_code,
47
+ gp_code: hl7_message.gp_code
48
+ )
49
+ end
50
+ # rubocop:enable Metrics/AbcSize
51
+
52
+ def find_or_initialize_abridged_patient
53
+ Patients::Abridgement.find_or_initialize_by(
54
+ hospital_number: patient_identification.internal_id
55
+ )
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,20 @@
1
+ # # frozen_string_literal: true
2
+
3
+ # require "renalware/feeds"
4
+ # require "attr_extras"
5
+
6
+ # module Renalware
7
+ # module Patients
8
+ # module Ingestion
9
+ # class UpdateMasterPatientIndex < ApplicationJob
10
+ # queue_as :master_patient_index
11
+ # queue_with_priority 10
12
+
13
+ # def perform(feed_message:)
14
+ # raise "todo"
15
+ # # UpdateMasterPatientIndex.call()
16
+ # end
17
+ # end
18
+ # end
19
+ # end
20
+ # end
@@ -13,9 +13,10 @@ module Renalware
13
13
  end
14
14
 
15
15
  def configure
16
- # Note that we no longer create all patients that coe in from the HL7 feed, so
17
- # MessageListener has been removed from the registry.
18
- # SubscriptionRegistry.instance.register(Feeds::MessageProcessor, MessageListener)
16
+ SubscriptionRegistry.instance.register(
17
+ Feeds::MessageProcessor,
18
+ Ingestion::MessageListener
19
+ )
19
20
  end
20
21
 
21
22
  def self.cast_user(user)
@@ -12,11 +12,15 @@ module Renalware
12
12
  has_paper_trail class_name: "Renalware::Problems::Version", on: [:create, :update, :destroy]
13
13
 
14
14
  belongs_to :patient, touch: true
15
- has_many :notes, dependent: :destroy
15
+ has_many :notes, -> { ordered }, dependent: :destroy
16
16
 
17
17
  scope :ordered, -> { order(position: :asc) }
18
- scope :with_notes, -> { includes(:notes) }
18
+ scope :with_notes, -> { eager_load(:notes).merge(Renalware::Problems::Note.ordered) }
19
19
  scope :with_patient, -> { includes(:patient) }
20
+ scope :with_versions, -> { includes(versions: :item) }
21
+
22
+ # This scope is called by CoreExtensions::ActiveRecord::Sort
23
+ scope :position_sorting_scope, ->(problem) { where(patient_id: problem.patient.id) }
20
24
 
21
25
  validates :patient, presence: true
22
26
  validates :description, presence: true
@@ -6,17 +6,16 @@ module Renalware
6
6
  module UKRDC
7
7
  module Incoming
8
8
  class Paths
9
- def initialize
10
- FileUtils.mkdir_p incoming
11
- FileUtils.mkdir_p archive
12
- end
13
-
14
9
  def incoming
15
- @incoming ||= working_path.join("incoming")
10
+ @incoming ||= begin
11
+ working_path.join("incoming").tap { |path| FileUtils.mkdir_p(path) }
12
+ end
16
13
  end
17
14
 
18
15
  def archive
19
- @archive ||= working_path.join("archive", "incoming")
16
+ @archive ||= begin
17
+ working_path.join("archive", "incoming").tap { |path| FileUtils.mkdir_p(path) }
18
+ end
20
19
  end
21
20
 
22
21
  private
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/admin"
4
+
5
+ module Renalware
6
+ module Admin
7
+ class DevopsPolicy < BasePolicy
8
+ def show?
9
+ user_is_devops?
10
+ end
11
+ alias index? show?
12
+ alias edit? show?
13
+ alias update? show?
14
+ alias destroy? show?
15
+ alias new? show?
16
+ end
17
+ end
18
+ end
@@ -2,7 +2,9 @@
2
2
 
3
3
  module Renalware
4
4
  module HD
5
- class DiaryPolicy < BasePolicy
5
+ module Scheduling
6
+ class DiaryPolicy < BasePolicy
7
+ end
6
8
  end
7
9
  end
8
10
  end
@@ -39,7 +39,7 @@ module Renalware
39
39
  end
40
40
 
41
41
  def dry_weights
42
- @dry_weights ||= DryWeight.for_patient(patient).ordered
42
+ @dry_weights ||= DryWeight.for_patient(patient).ordered.limit(5)
43
43
  end
44
44
 
45
45
  def body_compositions
@@ -48,6 +48,7 @@ module Renalware
48
48
  .for_patient(patient)
49
49
  .includes([:modality_description, :assessor])
50
50
  .ordered
51
+ .limit(5)
51
52
  end
52
53
  end
53
54
  end
@@ -10,7 +10,7 @@ module Renalware
10
10
  end
11
11
 
12
12
  def to_hash
13
- { id: id, label: name_and_address }
13
+ { id: id, text: name_and_address }
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "collection_presenter"
4
+
5
+ # TODO: mixing query and presenter here..
6
+ module Renalware
7
+ module HD
8
+ module Scheduling
9
+ class DiaryPresenter
10
+ attr_reader :user, :weekly_diary, :master_diary, :null_diary
11
+ delegate :id, :hospital_unit_id, :to_s, :week, :created_at, to: :weekly_diary
12
+
13
+ # https://github.com/avdi/naught
14
+ NullDiary = Naught.build do |config|
15
+ config.black_hole
16
+ config.define_explicit_conversions
17
+ config.predicates_return false
18
+
19
+ def master?
20
+ false
21
+ end
22
+
23
+ def slot_for(diary_id, diurnal_period_code_id, station_id, day_of_week)
24
+ NullSlot.new(diary_id, diurnal_period_code_id, station_id, day_of_week)
25
+ end
26
+ end
27
+
28
+ def initialize(user, weekly_diary)
29
+ @user = user
30
+ @weekly_diary = weekly_diary
31
+ @master_diary = weekly_diary.master_diary
32
+ @null_diary = NullDiary.new
33
+ end
34
+
35
+ def each_diurnal_period
36
+ DiurnalPeriodCode.all.each do |diurnal_period_code|
37
+ yield(diurnal_period_code) if block_given?
38
+ end
39
+ end
40
+
41
+ def each_station
42
+ stations.each_with_index { |station, index| yield(station, index + 1) if block_given? }
43
+ end
44
+
45
+ # rubocop:disable Metrics/LineLength, Metrics/AbcSize
46
+ def each_day(diurnal_period, station)
47
+ (1..last_day_of_week).each do |day_of_week|
48
+ diurnal_period_id = diurnal_period.id
49
+ station_id = station.id
50
+ valid_from = weekly_diary.week.date_on_first_day_of_week
51
+ slot = weekly_diary.slot_for(diurnal_period_id, station_id, day_of_week) ||
52
+ master_diary.slot_for(diurnal_period_id, station_id, day_of_week, valid_from: valid_from) ||
53
+ null_diary.slot_for(weekly_diary.id, diurnal_period_id, station_id, day_of_week)
54
+ yield(slot) if block_given?
55
+ end
56
+ end
57
+ # rubocop:enable Metrics/LineLength, Metrics/AbcSize
58
+
59
+ def day_names
60
+ all_day_names = Time::DAYS_INTO_WEEK.keys
61
+ all_day_names.take(last_day_of_week)
62
+ end
63
+
64
+ def stations
65
+ @stations ||= begin
66
+ Station.includes(:location).for_unit(hospital_unit_id).ordered.map do |station|
67
+ StationPresenter.new(station)
68
+ end
69
+ end
70
+ end
71
+
72
+ def station_locations
73
+ StationLocation.all
74
+ end
75
+
76
+ private
77
+
78
+ def last_day_of_week
79
+ Renalware.config.include_sunday_on_hd_diaries ? 7 : 6
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/hd"
4
+ require_dependency "collection_presenter"
5
+
6
+ # TODO: mixing query and presenter here..
7
+ module Renalware
8
+ module HD
9
+ module Scheduling
10
+ class DiarySlotPresenter < SimpleDelegator
11
+ delegate :master?, to: :diary, allow_nil: true
12
+
13
+ # Patients who prefer to dialyse on this day e.g. Mon and in this period e.g. AM.
14
+ # Flag those already assigned so they cannot be chosen.
15
+ def patients_preferring_to_dialyse_today_in_this_period
16
+ patients = Renalware::HD::PatientsDialysingByDayAndPeriodQuery
17
+ .new(
18
+ diary.hospital_unit_id,
19
+ day_of_week,
20
+ diurnal_period_code.code
21
+ ).call.all
22
+ simplify(patients)
23
+ end
24
+
25
+ # Patients who prefer to dialyse on this day e.g. Mon
26
+ # Flag those already assigned so they cannot be chosen.
27
+ def patients_preferring_to_dialyse_today
28
+ patients = Renalware::HD::PatientsDialysingByDayQuery
29
+ .new(
30
+ diary.hospital_unit_id,
31
+ day_of_week
32
+ ).call.all
33
+ simplify(patients)
34
+ end
35
+
36
+ # rubocop:disable Metrics/MethodLength
37
+ def patient_search_options
38
+ hospital_unit = Renalware::Hospitals::Unit.find(diary.hospital_unit_id)
39
+ [
40
+ OpenStruct.new(
41
+ id: :dialysing_on_day_and_period,
42
+ name: "Dialysing #{day_of_week_name} #{diurnal_period_code.to_s.upcase}"
43
+ ),
44
+ OpenStruct.new(
45
+ id: :dialysing_on_day,
46
+ name: "Dialysing on #{day_of_week_name}"
47
+ ),
48
+ OpenStruct.new(
49
+ id: :dialysing_at_unit,
50
+ name: "All #{hospital_unit.unit_code} HD patients"
51
+ ),
52
+ OpenStruct.new(
53
+ id: :dialysing_at_hospital,
54
+ name: "All HD patients"
55
+ )
56
+ ]
57
+ end
58
+ # rubocop:enable Metrics/MethodLength
59
+
60
+ private
61
+
62
+ def simplify(patients)
63
+ patients.map do |patient|
64
+ hd_profile = patient.hd_profile
65
+ text = "#{patient.to_s(:long)} - "\
66
+ "#{hd_profile&.schedule_definition} "\
67
+ "#{hd_profile&.hospital_unit&.unit_code}".strip.truncate(65)
68
+ OpenStruct.new(id: patient.id, text: text)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end