renalware-core 2.0.113 → 2.0.115

Sign up to get free protection for your applications and to get access to all the features.
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