renalware-core 2.0.113 → 2.0.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "renalware/hd"
|
|
4
|
+
|
|
5
|
+
# A Diary is the schedule of HD Station/Patients assignments for one week for a particular unit.
|
|
6
|
+
# A Diary is split into Periods (eg. am pm eve), and each Period has a matrix of slots
|
|
7
|
+
# Imagine Days eg 1 to 6 on the X axis and Stations on the Y axis, and into each 'slot' we
|
|
8
|
+
# put a patient.
|
|
9
|
+
# A patient can only be in one slot in any period.
|
|
10
|
+
#
|
|
11
|
+
# Do not create instances of this class explicity, always use the STI sub classes eg MasterDiary.
|
|
12
|
+
module Renalware
|
|
13
|
+
module HD
|
|
14
|
+
module Scheduling
|
|
15
|
+
class Diary < ApplicationRecord
|
|
16
|
+
include Accountable
|
|
17
|
+
self.table_name = :hd_diaries
|
|
18
|
+
has_many :slots, class_name: "DiarySlot", dependent: :restrict_with_exception
|
|
19
|
+
belongs_to :hospital_unit, class_name: "Hospitals::Unit"
|
|
20
|
+
has_many :patients,
|
|
21
|
+
through: :slots, class_name: "Renalware::HD::Patient",
|
|
22
|
+
dependent: :restrict_with_exception
|
|
23
|
+
validates :hospital_unit_id, presence: true
|
|
24
|
+
validates :master, inclusion: { in: [true, false], allow_nil: true }
|
|
25
|
+
composed_of :week,
|
|
26
|
+
mapping: [%w(week_number week_number), %w(year year)],
|
|
27
|
+
constructor: lambda { |week_number, year|
|
|
28
|
+
WeekPeriod.new(week_number: week_number, year: year)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
def self.policy_class
|
|
32
|
+
DiaryPolicy
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Searchs the object graph rather than a SQL search
|
|
36
|
+
def slot_for(diurnal_period_code_id, station_id, day_of_week, valid_from: nil)
|
|
37
|
+
slots.find do |slot|
|
|
38
|
+
found = slot.diurnal_period_code_id == diurnal_period_code_id &&
|
|
39
|
+
slot.station_id == station_id &&
|
|
40
|
+
slot.day_of_week == day_of_week
|
|
41
|
+
|
|
42
|
+
# This applies to master diaries only really, where we only
|
|
43
|
+
# show recurring slots if they were created before the timeframe
|
|
44
|
+
# of the current week being viewed.
|
|
45
|
+
if found && valid_from
|
|
46
|
+
found = (slot.created_at <= valid_from.end_of_week)
|
|
47
|
+
end
|
|
48
|
+
found
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "renalware/hd"
|
|
4
|
+
|
|
5
|
+
# A cron job creates this delayed job, which does the following:
|
|
6
|
+
# For all Weekly Diaries in the past, plus this week
|
|
7
|
+
# for each day that is in the past
|
|
8
|
+
# mark any slots in the weekly diary as archived
|
|
9
|
+
# and where the weekly diary is inheriting slots from the master diary
|
|
10
|
+
# create corresponding slots in the weekly diary (ie migrate those slots from master to weekly)
|
|
11
|
+
# and archive them.
|
|
12
|
+
# No slots before the current day should be touched
|
|
13
|
+
module Renalware
|
|
14
|
+
module HD
|
|
15
|
+
module Scheduling
|
|
16
|
+
DiaryHousekeepingJob = Struct.new(:up_until) do
|
|
17
|
+
def max_attempts
|
|
18
|
+
1
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def queue_name
|
|
22
|
+
"hd_diary_houskeeping"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def priority
|
|
26
|
+
5 # medium
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def destroy_failed_jobs?
|
|
30
|
+
true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# :reek:UtilityFunction
|
|
34
|
+
def perform
|
|
35
|
+
up_until ||= Time.zone.today - 1.day
|
|
36
|
+
up_until = up_until.to_date
|
|
37
|
+
create_missing_weekly_diaries
|
|
38
|
+
move_elapsed_master_slots_into_weekly_slot_equivalent
|
|
39
|
+
archive_old_weekly_slots(up_until)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
# Just inc case there are some missing weekly diaries
|
|
45
|
+
def create_missing_weekly_diaries
|
|
46
|
+
args = ArchiveArguments.new
|
|
47
|
+
Hospitals::Unit.hd_sites.each do |unit|
|
|
48
|
+
DiaryRange.new(
|
|
49
|
+
from_week_period: args.from_week_period,
|
|
50
|
+
to_week_period: args.to_week_period,
|
|
51
|
+
unit: unit
|
|
52
|
+
).create_missing_weekly_diaries(by: User.first)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Copy any master slots that are now in the past (as of midnight Sunday just gone) and
|
|
57
|
+
# which do not already have a weekly slot overriding the master, into weekly slots.
|
|
58
|
+
# This is to stop changes to the master diary affecting diary history.
|
|
59
|
+
# This executes a SQL view to do the dirty work. It references a SQL view called
|
|
60
|
+
# hd_diary_matrix to help it to decide which master slots to copy to the weekly diary.
|
|
61
|
+
# That view could be useful for other things. We could in fact build the diary UI from it.
|
|
62
|
+
def move_elapsed_master_slots_into_weekly_slot_equivalent
|
|
63
|
+
Rails.logger.debug("DEBUG: SELECT renalware.hd_diary_archive_elapsed_master_slots()")
|
|
64
|
+
conn = ActiveRecord::Base.connection
|
|
65
|
+
conn.execute("SELECT renalware.hd_diary_archive_elapsed_master_slots()")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Flag elapsed slots as archived. Principally this will help us to stop them from being
|
|
69
|
+
# changed.
|
|
70
|
+
def archive_old_weekly_slots(up_until)
|
|
71
|
+
slots = DiarySlot
|
|
72
|
+
.unarchived
|
|
73
|
+
.joins(:diary)
|
|
74
|
+
.where(hd_diaries: { year: up_until.year, week_number: up_until.cweek })
|
|
75
|
+
.where(day_of_week: up_until.cwday)
|
|
76
|
+
|
|
77
|
+
ids = slots.pluck(:id)
|
|
78
|
+
Rails.logger.info("Archiving #{ids.length} slots up_until #{up_until} with ids #{ids}")
|
|
79
|
+
slots.update_all(archived: true, archived_at: Time.zone.now)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "renalware/hd"
|
|
4
|
+
require "renalware/week_period"
|
|
5
|
+
require "attr_extras"
|
|
6
|
+
|
|
7
|
+
module Renalware
|
|
8
|
+
module HD
|
|
9
|
+
module Scheduling
|
|
10
|
+
# Represents a range of WeekPeriods and their diaries for a hospital unit
|
|
11
|
+
class DiaryRange
|
|
12
|
+
pattr_initialize [:from_week_period!, :to_week_period!, :unit!]
|
|
13
|
+
|
|
14
|
+
# If there are any missing weekly diaries in the date range, create them
|
|
15
|
+
def create_missing_weekly_diaries(by:)
|
|
16
|
+
week_periods_in_range.each do |period|
|
|
17
|
+
WeeklyDiary.find_or_create_by!(
|
|
18
|
+
hospital_unit_id: unit.id,
|
|
19
|
+
week_number: period.week_number,
|
|
20
|
+
year: period.year
|
|
21
|
+
) do |diary|
|
|
22
|
+
log_weekly_diary_creation(period)
|
|
23
|
+
diary.master_diary = master_diary_for(unit, by)
|
|
24
|
+
diary.by = by
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def log_weekly_diary_creation(period)
|
|
30
|
+
Rails.logger.debug(
|
|
31
|
+
"DEBUG: Creating weekly diary for unit #{unit.id} period "\
|
|
32
|
+
"#{period.week_number}/#{period.year}"
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def from_date
|
|
37
|
+
from_week_period.date_on_first_day_of_week
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def to_date
|
|
41
|
+
to_week_period.date_on_first_day_of_week
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# Returns an array of WeekPeriods for each cwyear and cweek in the date range.
|
|
47
|
+
def week_periods_in_range
|
|
48
|
+
@week_periods_in_range ||= begin
|
|
49
|
+
# The array in the uniq block here serves to filter the date range. It filters out
|
|
50
|
+
# just the dates representing the first day of each unique week/year.
|
|
51
|
+
mondays_in_date_range = (from_date..to_date).uniq { |dt| [dt.cwyear, dt.cweek] }
|
|
52
|
+
# Now map the dates (one per week) into WeekPeriods
|
|
53
|
+
mondays_in_date_range.map do |date|
|
|
54
|
+
WeekPeriod.new(week_number: date.cweek, year: date.cwyear)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def master_diary_for(unit, by)
|
|
60
|
+
MasterDiary.find_or_create_by!(hospital_unit_id: unit.id) do |diary|
|
|
61
|
+
Rails.logger.debug "DEBUG: Creating master diary for unit #{unit.id}"
|
|
62
|
+
diary.master = true
|
|
63
|
+
diary.by = by
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "renalware/hd"
|
|
4
|
+
|
|
5
|
+
module Renalware
|
|
6
|
+
module HD
|
|
7
|
+
module Scheduling
|
|
8
|
+
class DiarySlot < ApplicationRecord
|
|
9
|
+
self.table_name = :hd_diary_slots
|
|
10
|
+
# Changing to_ary from private to public here is a hack required to remove a SimpleDelgator
|
|
11
|
+
# warning in eg Renalware::HD::Scheduling::WeeklyDiary::WeeklySlotDecorator:
|
|
12
|
+
#
|
|
13
|
+
# `respond_to?': delegator does not forward private method #to_ary
|
|
14
|
+
#
|
|
15
|
+
# Yielding a slot from DiaryPresenter to the view seems to want to call #to_ary on it, (have
|
|
16
|
+
# not looked into why) and as the slot is wrapped in a decorator using SimpleDelegator,
|
|
17
|
+
# SimpleDelegatr complains it can't delegate to a private method.
|
|
18
|
+
# An alternative is to define the following in each Slot decorator
|
|
19
|
+
#
|
|
20
|
+
# def respond_to?(method, include_private = false)
|
|
21
|
+
# return false if method == :to_ary
|
|
22
|
+
# super
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# or to use method_missnig instead of SimpleDelegator in Slot Decorators.
|
|
26
|
+
#
|
|
27
|
+
public :to_ary
|
|
28
|
+
|
|
29
|
+
# Virtual attribute used on a form to determine what action as preformed on the slot
|
|
30
|
+
attr_accessor :change_type
|
|
31
|
+
# Virtual to help the prepopulation of select2 with the offending patient if an error
|
|
32
|
+
attr_accessor :patient_ids
|
|
33
|
+
|
|
34
|
+
include Accountable
|
|
35
|
+
belongs_to :diary, class_name: "Renalware::HD::Scheduling::Diary", touch: true
|
|
36
|
+
belongs_to :patient, touch: true
|
|
37
|
+
belongs_to :station, class_name: "Renalware::HD::Station"
|
|
38
|
+
belongs_to :diurnal_period_code
|
|
39
|
+
|
|
40
|
+
validates :diary, presence: true
|
|
41
|
+
validates :patient, presence: true
|
|
42
|
+
validates :station, presence: true
|
|
43
|
+
validates :diurnal_period_code, presence: true
|
|
44
|
+
validates :day_of_week, presence: true, inclusion: { in: 1..7 }
|
|
45
|
+
|
|
46
|
+
scope :weekly, -> { joins(:diary).where(hd_diaries: { master: false }) }
|
|
47
|
+
scope :archived, -> { weekly.where(archived: true) }
|
|
48
|
+
scope :unarchived, -> { weekly.where(archived: false) }
|
|
49
|
+
|
|
50
|
+
# A patient can only be assigned to one station in any period (e.g. am)/day combination
|
|
51
|
+
# for a diary. I.e. they can't be on two stations on Monday morning.
|
|
52
|
+
validates :patient_id,
|
|
53
|
+
uniqueness: { scope: [:diary, :diurnal_period_code, :day_of_week] }
|
|
54
|
+
|
|
55
|
+
# Scoped to a diary, the combination of day + station + diurnal_period is unique
|
|
56
|
+
validates :diurnal_period_code_id,
|
|
57
|
+
uniqueness: { scope: [:diary, :station_id, :day_of_week] }
|
|
58
|
+
|
|
59
|
+
delegate :id, to: :diary, prefix: true
|
|
60
|
+
|
|
61
|
+
def self.policy_class
|
|
62
|
+
DiaryPolicy
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def on_master_diary?
|
|
66
|
+
diary&.master?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def description
|
|
70
|
+
period = diurnal_period_code.code.upcase # e.g. AM
|
|
71
|
+
if diary.master
|
|
72
|
+
"Recurring every #{day_of_week_name} #{period}"
|
|
73
|
+
else
|
|
74
|
+
"This week only on #{day_of_week_name} #{period}"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def day_of_week_name
|
|
79
|
+
Time::DAYS_INTO_WEEK.keys[day_of_week - 1].capitalize
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def cell_id
|
|
83
|
+
"#{diurnal_period_code&.id}-#{station&.id}-#{day_of_week}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "renalware/hd"
|
|
4
|
+
require "renalware/week_period"
|
|
5
|
+
##
|
|
6
|
+
# Returns a hospital unit's diary for the requested week (passed to #new as a WeekPeriod
|
|
7
|
+
# value object).
|
|
8
|
+
#
|
|
9
|
+
module Renalware
|
|
10
|
+
module HD
|
|
11
|
+
module Scheduling
|
|
12
|
+
class FindOrCreateDiaryByWeekQuery
|
|
13
|
+
attr_reader :unit_id, :period, :by, :relation
|
|
14
|
+
|
|
15
|
+
def initialize(unit_id:, week_period:, by:, relation: WeeklyDiary.all)
|
|
16
|
+
@relation = relation
|
|
17
|
+
@period = week_period
|
|
18
|
+
@unit_id = unit_id
|
|
19
|
+
@by = by
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call
|
|
23
|
+
find_or_create_diary
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
# Find the diary for the current unit/week/year, or, if for example if a user wants to fill
|
|
29
|
+
# next week's diary and it does not yet exist, create it
|
|
30
|
+
# NB we _always_ return a diary from this method - whether it is found or built just-in-time
|
|
31
|
+
def find_or_create_diary
|
|
32
|
+
attrs = {
|
|
33
|
+
hospital_unit_id: unit_id,
|
|
34
|
+
week_number: period.week_number,
|
|
35
|
+
year: period.year,
|
|
36
|
+
master: false
|
|
37
|
+
}
|
|
38
|
+
relation.find_by(**attrs) || build_diary(**attrs)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Create a new Weekly diary for the current week/year/unit
|
|
42
|
+
# Add in DiaryPeriods for each currently defined diurnal period eg am pm eve
|
|
43
|
+
def build_diary(attrs)
|
|
44
|
+
master_diary = FindOrCreateMasterDiary.for_unit(unit_id, by)
|
|
45
|
+
diary = WeeklyDiary.create!(**attrs, by: by, master_diary: master_diary)
|
|
46
|
+
# Reload the diary using the supplied relation (might be egager_loads etc)
|
|
47
|
+
relation.find(diary.id)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Renalware
|
|
4
|
+
module HD
|
|
5
|
+
module Scheduling
|
|
6
|
+
class FindOrCreateMasterDiary
|
|
7
|
+
attr_reader :unit_id, :user
|
|
8
|
+
|
|
9
|
+
def self.for_unit(unit_id, user)
|
|
10
|
+
new(unit_id, user).call
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(unit_id, user)
|
|
14
|
+
@unit_id = unit_id
|
|
15
|
+
@user = user
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call
|
|
19
|
+
MasterDiary.find_or_initialize_by(hospital_unit_id: unit_id).tap do |master|
|
|
20
|
+
master.by = user
|
|
21
|
+
master.save!
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "renalware/hd"
|
|
4
|
+
|
|
5
|
+
module Renalware
|
|
6
|
+
module HD
|
|
7
|
+
module Scheduling
|
|
8
|
+
class MasterDiary < Diary
|
|
9
|
+
# Overwrite the existing master attribute to ensure it defaults to true
|
|
10
|
+
attribute :master, :boolean, default: true
|
|
11
|
+
validates :hospital_unit_id, uniqueness: true
|
|
12
|
+
has_many :weekly_diaries, class_name: "WeeklyDiary"
|
|
13
|
+
# While our DB constraints could check for the string
|
|
14
|
+
# "Renalware::HD::Scheduling::MasterDiary" in the type column, this feels a bit fragile, so
|
|
15
|
+
# instead a MasterDiary must have a corresponding
|
|
16
|
+
# master = TRUE column
|
|
17
|
+
validates :master, inclusion: { in: [true], allow_nil: false }
|
|
18
|
+
|
|
19
|
+
class MasterSlotDecorator < SimpleDelegator
|
|
20
|
+
def master?
|
|
21
|
+
true
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def decorate_slot(slot)
|
|
26
|
+
MasterSlotDecorator.new(slot)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def slot_for(*args)
|
|
30
|
+
(slot = super) && decorate_slot(slot)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "renalware/hd"
|
|
4
|
+
|
|
5
|
+
module Renalware
|
|
6
|
+
module HD
|
|
7
|
+
module Scheduling
|
|
8
|
+
class WeeklyDiary < Diary
|
|
9
|
+
# Overwrite the existing master attribute to ensure it defaults to false
|
|
10
|
+
attribute :master, :boolean, default: false
|
|
11
|
+
belongs_to :master_diary, class_name: "Renalware::HD::Scheduling::MasterDiary"
|
|
12
|
+
validates :week_number,
|
|
13
|
+
presence: true,
|
|
14
|
+
uniqueness: { scope: [:year, :hospital_unit_id] },
|
|
15
|
+
inclusion: { in: 1..53 }
|
|
16
|
+
validates :year, presence: true
|
|
17
|
+
validates :master, inclusion: { in: [false], allow_nil: false }
|
|
18
|
+
validates :master_diary, presence: true
|
|
19
|
+
delegate :to_s, to: :week
|
|
20
|
+
scope :ordered, -> { order(year: :desc, week_number: :desc) }
|
|
21
|
+
|
|
22
|
+
def applies_to_current_week?
|
|
23
|
+
today = Time.zone.today
|
|
24
|
+
[today.cweek, today.year] == [week.week_number, week.year]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def archived?
|
|
28
|
+
false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class WeeklySlotDecorator < SimpleDelegator
|
|
32
|
+
def master?
|
|
33
|
+
false
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def decorate_slot(slot)
|
|
38
|
+
WeeklySlotDecorator.new(slot)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def slot_for(*args)
|
|
42
|
+
(slot = super) && decorate_slot(slot)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
require_dependency "renalware/pathology"
|
|
4
4
|
|
|
5
5
|
#
|
|
6
|
-
# When subscribed to HL7 `
|
|
6
|
+
# When subscribed to HL7 `oru_message_arrived` messages, gets notified of incoming HL7 messages
|
|
7
7
|
# and creates the observations contained therein provided the patient exists.
|
|
8
8
|
#
|
|
9
9
|
module Renalware
|
|
10
10
|
module Pathology
|
|
11
11
|
class MessageListener
|
|
12
12
|
# Note: We are already inside a transaction here
|
|
13
|
-
def
|
|
13
|
+
def oru_message_arrived(hl7_message:, **)
|
|
14
14
|
pathology_params = parse_pathology_params(hl7_message)
|
|
15
15
|
create_observation_requests_and_their_child_observations_from(pathology_params)
|
|
16
16
|
#
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "renalware/pathology/requests"
|
|
4
|
+
|
|
5
|
+
module Renalware
|
|
6
|
+
module Pathology
|
|
7
|
+
module Requests
|
|
8
|
+
class GlobalRule
|
|
9
|
+
class LatestCRFOlderThanWeeks < GlobalRule
|
|
10
|
+
validates :param_comparison_value, presence: true
|
|
11
|
+
|
|
12
|
+
# Returns true if the patient has a transplant registration with a latest CRF date
|
|
13
|
+
# older than <param_comparison_value> weeks ago.
|
|
14
|
+
def observation_required_for_patient?(patient, date)
|
|
15
|
+
registration = registration_for(patient)
|
|
16
|
+
return false unless registration
|
|
17
|
+
|
|
18
|
+
latest_crf_date = registration.document.crf.latest.recorded_on
|
|
19
|
+
return false if latest_crf_date.blank?
|
|
20
|
+
|
|
21
|
+
max_crf_date = date - param_comparison_value.to_i.weeks
|
|
22
|
+
latest_crf_date < max_crf_date
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_s
|
|
26
|
+
"latest CRF older than #{param_comparison_value} weeks"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def registration_for(patient)
|
|
32
|
+
Transplants::Registration.for_patient(patient).first
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -58,7 +58,7 @@ module Renalware
|
|
|
58
58
|
has_many :exit_site_infections, class_name: "PD::ExitSiteInfection"
|
|
59
59
|
has_many :peritonitis_episodes, class_name: "PD::PeritonitisEpisode"
|
|
60
60
|
has_many :pd_regimes, class_name: "PD::Regime"
|
|
61
|
-
has_many :problems, class_name: "Problems::Problem"
|
|
61
|
+
has_many :problems, -> { ordered }, class_name: "Problems::Problem"
|
|
62
62
|
has_many :prescriptions, class_name: "Medications::Prescription"
|
|
63
63
|
has_many :drugs, through: :prescriptions
|
|
64
64
|
has_many :medication_routes, through: :prescriptions, class_name: "Medications::MedicationRoute"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "renalware/patients"
|
|
4
|
+
|
|
5
|
+
module Renalware
|
|
6
|
+
module Patients
|
|
7
|
+
class Abridgement < ApplicationRecord
|
|
8
|
+
self.table_name = "patient_master_index"
|
|
9
|
+
validates :hospital_number, presence: true
|
|
10
|
+
validates :given_name, presence: true
|
|
11
|
+
validates :family_name, presence: true
|
|
12
|
+
|
|
13
|
+
def self.policy_class
|
|
14
|
+
::Renalware::BasePolicy
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "renalware/patients"
|
|
4
|
+
|
|
5
|
+
module Renalware
|
|
6
|
+
module Patients
|
|
7
|
+
module Ingestion
|
|
8
|
+
class Command
|
|
9
|
+
attr_reader :message
|
|
10
|
+
|
|
11
|
+
def initialize(message)
|
|
12
|
+
@message = message
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.for(message)
|
|
16
|
+
CommandFactory.new.for(message)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def call
|
|
20
|
+
raise NotImplementedError
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|