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
@@ -10,8 +10,12 @@ module Renalware
|
|
10
10
|
|
11
11
|
def index
|
12
12
|
observation_requests = find_observation_requests
|
13
|
-
|
14
|
-
|
13
|
+
render locals: {
|
14
|
+
observation_requests: observation_requests.result .page(page).per(per_page),
|
15
|
+
search: observation_requests,
|
16
|
+
obr_filter_options: obr_filter_options,
|
17
|
+
patient: @patient
|
18
|
+
}
|
15
19
|
end
|
16
20
|
|
17
21
|
def show
|
@@ -22,16 +26,31 @@ module Renalware
|
|
22
26
|
|
23
27
|
private
|
24
28
|
|
29
|
+
# Select just the OBR description ids and codes that have been associated with this patient.
|
30
|
+
# We'll uses them to build a filter dropdown list.
|
31
|
+
def obr_filter_options
|
32
|
+
@patient.observation_requests
|
33
|
+
.joins(:description)
|
34
|
+
.order("pathology_request_descriptions.code asc")
|
35
|
+
.pluck(
|
36
|
+
Arel.sql(
|
37
|
+
"distinct on(pathology_request_descriptions.code) pathology_request_descriptions.code"
|
38
|
+
),
|
39
|
+
"pathology_request_descriptions.id",
|
40
|
+
"pathology_request_descriptions.name"
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
25
44
|
def find_observation_requests
|
26
45
|
@patient.observation_requests
|
27
|
-
.page(page)
|
28
46
|
.includes(:description)
|
29
47
|
.ordered
|
48
|
+
.ransack(params[:q])
|
30
49
|
end
|
31
50
|
|
32
51
|
def find_observation_request
|
33
52
|
@patient.observation_requests
|
34
|
-
.includes(
|
53
|
+
.includes(observations: :description)
|
35
54
|
.find(params[:id])
|
36
55
|
end
|
37
56
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_dependency "renalware/patients"
|
4
|
+
|
5
|
+
module Renalware
|
6
|
+
module Patients
|
7
|
+
class AbridgementsController < BaseController
|
8
|
+
def index
|
9
|
+
authorize Abridgement, :index?
|
10
|
+
render locals: {
|
11
|
+
form: AbridgementSearchForm.new,
|
12
|
+
results: abridgements_matching_search_criteria,
|
13
|
+
results_matching_dob: abridgements_matching_dobs
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def abridgements_matching_search_criteria
|
20
|
+
return [] if search_params.blank?
|
21
|
+
|
22
|
+
Abridgement.where(hospital_number: search_params)
|
23
|
+
end
|
24
|
+
|
25
|
+
def abridgements_matching_dobs
|
26
|
+
return [] if abridgements_matching_search_criteria.empty?
|
27
|
+
|
28
|
+
dobs = abridgements_matching_search_criteria.map(&:born_on).uniq.compact
|
29
|
+
return [] if dobs.empty?
|
30
|
+
|
31
|
+
Abridgement.where(born_on: dobs) - abridgements_matching_search_criteria
|
32
|
+
end
|
33
|
+
|
34
|
+
def search_params
|
35
|
+
params.dig(:search, :criteria)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -5,8 +5,26 @@ require_dependency "renalware/hd/base_controller"
|
|
5
5
|
module Renalware
|
6
6
|
module PD
|
7
7
|
class RegimesController < BaseController
|
8
|
+
include Renalware::Concerns::Pageable
|
8
9
|
before_action :load_patient
|
9
10
|
|
11
|
+
def index
|
12
|
+
regimes = regime_type_class.for_patient(patient).with_bags.ordered.page(page).per(per_page)
|
13
|
+
render locals: {
|
14
|
+
patient: patient,
|
15
|
+
regimes: regimes,
|
16
|
+
pd_type_string: pd_type_string
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def capd_regimes
|
21
|
+
@capd_regimes ||= CAPDRegime.for_patient(patient).with_bags.ordered.page(1).per(5)
|
22
|
+
end
|
23
|
+
|
24
|
+
def apd_regimes
|
25
|
+
@apd_regimes ||= APDRegime.for_patient(patient).with_bags.ordered.page(1).per(5)
|
26
|
+
end
|
27
|
+
|
10
28
|
def new
|
11
29
|
regime = cloned_last_known_regime_of_type || patient.pd_regimes.new(type: regime_type)
|
12
30
|
|
@@ -70,6 +88,14 @@ module Renalware
|
|
70
88
|
params[:type] ? "Renalware::#{params[:type]}" : nil
|
71
89
|
end
|
72
90
|
|
91
|
+
def regime_type_class
|
92
|
+
@regime_type_class ||= regime_type.constantize
|
93
|
+
end
|
94
|
+
|
95
|
+
def pd_type_string
|
96
|
+
regime_type_class.new.pd_type.upcase # CAPD or APD
|
97
|
+
end
|
98
|
+
|
73
99
|
def cloned_last_known_regime_of_type
|
74
100
|
regime = patient.pd_regimes
|
75
101
|
.order(start_date: :desc, created_at: :desc)
|
@@ -4,7 +4,7 @@ module Renalware
|
|
4
4
|
module Problems
|
5
5
|
class ProblemsController < BaseController
|
6
6
|
def index
|
7
|
-
problems = patient.problems
|
7
|
+
problems = patient.problems.with_notes
|
8
8
|
authorize problems
|
9
9
|
render locals: {
|
10
10
|
patient: patient,
|
@@ -14,7 +14,7 @@ module Renalware
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def show
|
17
|
-
problem = patient.problems.with_archived.
|
17
|
+
problem = patient.problems.with_archived.with_versions.find(params[:id])
|
18
18
|
notes = problem.notes.with_updated_by.ordered
|
19
19
|
authorize problem
|
20
20
|
render locals: {
|
@@ -3,10 +3,17 @@
|
|
3
3
|
require "inline_image"
|
4
4
|
require "git_commit_sha"
|
5
5
|
require "breadcrumb"
|
6
|
+
require "webpacker/helper"
|
6
7
|
|
7
8
|
module Renalware
|
8
9
|
module ApplicationHelper
|
9
10
|
include Renalware::Engine.routes.url_helpers
|
11
|
+
include ::Webpacker::Helper
|
12
|
+
|
13
|
+
# See https://github.com/rails/webpacker/blob/master/docs/engines.md
|
14
|
+
def current_webpacker_instance
|
15
|
+
Renalware.webpacker
|
16
|
+
end
|
10
17
|
|
11
18
|
def default_patient_link(patient)
|
12
19
|
link_to(patient.to_s(:default), patient_clinical_summary_path(patient))
|
@@ -37,9 +44,11 @@ module Renalware
|
|
37
44
|
end
|
38
45
|
|
39
46
|
# For use in pages
|
47
|
+
# rubocop:disable Rails/OutputSafety
|
40
48
|
def page_heading(title)
|
41
49
|
content_for(:page_title) { title.html_safe }
|
42
50
|
end
|
51
|
+
# rubocop:enable Rails/OutputSafety
|
43
52
|
|
44
53
|
def t?(key)
|
45
54
|
t(key, cascade: false, raise: false, default: "").present?
|
@@ -21,5 +21,23 @@ module Renalware
|
|
21
21
|
output.concat(capture(&block)) if block_given?
|
22
22
|
output.safe_concat("</article>")
|
23
23
|
end
|
24
|
+
|
25
|
+
# Renders
|
26
|
+
# <span>5 of 16<div>
|
27
|
+
# if the collection has been paginated, otherwise
|
28
|
+
# <span>5<div>
|
29
|
+
def collection_count(collection)
|
30
|
+
return unless collection&.respond_to?(:length)
|
31
|
+
|
32
|
+
parts = ["("]
|
33
|
+
parts.append(collection.length)
|
34
|
+
if collection.respond_to?(:total_count)
|
35
|
+
if collection.total_count > collection.length
|
36
|
+
parts.append(" of #{collection.total_count}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
parts.append(")")
|
40
|
+
content_tag("span", parts.join(""))
|
41
|
+
end
|
24
42
|
end
|
25
43
|
end
|
@@ -6,13 +6,14 @@ module Renalware
|
|
6
6
|
" field_with_errors" if model.errors.key?(attr)
|
7
7
|
end
|
8
8
|
|
9
|
-
def render_input(builder, attribute)
|
9
|
+
def render_input(builder, attribute, html_options: {})
|
10
10
|
renderable = builder.object.public_send(attribute)
|
11
11
|
return unless renderable
|
12
12
|
|
13
13
|
render input_partial_path_for(renderable),
|
14
14
|
attribute: attribute,
|
15
|
-
f: builder
|
15
|
+
f: builder,
|
16
|
+
html_options: html_options
|
16
17
|
end
|
17
18
|
|
18
19
|
def input_partial_path_for(renderable)
|
@@ -2,8 +2,12 @@
|
|
2
2
|
|
3
3
|
module Renalware
|
4
4
|
module UsersHelper
|
5
|
+
def current_user_is_developer?
|
6
|
+
current_user.has_role?(:devops)
|
7
|
+
end
|
8
|
+
|
5
9
|
def current_user_is_super_admin?
|
6
|
-
current_user.has_role?(:super_admin) ||
|
10
|
+
current_user.has_role?(:super_admin) || current_user_is_developer?
|
7
11
|
end
|
8
12
|
|
9
13
|
def current_user_is_admin?
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import { Controller } from "stimulus"
|
2
|
+
|
3
|
+
// Experimental Stimulus clipboard controller. No IE11 support.
|
4
|
+
// Can be enhanced but initially this exists just so we can test stimulus/webpack integration
|
5
|
+
// when the engine is pulled into a host app
|
6
|
+
export default class extends Controller {
|
7
|
+
static targets = ["source", "result"]
|
8
|
+
|
9
|
+
copy(event) {
|
10
|
+
event.preventDefault()
|
11
|
+
this.sourceTarget.select()
|
12
|
+
document.execCommand("copy")
|
13
|
+
console.log("Copied")
|
14
|
+
this.resultTarget.innerHTML = "Copied"
|
15
|
+
}
|
16
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
// This file is automatically compiled by Webpack, along with any other files
|
2
|
+
// present in this directory. You're encouraged to place your actual application logic in
|
3
|
+
// a relevant structure within app/javascript and only use these pack files to reference
|
4
|
+
// that code so it'll be compiled.
|
5
|
+
// Uncomment to copy all static images under ../images to the output folder and reference
|
6
|
+
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
|
7
|
+
// or the `imagePath` JavaScript helper below.
|
8
|
+
//
|
9
|
+
// const images = require.context('../images', true)
|
10
|
+
// const imagePath = (name) => images(name, true)
|
11
|
+
|
12
|
+
// require("@rails/ujs").start()
|
13
|
+
// require("@rails/activestorage").start()
|
14
|
+
|
15
|
+
import { Application } from "stimulus"
|
16
|
+
import { definitionsFromContext } from "stimulus/webpack-helpers"
|
17
|
+
|
18
|
+
const application = Application.start()
|
19
|
+
const context = require.context("../controllers", true, /\.js$/)
|
20
|
+
application.load(definitionsFromContext(context))
|
data/app/jobs/feed_job.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/struct:FeedJob
|
2
2
|
raw_message: |
|
3
3
|
MSH|^~\&|HM|LBE|SCM||20091112164645||ORU^R01|1258271|P|2.3.1|||AL||||
|
4
|
-
PID|||
|
4
|
+
PID|||Z100002^^^PAS Number||RABBIT^JESSICA^^^MS||19880924|F|||18 RABBITHOLE ROAD^LONDON^^^SE8 8JR|||||||||||||||||||
|
5
5
|
PV1||Inpatient|NIBC^^^^^^^^|||||MID^KINGS MIDWIVES||||||||||NHS|HXF888888^^^Visit Number|||||||||
|
6
6
|
ORC|RE|^PCS|09B0099478^LA||CM||||200911111841|||MID^KINGS MIDWIVES|||||||
|
7
|
-
OBR|1
|
7
|
+
OBR|1|PlacerOrderNumber1^PCS|09B0099478^LA|FBC^FULL BLOOD COUNT^MB||200911111841|200911111841|||||||200911111841|B^Blood|MID^KINGS MIDWIVES||09B0099478||||200911121646||HM|F||||||||||||||||||
|
8
8
|
OBX|1|TX|WBC^WBC^MB||6.09||||||F|||200911112026||BBKA^Donald DUCK|
|
9
9
|
OBX|2|TX|RBC^RBC^MB||4.00||||||F|||200911112026||BBKA^Donald DUCK|
|
10
10
|
OBX|3|TX|HGB^Hb^MB||11.8||||||F|||200911112026||BBKA^Donald DUCK|
|
@@ -11,6 +11,8 @@ module Renalware
|
|
11
11
|
include Feeds::Job
|
12
12
|
FILE_TO_EXTRACT_FROM_ARCHIVE = /epracmem.csv/.freeze
|
13
13
|
|
14
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
15
|
+
# TODO: refactor
|
14
16
|
def perform(file)
|
15
17
|
logging_to_stringio(strio = StringIO.new)
|
16
18
|
log "Before upload there are #{practice_membership_count} practice memberships"
|
@@ -25,6 +27,7 @@ module Renalware
|
|
25
27
|
ensure
|
26
28
|
file.update!(status: status, result: strio.string, time_taken: elapsed_ms)
|
27
29
|
end
|
30
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
28
31
|
|
29
32
|
private
|
30
33
|
|
@@ -12,6 +12,7 @@ module Renalware
|
|
12
12
|
|
13
13
|
FILE_TO_EXTRACT_FROM_ARCHIVE = /^egpcur.csv$/.freeze
|
14
14
|
|
15
|
+
# rubocop:disable Metrics/AbcSize
|
15
16
|
def perform(file)
|
16
17
|
logging_to_stringio(strio = StringIO.new)
|
17
18
|
log "PrimaryCarePhysician count before update: #{primary_care_physician_count}"
|
@@ -26,6 +27,7 @@ module Renalware
|
|
26
27
|
ensure
|
27
28
|
file.update!(status: status, result: strio.string, time_taken: elapsed_ms)
|
28
29
|
end
|
30
|
+
# rubocop:enable Metrics/AbcSize
|
29
31
|
|
30
32
|
private
|
31
33
|
|
@@ -11,10 +11,19 @@ module Renalware
|
|
11
11
|
# HL7Message.new(raw_message).patient_identification.internal_id
|
12
12
|
#
|
13
13
|
class HL7Message < SimpleDelegator
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
ACTIONS = {
|
15
|
+
"ADT^A28" => :add_person_information,
|
16
|
+
"ADT^A31" => :update_person_information,
|
17
|
+
"ADT^A08" => :update_admission,
|
18
|
+
"ADT^A01" => :admit_patient,
|
19
|
+
"ADT^A02" => :transfer_patient,
|
20
|
+
"ADT^A03" => :discharge_patient,
|
21
|
+
"ADT^A11" => :cancel_admission,
|
22
|
+
"MFN^M02" => :add_consultant,
|
23
|
+
"ADT^A34" => :merge_patient,
|
24
|
+
"ADT^A13" => :cancel_discharge,
|
25
|
+
"ORU^R01" => :add_pathology_observations
|
26
|
+
}.freeze
|
18
27
|
|
19
28
|
class ObservationRequest < SimpleDelegator
|
20
29
|
alias_attribute :date_time, :observation_date
|
@@ -117,6 +126,8 @@ module Renalware
|
|
117
126
|
alias_attribute :external_id, :patient_id
|
118
127
|
alias_attribute :sex, :admin_sex
|
119
128
|
alias_attribute :dob, :patient_dob
|
129
|
+
alias_attribute :born_on, :patient_dob
|
130
|
+
alias_attribute :died_at, :death_date
|
120
131
|
|
121
132
|
def internal_id
|
122
133
|
patient_id_list.split("^").first
|
@@ -134,6 +145,18 @@ module Renalware
|
|
134
145
|
patient_name[1]
|
135
146
|
end
|
136
147
|
|
148
|
+
def suffix
|
149
|
+
patient_name[3]
|
150
|
+
end
|
151
|
+
|
152
|
+
def title
|
153
|
+
patient_name[4]
|
154
|
+
end
|
155
|
+
|
156
|
+
def address
|
157
|
+
super.split("^")
|
158
|
+
end
|
159
|
+
|
137
160
|
private
|
138
161
|
|
139
162
|
def patient_name
|
@@ -153,8 +176,36 @@ module Renalware
|
|
153
176
|
self[:MSH].message_control_id
|
154
177
|
end
|
155
178
|
|
156
|
-
|
157
|
-
|
179
|
+
# Adding this so it is part of the interface and we can mock an HL7Message in tests
|
180
|
+
def to_hl7
|
181
|
+
super
|
182
|
+
end
|
183
|
+
|
184
|
+
def message_type
|
185
|
+
type.split("^").first
|
186
|
+
end
|
187
|
+
|
188
|
+
def event_type
|
189
|
+
parts = type.split("^")
|
190
|
+
parts.length == 2 && parts.last
|
191
|
+
end
|
192
|
+
|
193
|
+
%i(ORU ADT).each do |msg_type|
|
194
|
+
define_method(:"#{msg_type.to_s.downcase}?") do
|
195
|
+
msg_type.to_s == message_type
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def action
|
200
|
+
ACTIONS.fetch(type)
|
201
|
+
end
|
202
|
+
|
203
|
+
def practice_code
|
204
|
+
self[:PD1].e3.split("^")[2] if self[:PD1]
|
205
|
+
end
|
206
|
+
|
207
|
+
def gp_code
|
208
|
+
self[:PD1].e4.split("^")[0] if self[:PD1]
|
158
209
|
end
|
159
210
|
end
|
160
211
|
end
|
@@ -8,9 +8,13 @@ module Renalware
|
|
8
8
|
# message object.
|
9
9
|
#
|
10
10
|
class MessageParser
|
11
|
+
def self.parse(*args)
|
12
|
+
new.parse(*args)
|
13
|
+
end
|
14
|
+
|
11
15
|
def parse(message_string)
|
12
|
-
lines = message_string.split("\n").join("\r")
|
13
|
-
HL7Message.new(lines)
|
16
|
+
lines = message_string.split("\n").join("\r").lines
|
17
|
+
HL7Message.new(::HL7::Message.new(lines))
|
14
18
|
end
|
15
19
|
end
|
16
20
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_dependency "renalware/feeds"
|
4
|
+
require "attr_extras"
|
4
5
|
|
5
6
|
module Renalware
|
6
7
|
module Feeds
|
@@ -9,32 +10,48 @@ module Renalware
|
|
9
10
|
#
|
10
11
|
class MessageProcessor
|
11
12
|
include Broadcasting
|
13
|
+
attr_reader :raw_message, :hl7_message, :feed_message
|
12
14
|
|
13
|
-
#
|
14
|
-
|
15
|
-
|
15
|
+
# We want to wrap message processing in a transaction because if message processing
|
16
|
+
# fails we don't want to leave an unprocessed message in the feed_messages table.
|
17
|
+
# If we did, and the same FeedJob retires a few minutes later, if will try to save to
|
18
|
+
# feed_messages with the same MD5 body_hash (the message is identical to one already saved)
|
19
|
+
# resulting in unique key violation.
|
20
|
+
# Using a transaction here prevents any orphaned records if there is an error.
|
21
|
+
# However we should be aware that any listeners raising an error will prevent successful
|
22
|
+
# in all other listeners. So a listener should be careful to catch errors and not re-raise
|
23
|
+
# them, or use the :message_processed message (lower down) which is safer.
|
16
24
|
def call(raw_message)
|
17
|
-
|
25
|
+
@raw_message = raw_message
|
18
26
|
|
19
|
-
|
20
|
-
# fails we don't want to leave an unprocessed message in the feed_messages table.
|
21
|
-
# If we did, and the same FeedJob retires a few minutes later, if will try to save to
|
22
|
-
# feed_messages with the same MD5 body_hash (the message is identical to one already saved)
|
23
|
-
# resulting in unique key violation.
|
24
|
-
# Using a transaction here prevents any orphaned records if there is an error.
|
25
|
-
# However we should be aware that any listeners raising an error will prevent successful
|
26
|
-
# in all other listeners. So a listener should be careful to catch errors and not re-raise
|
27
|
-
# them, or use the :message_processed message (lower down) which is safer.
|
27
|
+
parse_raw_message_into_hl7_object
|
28
28
|
ActiveRecord::Base.transaction do
|
29
|
-
|
30
|
-
|
31
|
-
broadcast(
|
32
|
-
:message_arrived,
|
33
|
-
hl7_message: hl7_message,
|
34
|
-
feed_message: feed_message
|
35
|
-
)
|
29
|
+
create_feed_message_using_raw_message_and_basic_extracted_patient_data
|
30
|
+
allow_listeners_to_process_the_message
|
36
31
|
end
|
37
32
|
|
33
|
+
allow_listeners_to_post_process_the_message
|
34
|
+
rescue StandardError => exception
|
35
|
+
notify_exception(exception)
|
36
|
+
raise exception
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def notify_exception(exception)
|
42
|
+
Engine.exception_notifier.notify(exception)
|
43
|
+
end
|
44
|
+
|
45
|
+
def allow_listeners_to_process_the_message
|
46
|
+
message_to_broadcast = "#{hl7_message.message_type.downcase}_message_arrived"
|
47
|
+
broadcast(
|
48
|
+
message_to_broadcast.to_sym,
|
49
|
+
hl7_message: hl7_message,
|
50
|
+
feed_message: feed_message
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
def allow_listeners_to_post_process_the_message
|
38
55
|
# Another event, this one letting anyone interested know that a message been successfully
|
39
56
|
# processed. They might want to forward the message on somewhere else for instance.
|
40
57
|
# Think Diaverum.
|
@@ -44,8 +61,9 @@ module Renalware
|
|
44
61
|
# e.g. forwarding, logging etc.
|
45
62
|
# Its is recommended here to use an async listener - see example in renalware-diaverum
|
46
63
|
# - so that any error in the listener has its own try mechansim and does not cause the
|
47
|
-
# current job to retry
|
48
|
-
|
64
|
+
# current job to retry.
|
65
|
+
message_to_broadcast = "#{hl7_message.message_type.downcase}_message_processed"
|
66
|
+
broadcast(message_to_broadcast.to_sym, feed_message: feed_message)
|
49
67
|
rescue Feeds::DuplicateMessageReceivedError => e
|
50
68
|
Rails.logger.warn("Rejected duplicate HL7 message: #{e.message}")
|
51
69
|
rescue StandardError => e
|
@@ -53,10 +71,8 @@ module Renalware
|
|
53
71
|
raise e
|
54
72
|
end
|
55
73
|
|
56
|
-
|
57
|
-
|
58
|
-
def build_hl7_object_from(raw_message)
|
59
|
-
MessageParser.new.parse(raw_message)
|
74
|
+
def parse_raw_message_into_hl7_object
|
75
|
+
@hl7_message = MessageParser.parse(raw_message)
|
60
76
|
end
|
61
77
|
|
62
78
|
# If the incoming message has already been processed we should not be processing it again.
|
@@ -64,12 +80,8 @@ module Renalware
|
|
64
80
|
# same message payload is saved twice - in this case we exit #call early, the broadcast
|
65
81
|
# is not issued and therefore the message is not processed. The message will go back into
|
66
82
|
# the delayed_job queue and retry, failing until it finally gives up!
|
67
|
-
def
|
68
|
-
PersistMessage.new.call(hl7_message)
|
69
|
-
end
|
70
|
-
|
71
|
-
def notify_exception(exception)
|
72
|
-
Engine.exception_notifier.notify(exception)
|
83
|
+
def create_feed_message_using_raw_message_and_basic_extracted_patient_data
|
84
|
+
@feed_message = PersistMessage.new.call(hl7_message)
|
73
85
|
end
|
74
86
|
end
|
75
87
|
end
|
@@ -8,21 +8,27 @@ module Renalware
|
|
8
8
|
# hl7_message is an HL7Message (a decorator around ::HL7::Message)
|
9
9
|
# If the same message is persisted twice we'll get an ActiveRecord::RecordNotUnique error
|
10
10
|
# but that's fine as we don't want to process the same HL7 message twice.
|
11
|
+
# rubocop:disable Metrics/MethodLength
|
11
12
|
def call(hl7_message)
|
13
|
+
body_hash = Digest::MD5.hexdigest(hl7_message.to_hl7)
|
12
14
|
Message.create!(
|
13
15
|
event_code: hl7_message.type,
|
14
16
|
header_id: hl7_message.header_id,
|
15
17
|
body: hl7_message.to_s,
|
16
|
-
body_hash:
|
18
|
+
body_hash: body_hash,
|
17
19
|
patient_identifier: hl7_message.patient_identification&.internal_id
|
18
20
|
)
|
19
|
-
rescue ActiveRecord::RecordNotUnique
|
21
|
+
rescue ActiveRecord::RecordNotUnique
|
20
22
|
# If a duplicate messages comes in (we have calculated the body_hash for the message and it
|
21
23
|
# turns out that body_hash is not unique in the database, meaning the message is already
|
22
24
|
# stored) then raise a custom error so it can be handled upstream - ie we can choose to
|
23
25
|
# ignore it.
|
24
|
-
raise
|
26
|
+
raise(
|
27
|
+
DuplicateMessageError,
|
28
|
+
"header_id=#{hl7_message.header_id}, body_hash=#{body_hash}"
|
29
|
+
)
|
25
30
|
end
|
31
|
+
# rubocop:enable Metrics/MethodLength
|
26
32
|
end
|
27
33
|
end
|
28
34
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "renalware/hd"
|
4
|
+
|
5
|
+
module Renalware
|
6
|
+
module HD
|
7
|
+
module Scheduling
|
8
|
+
class ArchiveArguments
|
9
|
+
attr_reader :from_week_period, :to_week_period, :up_until_date
|
10
|
+
|
11
|
+
def initialize(from: nil, to: nil)
|
12
|
+
@from_week_period = WeekPeriod.from_date(from || 1.year.ago)
|
13
|
+
@up_until_date = (to || Time.zone.now - 1.day).to_date
|
14
|
+
@to_week_period = WeekPeriod.from_date(@up_until_date)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|