renalware-core 2.0.89 → 2.0.90
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/app/models/renalware/hd/profile_for_modality.rb +20 -0
- data/app/models/renalware/hd/profiles_in_date_range_query.rb +32 -0
- data/app/models/renalware/hd/revise_hd_profile.rb +3 -5
- data/app/models/renalware/modalities/description.rb +8 -0
- data/app/models/renalware/pd/regime_for_modality.rb +21 -0
- data/app/models/renalware/pd/regimes_in_date_range_query.rb +30 -0
- data/app/models/renalware/ukrdc/create_encrypted_patient_xml_files.rb +6 -1
- data/app/models/renalware/ukrdc/treatment.rb +5 -0
- data/app/models/renalware/ukrdc/treatment_timeline/generate_timeline.rb +13 -7
- data/app/models/renalware/ukrdc/treatment_timeline/generate_treatments.rb +9 -4
- data/app/models/renalware/ukrdc/treatment_timeline/generator_factory.rb +1 -1
- data/app/models/renalware/ukrdc/treatment_timeline/generic/generator.rb +45 -0
- data/app/models/renalware/ukrdc/treatment_timeline/hd/generator.rb +128 -0
- data/app/models/renalware/ukrdc/treatment_timeline/hd/modality_code_map.rb +29 -0
- data/app/models/renalware/ukrdc/treatment_timeline/hd/profile_decorator.rb +45 -0
- data/app/models/renalware/ukrdc/treatment_timeline/pd/generator.rb +119 -0
- data/app/models/renalware/ukrdc/treatment_timeline/pd/modality_code_map.rb +35 -0
- data/app/models/renalware/ukrdc/treatment_timeline/pd/regime_decorator.rb +37 -0
- data/app/models/renalware/ukrdc/treatment_timeline/prepare_tables.rb +1 -0
- data/app/presenters/renalware/hd/session_presenter.rb +6 -0
- data/app/presenters/renalware/ukrdc/patient_presenter.rb +17 -6
- data/app/views/renalware/api/ukrdc/patients/_medications.xml.builder +1 -8
- data/app/views/renalware/api/ukrdc/patients/_treatments.xml.builder +84 -45
- data/app/views/renalware/api/ukrdc/patients/procedures/_dialysis_session.xml.builder +1 -1
- data/app/views/renalware/api/ukrdc/patients/treatments/_generic.xml.builder +15 -0
- data/app/views/renalware/api/ukrdc/patients/treatments/_hd.xml.builder +30 -0
- data/app/views/renalware/api/ukrdc/patients/treatments/_pd.xml.builder +13 -0
- data/db/migrate/20190705083727_alter_ukrdc_treatments.rb +8 -0
- data/db/migrate/20190705105921_create_hd_profile_for_modalites.rb +7 -0
- data/db/migrate/20190709101610_create_pd_regime_for_modalities.rb +7 -0
- data/db/views/hd_profile_for_modalities_v01.sql +63 -0
- data/db/views/pd_regime_for_modalities_v01.sql +60 -0
- data/lib/renalware/version.rb +1 -1
- data/lib/tasks/ukrdc.rake +2 -1
- data/spec/factories/hd/profiles.rb +24 -0
- data/spec/factories/pd/pd_regimes.rb +8 -0
- data/spec/factories/ukrdc/modality_codes.rb +30 -0
- metadata +21 -9
- data/app/models/renalware/ukrdc/treatment_timeline/generators/deaths_timeline.rb +0 -17
- data/app/models/renalware/ukrdc/treatment_timeline/generators/generic_timeline.rb +0 -17
- data/app/models/renalware/ukrdc/treatment_timeline/generators/hd_timeline.rb +0 -145
- data/app/models/renalware/ukrdc/treatment_timeline/generators/low_clearance_timelinex.rb +0 -17
- data/app/models/renalware/ukrdc/treatment_timeline/generators/pd_timeline.rb +0 -18
- data/app/models/renalware/ukrdc/treatment_timeline/generators/transplants_donor_timeline.rb +0 -17
- data/app/models/renalware/ukrdc/treatment_timeline/generators/transplants_recipient_timeline.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a92055c00589d505aae488859a5a4e9071a8d49d658c1bd1dcb1d205ad10da1
|
4
|
+
data.tar.gz: 56b74f8c39ed127a5d958d6f08fc65941a11ea37c4cd62efce78714a9350230f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b8ddfbdb456d9a15e402755da4de84caf59c0f8fb1c00e71f55d784251d955ddb712e7d3c5af3c9d4e643fd49767c2a9511a6acccd21db77f83d2dd8c28c259
|
7
|
+
data.tar.gz: 57fbed3713860f482a2c18bcfebbba90d33a8cabad1595b754cf0445cd257755e3b369baba7a0f4c38dc4e3ecdafde9220719eb9ddf88a2639402788616812a2
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_dependency "renalware/hd"
|
4
|
+
|
5
|
+
module Renalware
|
6
|
+
module HD
|
7
|
+
# Backed by a (scenic) view this model is used to resolve the HD Profile in use or created
|
8
|
+
# just after a patient was assigned an HD modality,
|
9
|
+
# Note that this view could have been written as a SQL function or a ruby query object.
|
10
|
+
# I implemented it as a view because I would like semi technical people to be able to inspect
|
11
|
+
# the view to ascertain which HD modalities have no profile etc. Its just easier to review the
|
12
|
+
# data if it is a view.
|
13
|
+
#
|
14
|
+
# Example usage
|
15
|
+
# modality_profile = HD::ProfileForModality.where(modality_id: 1)
|
16
|
+
# modality_profile.hd_profile_id # => 123
|
17
|
+
class ProfileForModality < ApplicationRecord
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "attr_extras"
|
4
|
+
|
5
|
+
module Renalware
|
6
|
+
module HD
|
7
|
+
class ProfilesInDateRangeQuery
|
8
|
+
pattr_initialize [:patient!, :from!, :to!]
|
9
|
+
|
10
|
+
def call
|
11
|
+
scope = Renalware::HD::Profile
|
12
|
+
.with_deactivated
|
13
|
+
.order(created_at: :asc, deactivated_at: :desc)
|
14
|
+
|
15
|
+
scope
|
16
|
+
.where(conditions.merge(deactivated_at: from..to))
|
17
|
+
.or(
|
18
|
+
scope.where(conditions.merge(deactivated_at: nil))
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def conditions
|
25
|
+
{
|
26
|
+
patient_id: patient.id,
|
27
|
+
created_at: from..DateTime::Infinity.new
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
module Renalware
|
4
4
|
module HD
|
5
5
|
class ReviseHDProfile
|
6
|
+
attr_reader :profile, :new_profile
|
7
|
+
|
6
8
|
def initialize(profile)
|
7
9
|
raise(ArgumentError, "Cannot revise a new Profile") unless profile.persisted?
|
8
10
|
|
@@ -15,12 +17,8 @@ module Renalware
|
|
15
17
|
return false unless profile.valid?
|
16
18
|
|
17
19
|
profile.restore_attributes
|
18
|
-
profile.supersede!(params)
|
20
|
+
@new_profile = profile.supersede!(params)
|
19
21
|
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
attr_reader :profile
|
24
22
|
end
|
25
23
|
end
|
26
24
|
end
|
@@ -26,6 +26,14 @@ module Renalware
|
|
26
26
|
def augmented_name_for(_patient)
|
27
27
|
name
|
28
28
|
end
|
29
|
+
|
30
|
+
# For a ModalityDescription with type Renalware::HD::ModalityDescription
|
31
|
+
# this will return "HD"
|
32
|
+
def namespace
|
33
|
+
return if type.blank?
|
34
|
+
|
35
|
+
type.gsub("::", "").gsub(/^Renalware/, "").gsub(/ModalityDescription$/, "").underscore
|
36
|
+
end
|
29
37
|
end
|
30
38
|
end
|
31
39
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_dependency "renalware/pd"
|
4
|
+
|
5
|
+
module Renalware
|
6
|
+
module PD
|
7
|
+
# Backed by a (scenic) view this model is used to resolve the PD Regime in use or created
|
8
|
+
# just after a patient was assigned an PD modality,
|
9
|
+
# Note that this view could have been written as a SQL function or a ruby query object.
|
10
|
+
# I implemented it as a view because I would like semi-technical people to be able to inspect
|
11
|
+
# the view to ascertain which PD modalities have no regime. Its just easier to review the
|
12
|
+
# data if it is a view.
|
13
|
+
#
|
14
|
+
# Example usage
|
15
|
+
# modality_regime = PD::RegimeForModality.where(modality_id: 1)
|
16
|
+
# modality_regime.pd_regime_id # => 123
|
17
|
+
#
|
18
|
+
class RegimeForModality < ApplicationRecord
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "attr_extras"
|
4
|
+
|
5
|
+
module Renalware
|
6
|
+
module PD
|
7
|
+
class RegimesInDateRangeQuery
|
8
|
+
pattr_initialize [:patient!, :from!, :to!]
|
9
|
+
|
10
|
+
def call
|
11
|
+
scope = Renalware::PD::Regime.order(start_date: :asc, end_date: :desc)
|
12
|
+
|
13
|
+
scope
|
14
|
+
.where(conditions.merge(end_date: from..to))
|
15
|
+
.or(
|
16
|
+
scope.where(conditions.merge(end_date: nil))
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def conditions
|
23
|
+
{
|
24
|
+
patient_id: patient.id,
|
25
|
+
start_date: from..DateTime::Infinity.new
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -67,9 +67,11 @@ module Renalware
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def create_patient_xml_files
|
70
|
+
count = 0
|
70
71
|
patients = ukrdc_patients_who_have_changed_since_last_send
|
71
72
|
summary.num_changed_patients = patients.count
|
72
73
|
patients.find_each do |patient|
|
74
|
+
count += 1
|
73
75
|
CreatePatientXMLFile.new(
|
74
76
|
patient: patient,
|
75
77
|
dir: paths.timestamped_xml_folder,
|
@@ -79,6 +81,9 @@ module Renalware
|
|
79
81
|
logger: logger,
|
80
82
|
force_send: force_send
|
81
83
|
).call
|
84
|
+
|
85
|
+
# Every n patients, force the garbage collector to kick in
|
86
|
+
GC.start if (count % 10).zero?
|
82
87
|
end
|
83
88
|
end
|
84
89
|
|
@@ -129,7 +134,7 @@ module Renalware
|
|
129
134
|
end
|
130
135
|
|
131
136
|
def email_recipients
|
132
|
-
Array(ENV.fetch("DAILY_REPORT_EMAIL_RECIPIENTS", "
|
137
|
+
Array(ENV.fetch("DAILY_REPORT_EMAIL_RECIPIENTS", "tim@airslie.com").split(","))
|
133
138
|
end
|
134
139
|
|
135
140
|
def export_results
|
@@ -8,6 +8,11 @@ module Renalware
|
|
8
8
|
belongs_to :patient
|
9
9
|
belongs_to :clinician, class_name: "Renalware::User"
|
10
10
|
belongs_to :modality_code
|
11
|
+
belongs_to :modality, class_name: "Modalities::Modality"
|
12
|
+
belongs_to :modality_description, class_name: "Modalities::Description"
|
13
|
+
belongs_to :hospital_unit, class_name: "Hospitals::Unit"
|
14
|
+
belongs_to :hd_profile, class_name: "HD::Profile"
|
15
|
+
belongs_to :pd_regime, class_name: "PD::Regime"
|
11
16
|
validates :patient, presence: true
|
12
17
|
validates :modality_code, presence: true
|
13
18
|
|
@@ -14,19 +14,25 @@ module Renalware
|
|
14
14
|
pattr_initialize :patient
|
15
15
|
|
16
16
|
def call
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
17
|
+
# RemapModelTableNamesToTheirPreparedEquivalents.new.call do
|
18
|
+
Rails.logger.info " Generating Treatment rows for modalities #{modality_names}"
|
19
|
+
modalities.each do |modality|
|
20
|
+
generator = GeneratorFactory.call(modality)
|
21
|
+
generator.call
|
23
22
|
end
|
23
|
+
# end
|
24
24
|
end
|
25
25
|
|
26
26
|
private
|
27
27
|
|
28
28
|
def modalities
|
29
|
-
|
29
|
+
@modalities ||= begin
|
30
|
+
patient.modalities.includes(:description).order(started_on: :asc, updated_at: :asc)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def modality_names
|
35
|
+
modalities.map { |mod| mod.description.name }.join("->")
|
30
36
|
end
|
31
37
|
end
|
32
38
|
end
|
@@ -23,18 +23,23 @@ module Renalware
|
|
23
23
|
private
|
24
24
|
|
25
25
|
def patient_scope
|
26
|
-
Renalware::Patient
|
26
|
+
Renalware::Patient
|
27
|
+
.select(:id)
|
28
|
+
.where("send_to_renalreg = true or send_to_rpv = true")
|
27
29
|
end
|
28
30
|
|
29
|
-
# rubocop:disable Rails/Output
|
31
|
+
# rubocop:disable Rails/Output, Metrics/AbcSize
|
30
32
|
def generate_treatments
|
33
|
+
PrepareTables.call
|
31
34
|
Rails.logger.info "#{patient_scope.count} patients"
|
32
|
-
patient_scope.find_each do |patient|
|
35
|
+
patient_scope.find_each.with_index do |patient, index|
|
33
36
|
print "\n#{patient.id}: "
|
34
37
|
GenerateTimeline.new(patient).call
|
38
|
+
# Start gargbage collection periodically to prevent server ram issues.
|
39
|
+
GC.start if (index % 50).zero?
|
35
40
|
end
|
36
41
|
end
|
37
|
-
# rubocop:enable Rails/Output
|
42
|
+
# rubocop:enable Rails/Output, Metrics/AbcSize
|
38
43
|
|
39
44
|
def log(msg)
|
40
45
|
Rails.logger.info(msg)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_dependency "renalware/ukrdc"
|
4
|
+
|
5
|
+
module Renalware
|
6
|
+
module UKRDC
|
7
|
+
module TreatmentTimeline
|
8
|
+
module Generic
|
9
|
+
# Handles creating a treatment record for any modality that has not been handled in a more
|
10
|
+
# specific way. Note that we only create a Treatment if we find a ukrdc_modality_code_id
|
11
|
+
# in the modality's description row in the database. Some modalities will not have that
|
12
|
+
# id so we just ignore them.
|
13
|
+
class Generator
|
14
|
+
pattr_initialize :modality
|
15
|
+
|
16
|
+
def call
|
17
|
+
create_treatment if ukrdc_modality_code.present?
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def ukrdc_modality_code
|
23
|
+
@ukrdc_modality_code ||= begin
|
24
|
+
UKRDC::ModalityCode.find_by(
|
25
|
+
id: modality.description.ukrdc_modality_code_id
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_treatment
|
31
|
+
Treatment.create!(
|
32
|
+
patient: modality.patient,
|
33
|
+
clinician: modality.created_by,
|
34
|
+
started_on: modality.started_on,
|
35
|
+
modality_id: modality.id,
|
36
|
+
modality_description_id: modality.description_id,
|
37
|
+
ended_on: modality.ended_on,
|
38
|
+
modality_code: ukrdc_modality_code
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_dependency "renalware/ukrdc"
|
4
|
+
require "attr_extras"
|
5
|
+
|
6
|
+
module Renalware
|
7
|
+
module UKRDC
|
8
|
+
module TreatmentTimeline
|
9
|
+
module HD
|
10
|
+
# Generates a set of treatments based on an HD modality and any HD::Profiles
|
11
|
+
# during the span of that modality.
|
12
|
+
#
|
13
|
+
# There will be an initial treatment triggered by the modality itself, and then
|
14
|
+
# a treatment for each significant change in the HD::Profile during the period of the
|
15
|
+
# modality (ie until it ends).
|
16
|
+
#
|
17
|
+
class Generator
|
18
|
+
pattr_initialize :modality
|
19
|
+
delegate :patient, to: :modality
|
20
|
+
|
21
|
+
def call
|
22
|
+
create_treatment_from_modality
|
23
|
+
create_treatments_within_modality
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def create_treatment_from_modality
|
29
|
+
create_treatment(
|
30
|
+
hd_profile_at_start_of_modality,
|
31
|
+
modality.started_on,
|
32
|
+
modality.ended_on
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
37
|
+
def create_treatment(profile, start_date, end_date)
|
38
|
+
treatments << Treatment.create!(
|
39
|
+
patient: patient,
|
40
|
+
clinician: modality.created_by,
|
41
|
+
started_on: start_date,
|
42
|
+
modality_id: modality.id,
|
43
|
+
modality_description_id: modality.description_id,
|
44
|
+
hospital_unit: profile&.hospital_unit,
|
45
|
+
ended_on: end_date,
|
46
|
+
modality_code: ukrdc_modality_code_from_profile(profile),
|
47
|
+
hd_profile: profile
|
48
|
+
)
|
49
|
+
|
50
|
+
# Update the end date on the previous treatment - ie the one we just added is
|
51
|
+
# taking over as the currently active treatment
|
52
|
+
unless treatments.length <= 1
|
53
|
+
previous_treatment = treatments[treatments.length - 2]
|
54
|
+
previous_treatment.update!(ended_on: start_date)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
58
|
+
|
59
|
+
# Find the modality that was active on the day of the modality change
|
60
|
+
# The profile might have been added up to say 14 days later however so if there is none
|
61
|
+
# active on the day the modality was created, search up to 14 days ahead until we find
|
62
|
+
# one. Return nil if none found.
|
63
|
+
# Its complicated a bit by the fact that although there is a prescribed_on in the
|
64
|
+
# hd_profile, it is sometimes missing so we need to default to created_at in that
|
65
|
+
# instance.
|
66
|
+
def hd_profile_at_start_of_modality
|
67
|
+
@hd_profile_at_start_of_modality ||= begin
|
68
|
+
hd_profile_id = Renalware::HD::ProfileForModality.find_by!(
|
69
|
+
modality_id: modality.id
|
70
|
+
).hd_profile_id
|
71
|
+
return if hd_profile_id.blank?
|
72
|
+
|
73
|
+
HD::ProfileDecorator.new(
|
74
|
+
Renalware::HD::Profile.with_deactivated.find(hd_profile_id)
|
75
|
+
)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# 3 things trigger a new Treatment for an HD patient
|
80
|
+
# - change of site
|
81
|
+
# - change of hd_type to from hd and (hdf_pre || hdf_post)
|
82
|
+
# - change of hd prescription
|
83
|
+
# Loop through the hd_profiles and trigger an new treatment when these change
|
84
|
+
# There is a problem here as we are creating duplicate treatments
|
85
|
+
# I think we need to first find the hd profile that is associated with the hd modality
|
86
|
+
# and that becomes the 'last_profile' here
|
87
|
+
def create_treatments_within_modality
|
88
|
+
last_profile = hd_profile_at_start_of_modality
|
89
|
+
|
90
|
+
hd_profiles.each do |profile_|
|
91
|
+
profile = HD::ProfileDecorator.new(profile_, last_profile: last_profile)
|
92
|
+
create_treatment_from(profile) if last_profile.nil? || profile.changed?
|
93
|
+
last_profile = profile
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def hd_profiles
|
98
|
+
profiles = Renalware::HD::ProfilesInDateRangeQuery.new(
|
99
|
+
patient: patient,
|
100
|
+
from: modality.started_on,
|
101
|
+
to: modality.ended_on
|
102
|
+
).call
|
103
|
+
profiles - Array(hd_profile_at_start_of_modality)
|
104
|
+
end
|
105
|
+
|
106
|
+
def create_treatment_from(profile)
|
107
|
+
start_date = profile.present? ? profile.created_at : modality.started_on
|
108
|
+
end_date = profile.present? ? profile.deactivated_at : modality.ended_on
|
109
|
+
|
110
|
+
create_treatment(profile, start_date, end_date)
|
111
|
+
end
|
112
|
+
|
113
|
+
def treatments
|
114
|
+
@treatments ||= []
|
115
|
+
end
|
116
|
+
|
117
|
+
def hd_patient
|
118
|
+
Renalware::HD.cast_patient(patient)
|
119
|
+
end
|
120
|
+
|
121
|
+
def ukrdc_modality_code_from_profile(profile)
|
122
|
+
ModalityCodeMap.new.code_for_profile(profile)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|