renalware-core 2.0.130 → 2.0.131

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -2
  3. data/app/controllers/renalware/admin/users_controller.rb +1 -0
  4. data/app/controllers/renalware/medications/prescriptions_controller.rb +2 -2
  5. data/app/controllers/renalware/patients/patients_controller.rb +1 -1
  6. data/app/jobs/renalware/letters/calculate_page_count_job.rb +5 -0
  7. data/app/models/concerns/renalware/pdf_compilation.rb +2 -2
  8. data/app/models/renalware/events/lists/form.rb +1 -1
  9. data/app/models/renalware/letters/approve_letter.rb +3 -1
  10. data/app/models/renalware/letters/delivery/email_letter_to_practice.rb +6 -14
  11. data/app/models/renalware/letters/lists/form.rb +2 -2
  12. data/app/models/renalware/letters/pdf_renderer.rb +1 -0
  13. data/app/models/renalware/letters/printing/pdf_combining.rb +2 -2
  14. data/app/models/renalware/medications/dose_unit.rb +2 -0
  15. data/app/models/renalware/modalities/description.rb +10 -2
  16. data/app/models/renalware/pd/apd/glucose_calculator.rb +2 -2
  17. data/app/models/renalware/system/update_user.rb +3 -0
  18. data/app/models/renalware/ukrdc/create_encrypted_patient_xml_files.rb +15 -4
  19. data/app/models/renalware/ukrdc/create_patient_xml_file.rb +16 -3
  20. data/app/models/renalware/ukrdc/outgoing/rendering/address.rb +38 -0
  21. data/app/models/renalware/ukrdc/outgoing/rendering/allergy.rb +27 -0
  22. data/app/models/renalware/ukrdc/outgoing/rendering/base.rb +20 -0
  23. data/app/models/renalware/ukrdc/outgoing/rendering/cause_of_death.rb +38 -0
  24. data/app/models/renalware/ukrdc/outgoing/rendering/clinic_visit_observation.rb +24 -0
  25. data/app/models/renalware/ukrdc/outgoing/rendering/clinician.rb +12 -0
  26. data/app/models/renalware/ukrdc/outgoing/rendering/diagnoses.rb +63 -0
  27. data/app/models/renalware/ukrdc/outgoing/rendering/diagnosis.rb +52 -0
  28. data/app/models/renalware/ukrdc/outgoing/rendering/dialysis_session.rb +81 -0
  29. data/app/models/renalware/ukrdc/outgoing/rendering/document.rb +46 -0
  30. data/app/models/renalware/ukrdc/outgoing/rendering/entered_at.rb +26 -0
  31. data/app/models/renalware/ukrdc/outgoing/rendering/entered_by.rb +12 -0
  32. data/app/models/renalware/ukrdc/outgoing/rendering/family_doctor.rb +30 -0
  33. data/app/models/renalware/ukrdc/outgoing/rendering/hd_session_observations.rb +51 -0
  34. data/app/models/renalware/ukrdc/outgoing/rendering/hd_treatment.rb +26 -0
  35. data/app/models/renalware/ukrdc/outgoing/rendering/lab_order.rb +80 -0
  36. data/app/models/renalware/ukrdc/outgoing/rendering/lab_orders.rb +30 -0
  37. data/app/models/renalware/ukrdc/outgoing/rendering/medication.rb +77 -0
  38. data/app/models/renalware/ukrdc/outgoing/rendering/name.rb +29 -0
  39. data/app/models/renalware/ukrdc/outgoing/rendering/observation.rb +70 -0
  40. data/app/models/renalware/ukrdc/outgoing/rendering/observations.rb +58 -0
  41. data/app/models/renalware/ukrdc/outgoing/rendering/patient.rb +167 -0
  42. data/app/models/renalware/ukrdc/outgoing/rendering/patient_number.rb +27 -0
  43. data/app/models/renalware/ukrdc/outgoing/rendering/patient_numbers.rb +53 -0
  44. data/app/models/renalware/ukrdc/outgoing/rendering/pd_treatment.rb +27 -0
  45. data/app/models/renalware/ukrdc/outgoing/rendering/primary_language.rb +35 -0
  46. data/app/models/renalware/ukrdc/outgoing/rendering/procedures.rb +40 -0
  47. data/app/models/renalware/ukrdc/outgoing/rendering/renal_diagnosis.rb +35 -0
  48. data/app/models/renalware/ukrdc/outgoing/rendering/transplant.rb +69 -0
  49. data/app/models/renalware/ukrdc/outgoing/rendering/treatment.rb +78 -0
  50. data/app/models/renalware/ukrdc/outgoing/rendering/user.rb +33 -0
  51. data/app/models/renalware/ukrdc/pathology_observation_requests_query.rb +1 -0
  52. data/app/models/renalware/ukrdc/xml_renderer.rb +54 -75
  53. data/app/models/renalware/user.rb +4 -1
  54. data/app/presenters/renalware/hd/scheduling/diary_presenter.rb +2 -2
  55. data/app/presenters/renalware/system/users_presenter.rb +1 -1
  56. data/app/presenters/renalware/ukrdc/transplant_operation_presenter.rb +4 -0
  57. data/app/views/renalware/admin/users/edit.html.slim +6 -0
  58. data/app/views/renalware/admin/users/index.html.slim +6 -1
  59. data/app/views/renalware/api/ukrdc/patients/_clinic_visit_observation.xml.builder +3 -3
  60. data/app/views/renalware/api/ukrdc/patients/_documents.xml.builder +28 -24
  61. data/app/views/renalware/api/ukrdc/patients/_sending_facility.xml.builder +1 -1
  62. data/app/views/renalware/api/ukrdc/patients/_treatments.xml.builder +0 -27
  63. data/app/views/renalware/api/ukrdc/patients/show.xml.builder +1 -5
  64. data/app/views/renalware/medications/drug_types/prescriptions/_table.html.slim +1 -1
  65. data/app/views/renalware/patients/patients/_form.html.slim +1 -0
  66. data/app/views/renalware/patients/patients/show/_contact_details.html.slim +2 -1
  67. data/config/initializers/core_extensions.rb +1 -0
  68. data/config/initializers/devise.rb +1 -1
  69. data/config/locales/renalware/medications/prescription.yml +2 -0
  70. data/db/migrate/20170526060804_enable_uuid_extension.rb +1 -1
  71. data/db/migrate/20191219145651_add_hidden_to_users.rb +8 -0
  72. data/db/migrate/20200106073329_add_next_of_kin_to_patients.rb +7 -0
  73. data/db/migrate/20200106210851_create_case_insensitive_index_on_patients.rb +22 -0
  74. data/lib/core_extensions/active_record/migration_helpers.rb +7 -0
  75. data/lib/core_extensions/ox/element_additions.rb +18 -0
  76. data/lib/renalware/engine.rb +2 -1
  77. data/lib/renalware/version.rb +1 -1
  78. data/lib/tasks/spec.rake +33 -0
  79. data/spec/factories/accesses/assessments.rb +18 -0
  80. data/spec/factories/accesses/catheter_insertion_techniques.rb +8 -0
  81. data/spec/factories/accesses/plans.rb +1 -1
  82. data/spec/factories/accesses/plans_types.rb +1 -1
  83. data/spec/factories/accesses/procedures.rb +2 -2
  84. data/spec/factories/accesses/profiles.rb +1 -1
  85. data/spec/factories/accesses/sites.rb +1 -1
  86. data/spec/factories/accesses/types.rb +1 -1
  87. data/spec/factories/hd/hd_session_document.rb +8 -8
  88. data/spec/factories/transplants/donations.rb +1 -1
  89. data/spec/factories/transplants/recipient_followups.rb +1 -1
  90. data/spec/factories/transplants/recipient_operations.rb +1 -1
  91. data/spec/factories/transplants/registration_status_descriptions.rb +1 -1
  92. data/spec/factories/transplants/registration_statuses.rb +1 -1
  93. data/spec/factories/transplants/registrations.rb +1 -1
  94. data/spec/factories/transplants/rejection_episodes.rb +1 -1
  95. data/spec/factories/transplants/rejection_treatments.rb +1 -1
  96. data/spec/factories/ukrdc/modality_codes.rb +1 -1
  97. data/spec/support/capybara_helper.rb +6 -0
  98. data/spec/support/letters_spec_helper.rb +2 -2
  99. data/spec/support/pages/accesses/procedure_page.rb +53 -0
  100. data/spec/support/pages/accesses/profile_page.rb +45 -0
  101. data/spec/support/pages/clinical/allergy_page.rb +74 -0
  102. data/spec/support/pages/letters/form.rb +5 -3
  103. data/spec/support/shared_contexts/a_global_rule_set.rb +1 -1
  104. data/spec/support/xml_spec_helper.rb +9 -0
  105. metadata +60 -11
  106. data/app/views/renalware/api/ukrdc/patients/_clinical_relationships.xml.builder +0 -7
  107. data/app/views/renalware/api/ukrdc/patients/_family_histories.xml.builder +0 -7
  108. data/app/views/renalware/api/ukrdc/patients/_program_memberships.xml.builder +0 -7
  109. data/app/views/renalware/api/ukrdc/patients/_surveys.xml.builder +0 -7
  110. data/app/views/renalware/api/ukrdc/patients/observations/_blood_pressure.xml.builder +0 -0
  111. data/app/views/renalware/api/ukrdc/patients/observations/_standing_blood_pressure.xml.builder +0 -0
  112. data/app/views/renalware/api/ukrdc/patients/observations/_weight.xml.builder +0 -0
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renalware
4
+ module UKRDC
5
+ module Outgoing
6
+ module Rendering
7
+ class Base
8
+ protected
9
+
10
+ def create_node(name, value = nil)
11
+ elem = Ox::Element.new(name.to_s)
12
+ elem << value.to_s if value.present?
13
+ yield(elem) if block_given?
14
+ elem
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # frozen_string_literal: true
4
+
5
+ module Renalware
6
+ module UKRDC
7
+ module Outgoing
8
+ module Rendering
9
+ class CauseOfDeath < Rendering::Base
10
+ pattr_initialize [:patient!]
11
+ delegate :first_cause, to: :patient, allow_nil: true
12
+
13
+ def xml
14
+ return nil unless patient.dead? && first_cause.present?
15
+
16
+ cause_of_death_element
17
+ end
18
+
19
+ private
20
+
21
+ def cause_of_death_element
22
+ cause_code = first_cause.code
23
+ cause_code = 99 if cause_code == 34
24
+
25
+ create_node("CauseOfDeath") do |elem|
26
+ elem << create_node("DiagnosisType", "final")
27
+ elem << create_node("Diagnosis") do |diagnosis_element|
28
+ diagnosis_element << create_node("CodingStandard", "EDTA_COD")
29
+ diagnosis_element << create_node("Code", cause_code)
30
+ end
31
+ elem << create_node("EnteredOn", first_cause.created_at.to_datetime)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renalware
4
+ module UKRDC
5
+ module Outgoing
6
+ module Rendering
7
+ class ClinicVisitObservation < Rendering::Base
8
+ pattr_initialize [:visit!, :method!, :i18n_key]
9
+
10
+ def xml
11
+ measurement = visit.public_send(method)
12
+ return if measurement.blank? || measurement.to_f.zero?
13
+
14
+ Observation.new(
15
+ observed_at: visit.datetime,
16
+ measurement: measurement,
17
+ i18n_key: i18n_key || method
18
+ ).xml
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renalware
4
+ module UKRDC
5
+ module Outgoing
6
+ module Rendering
7
+ class Clinician < Rendering::User
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renalware
4
+ module UKRDC
5
+ module Outgoing
6
+ module Rendering
7
+ class Diagnoses < Rendering::Base
8
+ pattr_initialize [:patient!]
9
+
10
+ def xml
11
+ diagnoses_element
12
+ end
13
+
14
+ private
15
+
16
+ def diagnoses_element
17
+ create_node("Diagnoses") do |elem|
18
+ create_comobitity_diagnoses_inside(elem)
19
+ elem << smoking_diagnosis_element
20
+ elem << cause_of_death_element
21
+ elem << renal_diagnosis_element
22
+ end
23
+ end
24
+
25
+ # See email from GS to TC 23/5/18 regarding dates.
26
+ # UKRDC would like to receive a date so if no onset date stored in RW for the
27
+ # comorbidity, send the esrf date as the identification date
28
+ def create_comobitity_diagnoses_inside(elem)
29
+ patient.yes_comorbidities.each do |comorb|
30
+ options = { coding_standard: "SNOMED", code: comorb.code, description: comorb.name }
31
+ if comorb.date.present?
32
+ options[:onset_time] = comorb.date
33
+ elsif patient.esrf_on.present?
34
+ options[:identification_time] = patient.esrf_on
35
+ end
36
+ elem << Rendering::Diagnosis.new(options).xml
37
+ end
38
+ end
39
+
40
+ def smoking_diagnosis_element
41
+ return if patient.smoking_cormbidity&.value.blank?
42
+
43
+ diagnosis = Rendering::Diagnosis.new(
44
+ coding_standard: "SNOMED",
45
+ code: patient.smoking_cormbidity.snomed_code,
46
+ description: "Smoking history: #{patient.smoking_cormbidity.snomed_description}"
47
+ )
48
+ diagnosis.identification_time = patient.esrf_on if patient.esrf_on.present?
49
+ diagnosis.xml
50
+ end
51
+
52
+ def cause_of_death_element
53
+ Rendering::CauseOfDeath.new(patient: patient).xml
54
+ end
55
+
56
+ def renal_diagnosis_element
57
+ Rendering::RenalDiagnosis.new(patient: patient).xml
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renalware
4
+ module UKRDC
5
+ module Outgoing
6
+ module Rendering
7
+ class Diagnosis < Rendering::Base
8
+ pattr_initialize [
9
+ :coding_standard!,
10
+ :code!,
11
+ :description!,
12
+ :onset_time,
13
+ :identification_time,
14
+ root_elemment_name!: "Diagnosis"
15
+ ]
16
+ attr_accessor :onset_time, :identification_time
17
+
18
+ def xml
19
+ diagnosis_element
20
+ end
21
+
22
+ private
23
+
24
+ # The nested Diagnosis is correct.
25
+ def diagnosis_element
26
+ create_node(root_elemment_name) do |elem|
27
+ elem << create_node("Diagnosis") do |diagnosis_elem|
28
+ diagnosis_elem << create_node("CodingStandard", coding_standard)
29
+ diagnosis_elem << create_node("Code", code)
30
+ diagnosis_elem << create_node("Description", description)
31
+ end
32
+ add_onset_time_element_to(elem)
33
+ add_identification_time_element_to(elem)
34
+ end
35
+ end
36
+
37
+ def add_onset_time_element_to(elem)
38
+ return if onset_time.blank?
39
+
40
+ elem << create_node("OnsetTime", onset_time)
41
+ end
42
+
43
+ def add_identification_time_element_to(elem)
44
+ return if identification_time.blank?
45
+
46
+ elem << create_node("IdentificationTime", identification_time)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renalware
4
+ module UKRDC
5
+ module Outgoing
6
+ module Rendering
7
+ class DialysisSession < Rendering::Base
8
+ pattr_initialize [:session!]
9
+
10
+ def xml
11
+ dialysis_session_element
12
+ end
13
+
14
+ private
15
+
16
+ # The nested Diagnosis is correct.
17
+ def dialysis_session_element
18
+ create_node("DialysisSession") do |session_elem|
19
+ session_elem["start"] = session.performed_on_date
20
+ session_elem["stop"] = session.performed_on_date
21
+ session_elem << procedure_type_element
22
+ session_elem << clinician_element
23
+ session_elem << procedure_time_element
24
+ session_elem << entered_by_element
25
+ session_elem << entered_at_element
26
+ session_elem << external_id_element
27
+ session_elem << attributes_element
28
+ end
29
+ end
30
+
31
+ def procedure_type_element
32
+ create_node("ProcedureType") do |elem|
33
+ elem << create_node("CodingStandard", "SNOMED")
34
+ elem << create_node("Code", "302497006")
35
+ elem << create_node("Description", "Haemodialysis")
36
+ end
37
+ end
38
+
39
+ def clinician_element
40
+ create_node("Clinician") do |elem|
41
+ elem << create_node("Description", session.updated_by)
42
+ end
43
+ end
44
+
45
+ def entered_at_element
46
+ create_node("EnteredAt") do |elem|
47
+ elem << create_node("Code", session.hospital_unit_renal_registry_code)
48
+ end
49
+ end
50
+
51
+ def entered_by_element
52
+ Rendering::EnteredBy.new(user: session.updated_by).xml
53
+ end
54
+
55
+ def external_id_element
56
+ create_node("ExternalId", session.uuid)
57
+ end
58
+
59
+ def procedure_time_element
60
+ create_node("ProcedureTime", session.start_datetime.to_datetime)
61
+ end
62
+
63
+ # rubocop:disable Metrics/AbcSize
64
+ def attributes_element
65
+ create_node("Attributes") do |elem|
66
+ elem << create_node("QHD19", session.had_intradialytic_hypotension?)
67
+ elem << create_node("QHD20", session.access_rr02_code)
68
+ elem << create_node("QHD21", session.access_rr41_code)
69
+ elem << create_node("QHD22", "N") # Access in two sites simultaneously
70
+ elem << create_node("QHD30", session.blood_flow)
71
+ elem << create_node("QHD31", session.duration_in_minutes) # Time Dialysed in Minutes
72
+ elem << create_node("QHD32", session.sodium_content) # Sodium in Dialysate
73
+ elem << create_node("QHD33") # TODO: Needling Method
74
+ end
75
+ end
76
+ # rubocop:enable Metrics/AbcSize
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renalware
4
+ module UKRDC
5
+ module Outgoing
6
+ module Rendering
7
+ class Document < Rendering::Base
8
+ pattr_initialize [:letter!]
9
+
10
+ def xml
11
+ document_element
12
+ end
13
+
14
+ private
15
+
16
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
17
+ def document_element
18
+ create_node("Document") do |elem|
19
+ elem << create_node("DocumentTime", letter.issued_on.to_time.iso8601)
20
+ elem << Clinician.new(user: letter.author).xml
21
+ elem << create_node("DocumentName", letter.title)
22
+ elem << create_node("Status") do |status|
23
+ status << create_node("Code", "ACTIVE")
24
+ end
25
+ elem << EnteredBy.new(user: letter.updated_by).xml
26
+ if letter.hospital_unit_renal_registry_code.present?
27
+ elem << create_node("EnteredAt") do |entered_at|
28
+ entered_at << create_node("CodingStandard", "LOCAL")
29
+ entered_at << create_node("Code", letter.hospital_unit_renal_registry_code)
30
+ end
31
+ end
32
+ elem << create_node("FileType", "application/pdf")
33
+ elem << create_node("FileName", letter.pdf_stateless_filename)
34
+ elem << create_node("Stream", encoded_document_content)
35
+ end
36
+ end
37
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
38
+
39
+ def encoded_document_content
40
+ Base64.encode64(Renalware::Letters::PdfRenderer.call(letter))
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renalware
4
+ module UKRDC
5
+ module Outgoing
6
+ module Rendering
7
+ class EnteredAt < Rendering::Base
8
+ pattr_initialize [:hospital_unit!]
9
+
10
+ def xml
11
+ element
12
+ end
13
+
14
+ private
15
+
16
+ def element
17
+ Ox::Element.new("EnteredAt").tap do |elem|
18
+ elem << create_node("CodingStandard", "LOCAL")
19
+ elem << create_node("Code", hospital_unit&.unit_code)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renalware
4
+ module UKRDC
5
+ module Outgoing
6
+ module Rendering
7
+ class EnteredBy < Rendering::User
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renalware
4
+ module UKRDC
5
+ module Outgoing
6
+ module Rendering
7
+ class FamilyDoctor < Rendering::Base
8
+ pattr_initialize [:patient!]
9
+
10
+ def xml
11
+ family_doctor_element
12
+ end
13
+
14
+ private
15
+
16
+ def family_doctor_element
17
+ create_node("FamilyDoctor") do |family_doctor|
18
+ if patient.practice.present?
19
+ family_doctor << create_node("GPPracticeId", patient.practice.code)
20
+ end
21
+ if patient.primary_care_physician.present?
22
+ family_doctor << create_node("GPId", patient.primary_care_physician.code)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renalware
4
+ module UKRDC
5
+ module Outgoing
6
+ module Rendering
7
+ class HDSessionObservations < Rendering::Observation
8
+ pattr_initialize [:session!]
9
+
10
+ def create_observation_nodes_under(parent_element)
11
+ pre_and_post_observations.each do |pre_or_post, observations|
12
+ measurements_for(observations).each do |i18n_key, value|
13
+ parent_element << Rendering::Observation.new(
14
+ observed_at: observation_times[pre_or_post].iso8601,
15
+ measurement: value.to_s[0, 19].strip,
16
+ i18n_key: i18n_key,
17
+ pre_post: pre_or_post.to_s.upcase # eg PRE or POST
18
+ ).xml
19
+ end
20
+ end
21
+ end
22
+
23
+ def observation_times
24
+ @observation_times ||= begin
25
+ performed_on = session.performed_on.to_datetime
26
+ {
27
+ pre: performed_on + session.start_time.seconds_since_midnight.seconds,
28
+ post: performed_on + session.end_time.seconds_since_midnight.seconds
29
+ }
30
+ end
31
+ end
32
+
33
+ def pre_and_post_observations
34
+ {
35
+ pre: session.document.observations_before,
36
+ post: session.document.observations_after
37
+ }
38
+ end
39
+
40
+ def measurements_for(observations)
41
+ {
42
+ "blood_pressure.systolic" => observations.blood_pressure&.systolic,
43
+ "blood_pressure.diastolic" => observations.blood_pressure&.diastolic,
44
+ "weight" => observations.weight
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end