renalware-core 2.0.89 → 2.0.90

Sign up to get free protection for your applications and to get access to all the features.
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