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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/app/models/renalware/hd/profile_for_modality.rb +20 -0
  3. data/app/models/renalware/hd/profiles_in_date_range_query.rb +32 -0
  4. data/app/models/renalware/hd/revise_hd_profile.rb +3 -5
  5. data/app/models/renalware/modalities/description.rb +8 -0
  6. data/app/models/renalware/pd/regime_for_modality.rb +21 -0
  7. data/app/models/renalware/pd/regimes_in_date_range_query.rb +30 -0
  8. data/app/models/renalware/ukrdc/create_encrypted_patient_xml_files.rb +6 -1
  9. data/app/models/renalware/ukrdc/treatment.rb +5 -0
  10. data/app/models/renalware/ukrdc/treatment_timeline/generate_timeline.rb +13 -7
  11. data/app/models/renalware/ukrdc/treatment_timeline/generate_treatments.rb +9 -4
  12. data/app/models/renalware/ukrdc/treatment_timeline/generator_factory.rb +1 -1
  13. data/app/models/renalware/ukrdc/treatment_timeline/generic/generator.rb +45 -0
  14. data/app/models/renalware/ukrdc/treatment_timeline/hd/generator.rb +128 -0
  15. data/app/models/renalware/ukrdc/treatment_timeline/hd/modality_code_map.rb +29 -0
  16. data/app/models/renalware/ukrdc/treatment_timeline/hd/profile_decorator.rb +45 -0
  17. data/app/models/renalware/ukrdc/treatment_timeline/pd/generator.rb +119 -0
  18. data/app/models/renalware/ukrdc/treatment_timeline/pd/modality_code_map.rb +35 -0
  19. data/app/models/renalware/ukrdc/treatment_timeline/pd/regime_decorator.rb +37 -0
  20. data/app/models/renalware/ukrdc/treatment_timeline/prepare_tables.rb +1 -0
  21. data/app/presenters/renalware/hd/session_presenter.rb +6 -0
  22. data/app/presenters/renalware/ukrdc/patient_presenter.rb +17 -6
  23. data/app/views/renalware/api/ukrdc/patients/_medications.xml.builder +1 -8
  24. data/app/views/renalware/api/ukrdc/patients/_treatments.xml.builder +84 -45
  25. data/app/views/renalware/api/ukrdc/patients/procedures/_dialysis_session.xml.builder +1 -1
  26. data/app/views/renalware/api/ukrdc/patients/treatments/_generic.xml.builder +15 -0
  27. data/app/views/renalware/api/ukrdc/patients/treatments/_hd.xml.builder +30 -0
  28. data/app/views/renalware/api/ukrdc/patients/treatments/_pd.xml.builder +13 -0
  29. data/db/migrate/20190705083727_alter_ukrdc_treatments.rb +8 -0
  30. data/db/migrate/20190705105921_create_hd_profile_for_modalites.rb +7 -0
  31. data/db/migrate/20190709101610_create_pd_regime_for_modalities.rb +7 -0
  32. data/db/views/hd_profile_for_modalities_v01.sql +63 -0
  33. data/db/views/pd_regime_for_modalities_v01.sql +60 -0
  34. data/lib/renalware/version.rb +1 -1
  35. data/lib/tasks/ukrdc.rake +2 -1
  36. data/spec/factories/hd/profiles.rb +24 -0
  37. data/spec/factories/pd/pd_regimes.rb +8 -0
  38. data/spec/factories/ukrdc/modality_codes.rb +30 -0
  39. metadata +21 -9
  40. data/app/models/renalware/ukrdc/treatment_timeline/generators/deaths_timeline.rb +0 -17
  41. data/app/models/renalware/ukrdc/treatment_timeline/generators/generic_timeline.rb +0 -17
  42. data/app/models/renalware/ukrdc/treatment_timeline/generators/hd_timeline.rb +0 -145
  43. data/app/models/renalware/ukrdc/treatment_timeline/generators/low_clearance_timelinex.rb +0 -17
  44. data/app/models/renalware/ukrdc/treatment_timeline/generators/pd_timeline.rb +0 -18
  45. data/app/models/renalware/ukrdc/treatment_timeline/generators/transplants_donor_timeline.rb +0 -17
  46. data/app/models/renalware/ukrdc/treatment_timeline/generators/transplants_recipient_timeline.rb +0 -17
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/ukrdc"
4
+
5
+ module Renalware
6
+ module UKRDC
7
+ module TreatmentTimeline
8
+ module HD
9
+ class ModalityCodeMap
10
+ def code_for_profile(profile)
11
+ hd_type = profile&.hd_type
12
+ return default_code if hd_type.blank?
13
+
14
+ ukrr_name = case hd_type.to_s.downcase
15
+ when "hd" then "Haemodialysis"
16
+ when "hdf_pre", "hdf_post" then "Haemodiafiltration"
17
+ end
18
+
19
+ ModalityCode.find_by!(description: ukrr_name)
20
+ end
21
+
22
+ def default_code
23
+ ModalityCode.find_by!(description: "Haemodialysis")
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/ukrdc"
4
+ module Renalware
5
+ module UKRDC
6
+ module TreatmentTimeline
7
+ module HD
8
+ # Decorates an HD::Profile, adding methods that detect any changes significant enough
9
+ # to warrant generating a new UKRDC Treatment.
10
+ class ProfileDecorator < DumbDelegator
11
+ def initialize(profile, last_profile: nil)
12
+ @last_profile = last_profile
13
+ super(profile)
14
+ end
15
+
16
+ def hd_type
17
+ document.dialysis.hd_type
18
+ end
19
+
20
+ def changed?
21
+ return true if last_profile.blank?
22
+
23
+ hd_type_changed? || hospital_unit_changed?
24
+ end
25
+
26
+ def unchanged?
27
+ !changed?
28
+ end
29
+
30
+ def hd_type_changed?
31
+ last_profile.document.dialysis.hd_type != hd_type
32
+ end
33
+
34
+ def hospital_unit_changed?
35
+ last_profile.hospital_unit_id != hospital_unit_id
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :last_profile
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,119 @@
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 PD
10
+ # Generates a set of treatments based on a PD modality.
11
+ #
12
+ # There will be an initial treatment triggered by the modality itself, and then
13
+ # a treatment for each significant change in the pd regime during the period of the
14
+ # modality (until it ends)
15
+ #
16
+ class Generator
17
+ pattr_initialize :modality
18
+ delegate :patient, to: :modality
19
+
20
+ def call
21
+ create_treatment_from_modality
22
+ create_treatments_within_modality
23
+ end
24
+
25
+ private
26
+
27
+ def create_treatment_from_modality
28
+ create_treatment(
29
+ pd_regime_at_start_of_modality,
30
+ modality.started_on,
31
+ modality.ended_on
32
+ )
33
+ end
34
+
35
+ # Find the modality that was active on the day of the modality change
36
+ # The profile might have been added up to say 14 days later however so if there is none
37
+ # active on the day the modality was created, search up to 14 days ahead until we find
38
+ # one. Return nil if none found.
39
+ def pd_regime_at_start_of_modality
40
+ @pd_regime_at_start_of_modality ||= begin
41
+ pd_regime_id = Renalware::PD::RegimeForModality.find_by!(
42
+ modality_id: modality.id
43
+ )&.pd_regime_id
44
+ return if pd_regime_id.blank?
45
+
46
+ Renalware::PD::Regime.find(pd_regime_id)
47
+ end
48
+ end
49
+
50
+ # Things that trigger a new Treatment for an PD patient
51
+ # - change of regime type eg from APD to CAPD
52
+ # - ?
53
+ # Loop through the patient's regimes and trigger a new treatment when these change
54
+ def create_treatments_within_modality
55
+ last_regime = pd_regime_at_start_of_modality
56
+
57
+ pd_regimes.each do |regime_|
58
+ regime = PD::RegimeDecorator.new(regime_, last_regime: last_regime)
59
+ create_treatment_from(regime) if last_regime.nil? || regime.changed?
60
+ last_regime = regime
61
+ end
62
+ end
63
+
64
+ # Note be sure not to include the regime at the start of the modality otherwise we could
65
+ # output this as a duplicate treatment
66
+ def pd_regimes
67
+ regimes = Renalware::PD::RegimesInDateRangeQuery.new(
68
+ patient: patient,
69
+ from: modality.started_on,
70
+ to: modality.ended_on
71
+ ).call
72
+ regimes - Array(pd_regime_at_start_of_modality)
73
+ end
74
+
75
+ def create_treatment_from(regime)
76
+ start_date = regime.present? ? regime.start_date : modality.started_on
77
+ end_date = regime.present? ? regime.end_date : modality.ended_on
78
+
79
+ create_treatment(regime, start_date, end_date)
80
+ end
81
+
82
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
83
+ def create_treatment(regime, start_date, end_date)
84
+ treatments << Treatment.create!(
85
+ patient: patient,
86
+ clinician: modality.created_by,
87
+ started_on: start_date,
88
+ ended_on: end_date,
89
+ modality_id: modality.id,
90
+ modality_description_id: modality.description_id,
91
+ modality_code: treatment_for_pd_regime(regime),
92
+ pd_regime_id: regime&.id
93
+ )
94
+
95
+ # Update the end date on the previous treatment - ie the one we just added is
96
+ # taking over as the currently active treatment
97
+ unless treatments.length <= 1
98
+ previous_treatment = treatments[treatments.length - 2]
99
+ previous_treatment.update!(ended_on: start_date)
100
+ end
101
+ end
102
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
103
+
104
+ def treatments
105
+ @treatments ||= []
106
+ end
107
+
108
+ def treatment_for_pd_regime(regime)
109
+ ModalityCodeMap.new.code_for_pd_regime(regime)
110
+ end
111
+
112
+ def pd_patient
113
+ Renalware::PD.cast_patient(patient)
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/ukrdc"
4
+
5
+ module Renalware
6
+ module UKRDC
7
+ module TreatmentTimeline
8
+ module PD
9
+ class ModalityCodeMap
10
+ def code_for_pd_regime(regime)
11
+ return default_code if regime.blank?
12
+
13
+ ukrr_name = if regime.treatment =~ /assisted/i
14
+ case regime.pd_type
15
+ when :apd then "Assisted APD"
16
+ when :capd then "Assisted CAPD"
17
+ end
18
+ else
19
+ case regime.pd_type
20
+ when :apd then "APD"
21
+ when :capd then "CAPD"
22
+ end
23
+ end
24
+
25
+ ModalityCode.find_by!(description: ukrr_name)
26
+ end
27
+
28
+ def default_code
29
+ ModalityCode.find_by!(txt_code: 19)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
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 PD
10
+ class RegimeDecorator < DumbDelegator
11
+ def initialize(regime, last_regime:)
12
+ @last_regime = last_regime
13
+ super(regime)
14
+ end
15
+
16
+ def changed?
17
+ return true if last_regime.blank?
18
+
19
+ regime_type_changed?
20
+ end
21
+
22
+ def regime_type_changed?
23
+ last_regime&.type != type
24
+ end
25
+
26
+ def unchanged?
27
+ !changed?
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :last_regime
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -13,6 +13,7 @@ module Renalware
13
13
  # If the site has not defined the ukrdc_prepare_tables SQL function then we exit gracefully.
14
14
  class PrepareTables
15
15
  def self.call
16
+ Treatment.delete_all
16
17
  connection = ActiveRecord::Base.connection
17
18
  result = connection.execute(<<-SQL)
18
19
  select 1 where exists(select 1 from pg_proc where proname = 'ukrdc_prepare_tables');
@@ -143,6 +143,12 @@ module Renalware
143
143
  access_type_abbreviation.split(" ").first
144
144
  end
145
145
 
146
+ def access_rr41_code
147
+ return if access_type_abbreviation.blank?
148
+
149
+ access_type_abbreviation.split(" ").last
150
+ end
151
+
146
152
  protected
147
153
 
148
154
  attr_reader :session, :view_context
@@ -38,12 +38,12 @@ module Renalware
38
38
  super
39
39
  end
40
40
 
41
- def modalities
42
- __getobj__
43
- .modalities
44
- .includes(:description)
45
- .order(started_on: :asc, created_at: :asc)
46
- end
41
+ # def modalities
42
+ # __getobj__
43
+ # .modalities
44
+ # .includes(:description)
45
+ # .order(started_on: :asc, created_at: :asc)
46
+ # end
47
47
 
48
48
  def dead?
49
49
  current_modality_death?
@@ -100,11 +100,22 @@ module Renalware
100
100
  .includes(:updated_by)
101
101
  end
102
102
 
103
+ def treatments
104
+ @treatments ||= begin
105
+ UKRDC::Treatment.where(patient_id: id).delete_all
106
+ UKRDC::TreatmentTimeline::GenerateTimeline.new(self).call
107
+ UKRDC::Treatment.where(patient_id: id).order(:patient_id, :started_on)
108
+ end
109
+ end
110
+
103
111
  # We always send the patients current prescriptions.
112
+ # Because the XSD rejects non-numeric dose amounts, only send prescriptions with a
113
+ # dose_amount of eg 10 or 10.23 or .23
104
114
  def prescriptions
105
115
  __getobj__
106
116
  .prescriptions
107
117
  .includes(:termination, :medication_route, :drug)
118
+ .where("dose_amount ~ '^[ ]*(\\d+|\\d+\.\\d+|\\.\\d+)[ ]*$'")
108
119
  .order(:prescribed_on)
109
120
  end
110
121
 
@@ -26,14 +26,7 @@ xml.Medications do
26
26
  xml.Comments [prescription.dose_amount,
27
27
  prescription.dose_unit&.text,
28
28
  prescription.frequency].compact.join(" ")
29
-
30
- # rubocop:disable Style/RescueModifier
31
- # Only output DoseQuantity is it is an integer or decimal
32
- dose_amount_is_a_number = Float(prescription.dose_amount) rescue nil
33
- # rubocop:enable Style/RescueModifier
34
- if dose_amount_is_a_number
35
- xml.DoseQuantity prescription.dose_amount
36
- end
29
+ xml.DoseQuantity prescription.dose_amount&.strip
37
30
 
38
31
  if prescription.dose_unit.present?
39
32
  xml.DoseUoM do
@@ -2,52 +2,91 @@
2
2
 
3
3
  xml = builder
4
4
 
5
+ patient.treatments.each do |treatment|
6
+ namespace = treatment.modality_description.namespace&.downcase
7
+ partial = namespace&.to_sym || "generic"
8
+ locals = { treatment: treatment, builder: builder, patient: patient }
9
+
10
+ full_partial_path = "renalware/api/ukrdc/patients/treatments/#{partial}"
11
+ partial_exists = lookup_context.find_all(full_partial_path, [], true).any?
12
+
13
+ partial = partial_exists ? partial : "generic"
14
+ render "renalware/api/ukrdc/patients/treatments/#{partial}", **locals
15
+
16
+ # xml.Treatment do
17
+ # xml.EncounterNumber "#{treatment.modality_id}-#{treatment.hd_profile_id}"
18
+ # xml.EncounterType "N"
19
+ # xml.FromTime treatment.started_on&.iso8601
20
+ # xml.ToTime(treatment.ended_on&.iso8601) if treatment.ended_on.present?
21
+
22
+ # if treatment.hospital_unit.present?
23
+ # xml.HealthCareFacility do
24
+ # xml.CodingStandard "ODS"
25
+ # xml.Code treatment.hospital_unit.unit_code
26
+ # end
27
+ # end
28
+
29
+ # xml.AdmitReason do
30
+ # xml.CodingStandard "CF_RR7_TREATMENT"
31
+ # xml.Code treatment.modality_code.txt_code
32
+ # end
33
+
34
+ # # HD
35
+ # rr8 = treatment.hospital_unit&.unit_type_rr8
36
+ # if rr8.present?
37
+ # xml.Attributes do
38
+ # xml.QBL05 rr8 # eg HOME
39
+ # end
40
+ # end
41
+ # end
42
+ end
43
+
5
44
  # A temporary output of the treatment timeline using just modality descriptions with a
6
45
  # crude renal reg modality code assigned to them - HD, PD, Transplant, vCKD - all other modalities
7
46
  # fall through the gaps at this stage until we implement this properly. Also sub RR modal codes
8
47
  # like CAPD and HDF are not yet implemented, just the top level modality.
9
- patient.modalities.each do |modality|
10
- ukrdc_modality_code_id = modality.description.ukrdc_modality_code_id
11
-
12
- next if ukrdc_modality_code_id.blank?
13
-
14
- ukrdc_modality_code = Renalware::UKRDC::ModalityCode.find(ukrdc_modality_code_id)
15
-
16
- xml.Treatment do
17
- xml.EncounterNumber modality.id
18
- xml.EncounterType "N"
19
- xml.FromTime modality.started_on&.iso8601
20
- xml.ToTime(modality.ended_on&.iso8601) if modality.ended_on.present?
21
-
22
- xml.HealthCareFacility do
23
- xml.CodingStandard "ODS"
24
- xml.Code Renalware.config.ukrdc_site_code
25
- end
26
-
27
- xml.AdmitReason do
28
- xml.CodingStandard "CF_RR7_TREATMENT"
29
- xml.Code ukrdc_modality_code.txt_code
30
- end
31
-
32
- # This is a bit of hack to get the HD Profile location for the current modality
33
- if [1, 2, 3, 4, 5, 9].include?(ukrdc_modality_code.txt_code.to_i) # HD
34
- profile = Renalware::HD::Profile
35
- .where(patient_id: patient.id)
36
- .where(<<-SQL).order(created_at: :desc).first
37
- created_at::date <= '#{modality.started_on}'
38
- and (
39
- deactivated_at is NULL or
40
- (
41
- deactivated_at > '#{modality.started_on}'
42
- )
43
- )
44
- SQL
45
-
46
- if profile&.hospital_unit&.unit_type_rr8.present?
47
- xml.Attributes do
48
- xml.QBL05 profile.hospital_unit.unit_type_rr8 # eg HOME
49
- end
50
- end
51
- end
52
- end
53
- end
48
+ # patient.modalities.each do |modality|
49
+ # ukrdc_modality_code_id = modality.description.ukrdc_modality_code_id
50
+
51
+ # next if ukrdc_modality_code_id.blank?
52
+
53
+ # ukrdc_modality_code = Renalware::UKRDC::ModalityCode.find(ukrdc_modality_code_id)
54
+
55
+ # xml.Treatment do
56
+ # xml.EncounterNumber modality.id
57
+ # xml.EncounterType "N"
58
+ # xml.FromTime modality.started_on&.iso8601
59
+ # xml.ToTime(modality.ended_on&.iso8601) if modality.ended_on.present?
60
+
61
+ # xml.HealthCareFacility do
62
+ # xml.CodingStandard "ODS"
63
+ # xml.Code Renalware.config.ukrdc_site_code
64
+ # end
65
+
66
+ # xml.AdmitReason do
67
+ # xml.CodingStandard "CF_RR7_TREATMENT"
68
+ # xml.Code ukrdc_modality_code.txt_code
69
+ # end
70
+
71
+ # # This is a bit of hack to get the HD Profile location for the current modality
72
+ # if [1, 2, 3, 4, 5, 9].include?(ukrdc_modality_code.txt_code.to_i) # HD
73
+ # profile = Renalware::HD::Profile
74
+ # .where(patient_id: patient.id)
75
+ # .where(<<-SQL).order(created_at: :desc).first
76
+ # created_at::date <= '#{modality.started_on}'
77
+ # and (
78
+ # deactivated_at is NULL or
79
+ # (
80
+ # deactivated_at > '#{modality.started_on}'
81
+ # )
82
+ # )
83
+ # SQL
84
+
85
+ # if profile&.hospital_unit&.unit_type_rr8.present?
86
+ # xml.Attributes do
87
+ # xml.QBL05 profile.hospital_unit.unit_type_rr8 # eg HOME
88
+ # end
89
+ # end
90
+ # end
91
+ # end
92
+ # end