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.
- checksums.yaml +4 -4
- data/Rakefile +0 -2
- data/app/assets/javascripts/renalware/application.js.erb +0 -1
- data/app/assets/javascripts/renalware/feed_only_inputs.js.erb +26 -0
- data/app/assets/javascripts/renalware/feeds.js +50 -0
- data/app/assets/javascripts/renalware/person_ajax_search.js +43 -0
- data/app/assets/stylesheets/renalware/application.scss +0 -1
- data/app/assets/stylesheets/renalware/modules/_admin.scss +7 -0
- data/app/assets/stylesheets/renalware/modules/_pathology.scss +1 -1
- data/app/assets/stylesheets/renalware/modules/_patients.scss +6 -0
- data/app/assets/stylesheets/renalware/modules/_snippets.scss +2 -2
- data/app/assets/stylesheets/renalware/partials/_forms.scss +5 -1
- data/app/assets/stylesheets/renalware/partials/_layout.scss +4 -0
- data/app/{views/renalware/patients/_side_menu.html.slim → components/renalware/patients/side_menu_component.html.slim} +0 -0
- data/app/components/renalware/patients/side_menu_component.rb +18 -0
- data/app/controllers/renalware/clinical/body_compositions_controller.rb +6 -0
- data/app/controllers/renalware/feeds/hl7_test_messages_controller.rb +60 -0
- data/app/controllers/renalware/hd/mdm_patients_controller.rb +16 -6
- data/app/controllers/renalware/hd/scheduling/diaries_controller.rb +91 -0
- data/app/controllers/renalware/hd/scheduling/diary_slots_controller.rb +169 -0
- data/app/controllers/renalware/hd/session_forms/batches_controller.rb +1 -1
- data/app/controllers/renalware/pathology/observation_requests_controller.rb +23 -4
- data/app/controllers/renalware/pathology/requests/requests_controller.rb +0 -4
- data/app/controllers/renalware/patients/abridgements_controller.rb +39 -0
- data/app/controllers/renalware/pd/regimes_controller.rb +26 -0
- data/app/controllers/renalware/problems/problems_controller.rb +2 -2
- data/app/helpers/renalware/application_helper.rb +9 -0
- data/app/helpers/renalware/article_helper.rb +18 -0
- data/app/helpers/renalware/form_helper.rb +3 -2
- data/app/helpers/renalware/users_helper.rb +5 -1
- data/app/javascript/controllers/clipboard_controller.js +16 -0
- data/app/javascript/packs/renalware_core.js +20 -0
- data/app/jobs/feed_job.rb +1 -3
- data/app/jobs/hl7_message_example.yml +2 -2
- data/app/models/renalware/feeds/files/practice_memberships/import_job.rb +3 -0
- data/app/models/renalware/feeds/files/primary_care_physicians/import_job.rb +2 -0
- data/app/models/renalware/feeds/hl7_message.rb +57 -6
- data/app/models/renalware/feeds/hl7_test_form.rb +10 -0
- data/app/models/renalware/feeds/hl7_test_message.rb +11 -0
- data/app/models/renalware/feeds/message_parser.rb +6 -2
- data/app/models/renalware/feeds/message_processor.rb +44 -32
- data/app/models/renalware/feeds/persist_message.rb +9 -3
- data/app/models/renalware/feeds.rb +2 -0
- data/app/models/renalware/hd/scheduling/archive_arguments.rb +19 -0
- data/app/models/renalware/hd/scheduling/diary.rb +54 -0
- data/app/models/renalware/hd/scheduling/diary_housekeeping_job.rb +84 -0
- data/app/models/renalware/hd/scheduling/diary_range.rb +69 -0
- data/app/models/renalware/hd/scheduling/diary_slot.rb +88 -0
- data/app/models/renalware/hd/scheduling/find_or_create_diary_by_week_query.rb +52 -0
- data/app/models/renalware/hd/scheduling/find_or_create_master_diary.rb +27 -0
- data/app/models/renalware/hd/scheduling/master_diary.rb +35 -0
- data/app/models/renalware/hd/scheduling/weekly_diary.rb +47 -0
- data/app/models/renalware/pathology/message_listener.rb +2 -2
- data/app/models/renalware/pathology/requests/global_rule/latest_crf_older_than_weeks.rb +38 -0
- data/app/models/renalware/pathology/requests/global_rule/patient_is_diabetic.rb +1 -1
- data/app/models/renalware/patient.rb +1 -1
- data/app/models/renalware/patients/abridgement.rb +18 -0
- data/app/models/renalware/patients/abridgement_search_form.rb +12 -0
- data/app/models/renalware/patients/ingestion/command.rb +25 -0
- data/app/models/renalware/patients/ingestion/command_factory.rb +135 -0
- data/app/models/renalware/patients/ingestion/commands/add_or_update_patient.rb +65 -0
- data/app/models/renalware/patients/ingestion/message_listener.rb +21 -0
- data/app/models/renalware/patients/ingestion/message_mapper.rb +26 -0
- data/app/models/renalware/patients/ingestion/message_mappers/patient.rb +73 -0
- data/app/models/renalware/patients/ingestion/update_master_patient_index.rb +60 -0
- data/app/models/renalware/patients/ingestion/update_master_patient_index_job.rb.dead +20 -0
- data/app/models/renalware/patients.rb +4 -3
- data/app/models/renalware/problems/problem.rb +6 -2
- data/app/models/renalware/ukrdc/incoming/paths.rb +6 -7
- data/app/policies/renalware/admin/devops_policy.rb +18 -0
- data/app/policies/renalware/hd/{diary_policy.rb → scheduling/diary_policy.rb} +3 -1
- data/app/presenters/renalware/clinical/profile_presenter.rb +2 -1
- data/app/presenters/renalware/directory/person_auto_complete_presenter.rb +1 -1
- data/app/presenters/renalware/hd/scheduling/diary_presenter.rb +84 -0
- data/app/presenters/renalware/hd/scheduling/diary_slot_presenter.rb +74 -0
- data/app/presenters/renalware/hd/scheduling/null_slot.rb +42 -0
- data/app/presenters/renalware/pd/dashboard_presenter.rb +3 -2
- data/app/views/renalware/accesses/procedures/_form.html.slim +5 -1
- data/app/views/renalware/accesses/procedures/_list.html.slim +11 -4
- data/app/views/renalware/accesses/procedures/show.html.slim +17 -19
- data/app/views/renalware/addresses/_form.html.slim +17 -12
- data/app/views/renalware/admin/cache/show.html.slim +24 -0
- data/app/views/renalware/clinical/body_compositions/_list.html.slim +7 -43
- data/app/views/renalware/clinical/body_compositions/_table.html.slim +42 -0
- data/app/views/renalware/clinical/body_compositions/index.html.slim +9 -0
- data/app/views/renalware/clinical/dry_weights/_list.html.slim +2 -8
- data/app/views/renalware/clinical/dry_weights/_table.html.slim +7 -0
- data/app/views/renalware/clinical/profiles/show.html.slim +4 -2
- data/app/views/renalware/feeds/hl7_test_messages/create.js.erb +10 -0
- data/app/views/renalware/feeds/hl7_test_messages/new.slim +18 -0
- data/app/views/renalware/hd/mdm_patients/_filters.html.slim +0 -11
- data/app/views/renalware/hd/mdm_patients/_page_actions.html.slim +29 -0
- data/app/views/renalware/hd/mdm_patients/index.html.slim +7 -4
- data/app/views/renalware/hd/{diaries → scheduling/diaries}/_page_actions.html.slim +4 -4
- data/app/views/renalware/hd/{diaries → scheduling/diaries}/_table.html.slim +0 -0
- data/app/views/renalware/hd/{diaries → scheduling/diaries}/_weekly_diary.html.slim +1 -1
- data/app/views/renalware/hd/{diaries → scheduling/diaries}/edit.html.slim +6 -6
- data/app/views/renalware/hd/{diaries → scheduling/diaries}/index.html.slim +0 -0
- data/app/views/renalware/hd/{diaries → scheduling/diaries}/show.pdf.slim +0 -0
- data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/_form.html.slim +15 -6
- data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/_slot.html.slim +2 -2
- data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/_tab.html.slim +1 -1
- data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/create.js.erb +1 -1
- data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/destroy.js.erb +1 -1
- data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/edit.html.slim +2 -2
- data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/new.html.slim +0 -0
- data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/new.js.erb +0 -0
- data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/show.js.erb +0 -0
- data/app/views/renalware/hd/{diary_slots → scheduling/diary_slots}/update.js.erb +1 -1
- data/app/views/renalware/hospitals/units/index.html.slim +2 -2
- data/app/views/renalware/layouts/_patient.html.slim +2 -1
- data/app/views/renalware/letters/contacts/_form.html.slim +10 -13
- data/app/views/renalware/letters/letters/_form.html.slim +2 -9
- data/app/views/renalware/navigation/_developer.html.slim +4 -0
- data/app/views/renalware/navigation/_menu.html.slim +2 -0
- data/app/views/renalware/pathology/observation_requests/_filters.html.slim +17 -0
- data/app/views/renalware/pathology/observation_requests/index.html.slim +2 -1
- data/app/views/renalware/patients/_layout.html.slim +1 -1
- data/app/views/renalware/patients/_side_menu.html.slim.dead +7 -0
- data/app/views/renalware/patients/abridgements/_abridgement.html.slim +8 -0
- data/app/views/renalware/patients/abridgements/_table.html.slim +12 -0
- data/app/views/renalware/patients/abridgements/index.html.slim +22 -0
- data/app/views/renalware/patients/alerts/create.json +3 -0
- data/app/views/renalware/patients/patients/_form.html.slim +36 -17
- data/app/views/renalware/patients/patients/edit.html.slim +1 -1
- data/app/views/renalware/patients/patients/new.html.slim +1 -1
- data/app/views/renalware/patients/side_menu/_actions.html.slim +2 -0
- data/app/views/renalware/pd/_apd_regimes.html.slim +1 -1
- data/app/views/renalware/pd/_capd_regimes.html.slim +1 -1
- data/app/views/renalware/pd/dashboards/show/_apd_regimes.html.slim +11 -2
- data/app/views/renalware/pd/dashboards/show/_capd_regimes.html.slim +11 -2
- data/app/views/renalware/pd/regimes/index.html.slim +7 -0
- data/app/views/renalware/renal/profiles/_form.html.slim +7 -24
- data/app/views/renalware/shared/documents/_binary_marker_input.html.slim +2 -2
- data/app/views/renalware/shared/documents/_blood_group_input.html.slim +4 -2
- data/app/views/renalware/transplants/registrations/_form.html.slim +22 -17
- data/config/locales/renalware/clinical/body_composition.yml +6 -3
- data/config/locales/renalware/clinical/dry_weight.en.yml +4 -3
- data/config/locales/renalware/hd/diary_slots.en.yml +3 -2
- data/config/locales/renalware/letters/contact.en.yml +0 -2
- data/config/routes/feeds.rb +5 -0
- data/config/routes/hd.rb +11 -7
- data/config/routes/letters.rb +0 -5
- data/config/routes/patients.rb +1 -0
- data/config/routes/pd.rb +2 -2
- data/config/routes/renal.rb +0 -5
- data/config/routes.rb +1 -0
- data/config/webpack/development.js +5 -0
- data/config/webpack/environment.js +3 -0
- data/config/webpack/production.js +5 -0
- data/config/webpack/test.js +5 -0
- data/config/webpacker.yml +103 -0
- data/db/functions/hd_diary_archive_elapsed_master_slots_v01.sql +40 -0
- data/db/migrate/20190322120025_create_feed_hl7_test_messages.rb +12 -0
- data/db/migrate/20190325134823_create_patients_master_index.rb +24 -0
- data/db/migrate/20190327100851_add_handled_to_feed_messages.rb +5 -0
- data/db/migrate/20191012121433_add_consultant_to_users.rb +7 -0
- data/db/migrate/20191018143635_create_hd_diary_matrix_view.rb +7 -0
- data/db/migrate/20191018144917_create_fn_to_archive_master_slots.rb +9 -0
- data/db/views/hd_diary_matrix_v01.sql +38 -0
- data/lib/core_extensions/active_record/sort.rb +27 -9
- data/lib/renalware/configuration.rb +3 -0
- data/lib/renalware/engine.rb +31 -0
- data/lib/renalware/version.rb +1 -1
- data/lib/renalware/week_period.rb +8 -7
- data/lib/renalware.rb +13 -1
- data/lib/tasks/hd.rake +7 -0
- data/lib/tasks/pathology.rake +1 -1
- data/lib/tasks/renalware.rake +89 -4
- data/spec/factories/hd/scheduling/diaries.rb +18 -0
- data/spec/factories/hd/{slots.rb → scheduling/slots.rb} +1 -1
- data/spec/factories/patients/abridgements.rb +9 -0
- data/spec/support/hl7_helpers.rb +13 -0
- data/spec/support/pages/letters/form.rb +4 -1
- metadata +126 -50
- data/app/assets/javascripts/renalware/auto_complete.js +0 -63
- data/app/controllers/renalware/hd/diaries_controller.rb +0 -89
- data/app/controllers/renalware/hd/diary_slots_controller.rb +0 -175
- data/app/controllers/renalware/letters/descriptions_controller.rb +0 -22
- data/app/controllers/renalware/renal/prd_descriptions_controller.rb +0 -15
- data/app/models/renalware/hd/archive_yesterdays_slots_job.rb +0 -69
- data/app/models/renalware/hd/diary.rb +0 -41
- data/app/models/renalware/hd/diary_slot.rb +0 -83
- data/app/models/renalware/hd/find_or_create_diary_by_week_query.rb +0 -50
- data/app/models/renalware/hd/find_or_create_master_diary.rb +0 -26
- data/app/models/renalware/hd/master_diary.rb +0 -32
- data/app/models/renalware/hd/weekly_diary.rb +0 -45
- data/app/models/renalware/renal/prd_descriptions/search_query.rb +0 -35
- data/app/presenters/renalware/hd/diary_presenter.rb +0 -79
- data/app/presenters/renalware/hd/diary_slot_presenter.rb +0 -72
- data/app/presenters/renalware/hd/null_slot.rb +0 -40
- data/app/views/renalware/renal/prd_descriptions/search.json.jbuilder +0 -6
- data/lib/test_support/autocomplete_helpers.rb +0 -14
- 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
|
-
|
17
|
-
|
18
|
-
|
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, -> {
|
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 ||=
|
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 ||=
|
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
|
@@ -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
|
@@ -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
|