renalware-core 2.0.53 → 2.0.54

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/app/documents/renalware/hd/session_document.rb +1 -1
  3. data/app/inputs/clock_picker_input.rb +1 -1
  4. data/app/inputs/date_picker_input.rb +1 -1
  5. data/app/inputs/simple_time_input.rb +1 -1
  6. data/app/mailers/renalware/previews/report_preview.rb +1 -1
  7. data/app/mailers/renalware/previews/ukrdc_summary_preview.rb +20 -0
  8. data/app/mailers/renalware/previews/user_mailer_preview.rb +1 -0
  9. data/app/mailers/renalware/ukrdc/summary_mailer.rb +19 -0
  10. data/app/models/renalware/ukrdc/create_encrypted_patient_xml_files.rb +136 -0
  11. data/app/models/renalware/ukrdc/{send_patient.rb → create_patient_xml_file.rb} +24 -11
  12. data/app/models/renalware/ukrdc/export_summary.rb +22 -0
  13. data/app/models/renalware/ukrdc/paths.rb +58 -0
  14. data/app/models/renalware/ukrdc/patients_query.rb +10 -0
  15. data/app/presenters/renalware/ukrdc/patient_presenter.rb +1 -1
  16. data/app/validators/renalware/patients/hdf_presence_validator.rb +1 -1
  17. data/app/views/renalware/api/ukrdc/patients/_documents.xml.builder +0 -2
  18. data/app/views/renalware/api/ukrdc/patients/_medications.xml.builder +0 -2
  19. data/app/views/renalware/api/ukrdc/patients/encounters/_hd_session.xml.builder +0 -8
  20. data/app/views/renalware/api/ukrdc/patients/lab_orders/_lab_order.xml.builder +0 -3
  21. data/app/views/renalware/api/ukrdc/patients/lab_orders/_result_item.xml.builder +0 -6
  22. data/app/views/renalware/problems/problems/_archived_table.html.slim +1 -1
  23. data/app/views/renalware/problems/problems/_current_table.html.slim +3 -3
  24. data/app/views/renalware/ukrdc/summary_mailer/export_summary.html.slim +13 -0
  25. data/app/views/renalware/ukrdc/summary_mailer/export_summary.text.erb +11 -0
  26. data/db/migrate/20181025170410_add_ukrdc_helper_column_to_patients.rb +9 -0
  27. data/db/views/reporting_daily_pathology_v02.sql +1 -1
  28. data/lib/gpg_encrypt_folder.rb +16 -4
  29. data/lib/renalware/configuration.rb +3 -0
  30. data/lib/renalware/version.rb +1 -1
  31. data/lib/tasks/ukrdc.rake +2 -2
  32. metadata +12 -5
  33. data/app/models/renalware/ukrdc/send_patients.rb +0 -112
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c36923d9a31021d45bae1db60627efefd8b843457cb6a7e102012cb989d254a
4
- data.tar.gz: 86a0c83e095d0a70e3dbeb2905e44951e5553a3a7051977071e3ed302daee258
3
+ metadata.gz: 850fb8d5f1245a54162eaa4fb6e075f272563bac0729d30d4209fbf34c3fd378
4
+ data.tar.gz: 90e3476b1530fb2d49447bd9a882e456bcdabe533004be2d060993b361b6c076
5
5
  SHA512:
6
- metadata.gz: 05e3cebc48a295074ac4ad1727debdee1c11b2a76c3a42d4fb973ad7393e3a2ee2697f7aac3dec3bb286d133a75c4e701f1ec6dd0d0aeef396c6877ffd217754
7
- data.tar.gz: 07dd641741d884f975ac577f89ead6ef2ed8a33f11caabcab7ebe1921bbfe8e037f200fe00e5b5bc7ca2a89fecf4e04e008d6347528fb7b3929200943e315c38
6
+ metadata.gz: 63f4a175065d5c917e9e12b70466482fd0a9b8dad17122024909e6eb6a13b1f82183520346755ca72dfc8e3dbd0c450cd95351e9dfd2d95ff0fcaa8788a44d27
7
+ data.tar.gz: cd8734fc777f8dc1fcb54ff4814aa6a9e23a0628e774c5224b427873455d937f6a173a62779ffbbdf4ea2dafd40f4ee795a259a1e3affbb09390f348066b178d
@@ -75,7 +75,7 @@ module Renalware
75
75
  ).each { |att| validates(att, numericality: { allow_blank: true }) }
76
76
 
77
77
  validates :machine_urr, inclusion: { in: 0..100, allow_blank: true }
78
- validates :machine_ktv, inclusion: { in: (0.2..3.5), allow_blank: true }
78
+ validates :machine_ktv, inclusion: { in: (0.05..3.5), allow_blank: true }
79
79
  validates :blood_flow, numericality: {
80
80
  greater_than_or_equal_to: 50,
81
81
  less_than_or_equal_to: 800,
@@ -28,7 +28,7 @@ class ClockPickerInput < SimpleForm::Inputs::StringInput
28
28
  end
29
29
 
30
30
  def icon_clock
31
- "<span class='prefix'><i class='fa fa-clock-o'></i></span>".html_safe
31
+ "<span class='prefix'><i class='far fa-clock'></i></span>".html_safe
32
32
  end
33
33
 
34
34
  def input_type
@@ -30,7 +30,7 @@ class DatePickerInput < SimpleForm::Inputs::StringInput
30
30
  # rubocop:enable Metrics/AbcSize
31
31
 
32
32
  def icon_calendar
33
- "<span class='prefix'><i class='fa fa-calendar'></i></span>".html_safe
33
+ "<span class='prefix'><i class='far fa-calendar'></i></span>".html_safe
34
34
  end
35
35
 
36
36
  def input_type
@@ -25,7 +25,7 @@ class SimpleTimeInput < SimpleForm::Inputs::StringInput
25
25
  end
26
26
 
27
27
  def icon_clock
28
- "<span class='prefix'><i class='fa fa-clock-o'></i></span>".html_safe
28
+ "<span class='prefix'><i class='far fa-clock'></i></span>".html_safe
29
29
  end
30
30
 
31
31
  def input_type
@@ -4,7 +4,7 @@ require_dependency "renalware/reporting"
4
4
 
5
5
  module Renalware
6
6
  module Reporting
7
- # Preview all emails at http://localhost:3000/rails/mailers/report
7
+ # Preview all emails at http://localhost:3000/rails/mailers
8
8
  class ReportPreview < ActionMailer::Preview
9
9
  def daily_summary
10
10
  Reporting::ReportMailer.daily_summary
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/reporting"
4
+
5
+ module Renalware
6
+ module UKRDC
7
+ # Preview all emails at http://localhost:3000/rails/mailers
8
+ class SumaryPreview < ActionMailer::Preview
9
+ def daily_summary
10
+ summary = ExportSummary.new(
11
+ archive_folder: "/var/ukrdc/test",
12
+ num_changed_patients: 10,
13
+ milliseconds_taken: 123.0,
14
+ results: { sent: 1, unsent: 2 }
15
+ )
16
+ UKRDC::SummaryMailer.export_summary(summary: summary)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Renalware
4
+ # Preview all emails at http://localhost:3000/rails/mailers
4
5
  class Admin::UserMailerPreview < ActionMailer::Preview
5
6
  def approval
6
7
  user = User.first
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/reporting"
4
+
5
+ module Renalware
6
+ module UKRDC
7
+ class SummaryMailer < ApplicationMailer
8
+ def export_summary(to: "dev@airslie.com", **args)
9
+ mail(
10
+ to: Array(to),
11
+ subject: "UKRC export summary #{I18n.l(Time.zone.today)}"
12
+ ) do |format|
13
+ format.text { render(locals: args) }
14
+ format.html { render(locals: args) }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/ukrdc"
4
+ require "gpg_encrypt_folder"
5
+
6
+ module Renalware
7
+ module UKRDC
8
+ # Using a working folder with a timestamp name, find matching patients and for each, generate
9
+ # an XML file (see UKRDC Schema) containing changes since the last time we sent the URDC data
10
+ # about them. Encrypt the xml files and copy to an outgoing folder
11
+ # which might for example be a symlink to an outgoing folder in /mmedia/ukrdc which in turn
12
+ # is mount on a remote share for example on an SFTP server.
13
+ #
14
+ class CreateEncryptedPatientXMLFiles
15
+ attr_reader :patient_ids, :changed_since, :logger, :request_uuid, :paths, :timestamp, :summary
16
+
17
+ def initialize(changed_since: nil, patient_ids: nil, logger: nil)
18
+ @changed_since = Time.zone.parse(changed_since) if changed_since.present?
19
+ @patient_ids = Array(patient_ids)
20
+ @logger = logger || Rails.logger
21
+ @request_uuid = SecureRandom.uuid # helps group logs together
22
+ @timestamp = Time.zone.now.strftime("%Y%m%d%H%M%S%L")
23
+ @paths = Paths.new(timestamp: @timestamp, working_path: config.ukrdc_working_path)
24
+ @summary = ExportSummary.new
25
+ end
26
+
27
+ def call
28
+ logger.tagged(request_uuid) do
29
+ summary.milliseconds_taken = Benchmark.ms do
30
+ create_patient_xml_files
31
+ encrypt_patient_xml_files
32
+ copy_encrypted_xml_files_into_the_outgoing_folder
33
+ paths.create_symlink_to_latest_timestamped_folder_so_it_is_easier_to_eyeball
34
+ end
35
+ build_summary
36
+ print_summary
37
+ email_summary
38
+ end
39
+ rescue StandardError => exception
40
+ Engine.exception_notifier.notify(exception)
41
+ raise exception
42
+ end
43
+
44
+ private
45
+
46
+ def create_patient_xml_files
47
+ patients = ukrdc_patients_who_have_changed_since_last_send
48
+ summary.num_changed_patients = patients.count
49
+ patients.find_each do |patient|
50
+ CreatePatientXMLFile.new(
51
+ patient: patient,
52
+ dir: paths.timestamped_xml_folder,
53
+ changes_since: changed_since,
54
+ request_uuid: request_uuid,
55
+ logger: logger
56
+ ).call
57
+ end
58
+ end
59
+
60
+ def build_summary
61
+ summary.count_of_files_in_outgoing_folder = count_of_files_in_outgoing_folder
62
+ summary.archive_folder = paths.timestamped_folder
63
+ summary.results = export_results
64
+ end
65
+
66
+ # rubocop:disable Metrics/AbcSize
67
+ def ukrdc_patients_who_have_changed_since_last_send
68
+ @ukrdc_patients_who_have_changed_since_last_send ||= begin
69
+ logger.info("Finding #{patient_ids&.any? ? patient_ids : 'all ukrdc'} patients")
70
+ query = Renalware::UKRDC::PatientsQuery.new.call(changed_since: changed_since)
71
+ query = query.where(id: Array(patient_ids)) if patient_ids.present?
72
+
73
+ if changed_since.present?
74
+ logger.info("#{query.count} patients have changed since #{changed_since}")
75
+ else
76
+ logger.info("#{query.count} patients have changed since the last send")
77
+ end
78
+ query.all
79
+ end
80
+ end
81
+ # rubocop:enable Metrics/AbcSize
82
+
83
+ # rubocop:disable Metrics/AbcSize
84
+ def print_summary
85
+ logger.info("Files saved to #{summary.archive_folder}")
86
+ logger.info "*** Summary ***"
87
+ logger.info "Took #{summary.milliseconds_taken.to_i / 1000} seconds"
88
+ summary.results.map{ |key, value| logger.info("#{key}: #{value}") }
89
+ end
90
+ # rubocop:enable Metrics/AbcSize
91
+
92
+ def email_summary
93
+ UKRDC::SummaryMailer.export_summary(
94
+ to: email_recipients,
95
+ summary: summary
96
+ ).deliver
97
+ end
98
+
99
+ def email_recipients
100
+ Array(ENV.fetch("DAILY_REPORT_EMAIL_RECIPIENTS", "dev@airslie.com").split(","))
101
+ end
102
+
103
+ def export_results
104
+ TransmissionLog.where(request_uuid: request_uuid).group(:status).count(:status).to_h
105
+ end
106
+
107
+ def encrypt_patient_xml_files
108
+ logger.info "Encrypting..."
109
+ GpgEncryptFolder.new(folder: paths.timestamped_xml_folder, options: gpg_options).call
110
+ end
111
+
112
+ def copy_encrypted_xml_files_into_the_outgoing_folder
113
+ encrypted_files = Dir.glob(paths.timestamped_encrypted_folder.join("*.*"))
114
+ FileUtils.cp_r(encrypted_files, paths.outgoing_folder) if encrypted_files.any?
115
+ end
116
+
117
+ def count_of_files_in_outgoing_folder
118
+ Dir.glob(File.join(paths.outgoing_folder, "*.*")).count
119
+ end
120
+
121
+ def config
122
+ Renalware.config
123
+ end
124
+
125
+ def gpg_options
126
+ GpgOptions.new(
127
+ recipient: config.ukrdc_gpg_recipient,
128
+ keyring: config.ukrdc_gpg_keyring,
129
+ homedir: config.ukrdc_gpg_homedir,
130
+ destination_folder: paths.timestamped_encrypted_folder,
131
+ timestamp: timestamp
132
+ )
133
+ end
134
+ end
135
+ end
136
+ end
@@ -5,10 +5,12 @@ require "attr_extras"
5
5
 
6
6
  module Renalware
7
7
  module UKRDC
8
- class SendPatient
8
+ class CreatePatientXMLFile
9
9
  pattr_initialize [:patient!, :dir!, :request_uuid!, :renderer, :changes_since, :logger]
10
10
 
11
+ # rubocop:disable Metrics/AbcSize
11
12
  def call
13
+ update_patient_to_indicated_we_checked_them_for_any_relevant_changes
12
14
  UKRDC::TransmissionLog.with_logging(patient, request_uuid) do |log|
13
15
  logger.info " Patient #{patient.to_param}"
14
16
  xml_payload = build_payload(log)
@@ -16,17 +18,13 @@ module Renalware
16
18
  logger.info " skipping as no change in XML file"
17
19
  log.unsent_no_change_since_last_send!
18
20
  else
19
- send_file(xml_payload, log)
20
- # Important we use update_column here so we don't trigger updated_at to change
21
- # on the patient, which affects the results of PatientsQuery next time.
22
- patient.update_column(:sent_to_ukrdc_at, Time.zone.now)
23
- logger.info(
24
- " sending file and setting patient.sent_to_ukrdc_at = #{patient.sent_to_ukrdc_at}"
25
- )
21
+ create_xml_file(xml_payload, log)
22
+ update_patient_to_indicate_we_have_sent_their_data_to_ukrdc
26
23
  end
27
24
  logger.info " Status: #{log.status}"
28
25
  end
29
26
  end
27
+ # rubocop:enable Metrics/AbcSize
30
28
 
31
29
  private
32
30
 
@@ -34,11 +32,26 @@ module Renalware
34
32
  @logger ||= Rails.logger
35
33
  end
36
34
 
35
+ # Important we use update_column here so we don't trigger updated_at to change
36
+ # on the patient, which affects the results of PatientsQuery next time.
37
+ def update_patient_to_indicated_we_checked_them_for_any_relevant_changes
38
+ patient.update_column(:checked_for_ukrdc_changes_at, Time.zone.now)
39
+ end
40
+
41
+ # Important we use update_column here so we don't trigger updated_at to change
42
+ # on the patient, which affects the results of PatientsQuery next time.
43
+ def update_patient_to_indicate_we_have_sent_their_data_to_ukrdc
44
+ patient.update_column(:sent_to_ukrdc_at, Time.zone.now)
45
+ logger.info(
46
+ " sending file and setting patient.sent_to_ukrdc_at = #{patient.sent_to_ukrdc_at}"
47
+ )
48
+ end
49
+
37
50
  def xml_payload_same_as_last_sent_payload?(payload)
38
51
  payload.to_md5_hash == last_sent_transmission_log.payload_hash
39
52
  end
40
53
 
41
- def send_file(payload, log)
54
+ def create_xml_file(payload, log)
42
55
  File.open(xml_filepath, "w") { |file| file.write(payload) }
43
56
  log.file_path = xml_filepath
44
57
  log.sent!
@@ -99,9 +112,9 @@ module Renalware
99
112
  # This allows us to do payload comparisons independent of the time they were sent.
100
113
  def time_neutral_payload
101
114
  payload
102
- .gsub(/<Stream>[^<]*<\/Stream>/, "<Stream>removed</Stream>")
115
+ .gsub(%r{<Stream>[^<]*<\/Stream>}, "<Stream>removed</Stream>")
103
116
  .gsub(/ (time|start|stop)=["'][^'"]*['"]/, "")
104
- .gsub(/<UpdatedOn>[^<]*<\/UpdatedOn>/, "<UpdatedOn>removed</UpdatedOn>")
117
+ .gsub(%r{<UpdatedOn>[^<]*<\/UpdatedOn>}, "<UpdatedOn>removed</UpdatedOn>")
105
118
  end
106
119
  end
107
120
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/ukrdc"
4
+ require "attr_extras"
5
+
6
+ module Renalware
7
+ module UKRDC
8
+ class ExportSummary
9
+ attr_accessor_initialize [
10
+ :milliseconds_taken,
11
+ :num_changed_patients,
12
+ :results,
13
+ :archive_folder,
14
+ :count_of_files_in_outgoing_folder
15
+ ]
16
+
17
+ def elapsed_time
18
+ (milliseconds_taken.to_f / 1000.0).round(2)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "renalware/ukrdc"
4
+
5
+ module Renalware
6
+ module UKRDC
7
+ class Paths
8
+ attr_reader :timestamp, :working_path
9
+
10
+ def initialize(timestamp: nil, working_path:)
11
+ raise(ArgumentError, "Invalid working_path") if working_path.blank?
12
+
13
+ @timestamp = timestamp
14
+ @timestamp ||= Time.zone.now.strftime("%Y%m%d%H%M%S%L")
15
+ @working_path = Pathname(working_path)
16
+ create_folders
17
+ end
18
+
19
+ def archive_folder
20
+ @archive_folder ||= working_path.join("archive")
21
+ end
22
+
23
+ # Name for the symlink to the latest archive folder
24
+ def latest_archive_symlink_name
25
+ @latest_archive_symlink_name ||= archive_folder.join("latest")
26
+ end
27
+
28
+ def outgoing_folder
29
+ @outgoing_folder ||= working_path.join("outgoing")
30
+ end
31
+
32
+ def timestamped_folder
33
+ @timestamped_folder ||= archive_folder.join(timestamp)
34
+ end
35
+
36
+ def timestamped_xml_folder
37
+ @timestamped_xml_folder ||= timestamped_folder.join("xml")
38
+ end
39
+
40
+ def timestamped_encrypted_folder
41
+ @timestamped_encrypted_folder ||= timestamped_folder.join("encrypted")
42
+ end
43
+
44
+ def create_symlink_to_latest_timestamped_folder_so_it_is_easier_to_eyeball
45
+ if File.exist?(latest_archive_symlink_name)
46
+ FileUtils.rm(latest_archive_symlink_name)
47
+ end
48
+ FileUtils.ln_sf(timestamped_folder, latest_archive_symlink_name)
49
+ end
50
+
51
+ def create_folders
52
+ FileUtils.mkdir_p timestamped_xml_folder
53
+ FileUtils.mkdir_p timestamped_encrypted_folder
54
+ FileUtils.mkdir_p outgoing_folder
55
+ end
56
+ end
57
+ end
58
+ end
@@ -7,6 +7,16 @@ module Renalware
7
7
  # If the optional :changed_since argument is passed we select all RPV
8
8
  # patients how have been updated since that date. Otherwise we select all RPV
9
9
  # patients who have changed since the last time they were exported.
10
+ # A patient has a sent_to_ukrdc_at datetime which is the last time the data was actually sent.
11
+ # It maybe be that something about the patient has changed and updated_at > sent_to_ukrdc_at
12
+ # however when compiled to xml there are no effective changes, for example because some notes
13
+ # were added somewhere on the patient's data but those notes don't make it into the XML. So
14
+ # therefore no effective changes and we don't send the file to UKRDC.
15
+ # However a downside of this approach is that we always find updated patients and generate
16
+ # an XML for them, in order to compare an MD5 hash of the XML with what was previously sent, and
17
+ # *sometimes* this is waste of time if the MD5 hash has not changed. However we will live with
18
+ # this for now I think. If it is a problem we can use the checked_for_ukrdc_changes_at column
19
+ # on patients.
10
20
  class PatientsQuery
11
21
  def call(changed_since: nil)
12
22
  if changed_since.present?
@@ -78,7 +78,7 @@ module Renalware
78
78
  end
79
79
 
80
80
  def contact_details?
81
- email || home_telephone || mobile_telephone
81
+ email.present? || home_telephone.present? || mobile_telephone.present?
82
82
  end
83
83
 
84
84
  def allergies
@@ -10,7 +10,7 @@ module Renalware
10
10
  class HDFPresenceValidator < ActiveModel::EachValidator
11
11
  def validate_each(_record, _attribute, value)
12
12
  hdf = value
13
- attribute_names = hdf.attributes.map(&:first)
13
+ attribute_names = [:subs_volume]
14
14
  attribute_names.each do |attribute_name|
15
15
  hdf.errors.add(attribute_name, :blank) if value[attribute_name].blank?
16
16
  end
@@ -23,12 +23,10 @@ xml.Documents do
23
23
  xml.EnteredAt do
24
24
  xml.CodingStandard "ODS"
25
25
  xml.Code letter.hospital_unit_code
26
- xml.Description ""
27
26
  end
28
27
  xml.FileType "application/pdf"
29
28
  xml.FileName letter.pdf_stateless_filename
30
29
  xml.Stream Base64.encode64(Renalware::Letters::PdfRenderer.call(letter))
31
- xml.DocumentURL
32
30
  end
33
31
  end
34
32
  end
@@ -5,12 +5,10 @@ xml = builder
5
5
  xml.Medications do
6
6
  patient.prescriptions.each do |prescription|
7
7
  xml.Medication do
8
- xml.PrescriptionNumber
9
8
  xml.FromTime prescription.prescribed_on.to_datetime
10
9
  if prescription.terminated_or_marked_for_termination?
11
10
  xml.ToTime prescription.terminated_on&.to_datetime
12
11
  end
13
- xml.OrderedBy
14
12
  xml.Route do
15
13
  xml.CodingStandard "RR22"
16
14
  xml.Code prescription.medication_route&.rr_code
@@ -31,16 +31,8 @@ xml.Treatment do
31
31
  xml.HDP03 session.document.dialysis.flow_rate
32
32
  xml.HDP04 session&.dialysate&.sodium_content
33
33
  xml.QBL05 session.access_type
34
- xml.comment! "QBL06 HD Shared Care - including as XSD requires it, but not implemented yet"
35
- xml.QBL06 ""
36
- xml.comment! "QBL07 HD Shared Care - including as XSD requires it, but not implemented yet"
37
- xml.QBL07 ""
38
34
  xml.comment! "ERF61 - defaulting to 5 if not present, as element is required"
39
35
  xml.ERF61 patient.current_registration_status_rr_code || "5" # 5= not assessed
40
36
  xml.PAT35 patient.first_seen_on
41
37
  end
42
-
43
- # xml.UpdatedOn
44
- # xml.ActionCode
45
- # xml.ExternalId
46
38
  end
@@ -40,9 +40,6 @@ xml.LabOrder do
40
40
  end
41
41
  # xml.SpecimenCollectedTime request.requested_at.iso8601
42
42
  # xml.SpecimenReceivedTime "TODO"
43
- # xml.Priority do
44
- # xml.Code "TODO: Probably n/a"
45
- # end
46
43
  xml.SpecimenSource request.description.bottle_type
47
44
  # xml.Duration "OBR:27.3 but no available in the example messages I have"
48
45
 
@@ -5,7 +5,6 @@ observation = Renalware::Pathology::ObservationPresenter.new(observation)
5
5
  observation = Renalware::UKRDC::PathologyObservationPresenter.new(observation)
6
6
 
7
7
  xml.ResultItem do
8
- # xml.ResultType
9
8
  xml.EnteredOn observation.updated_at&.iso8601
10
9
  xml.PrePost observation.pre_post(patient_is_on_hd: patient.current_modality_hd?)
11
10
  xml.ServiceId do
@@ -18,12 +17,7 @@ xml.ResultItem do
18
17
  end
19
18
  xml.Description observation.description_name
20
19
  end
21
- # xml.SubId
22
20
  xml.ResultValue observation.result
23
21
  xml.ResultValueUnits observation.measurement_unit_name
24
- # xml.AbnormalFlags
25
- # xml.Status
26
22
  xml.ObservationTime observation.observed_at&.iso8601
27
- # xml.Comments
28
- # xml.ReferenceComment
29
23
  end
@@ -5,7 +5,7 @@ table#archived_problems.manual-stripes.auto-layout
5
5
  th Description
6
6
  th.col-width-date Archived on
7
7
  th.nowrap Archived by
8
- th.col-width-date Recorded on
8
+ th.col-width-date Updated on
9
9
  th.nowrap Recorded by
10
10
 
11
11
  = content_tag(:tbody, class: "sortables", data: { rel: sort_patient_problems_path(@patient) }) do
@@ -3,8 +3,8 @@ table#current_problems.manual-stripes.auto-layout
3
3
  tr
4
4
  th.options.col-width-small
5
5
  th Description
6
- th.col-width-date Recorded on
7
- td.nowrap Recorded by
6
+ th.col-width-date Updated on
7
+ td.nowrap Updated by
8
8
  th.col-width-tiny Reorder
9
9
  = content_tag(:tbody, class: "sortables", data: { rel: sort_patient_problems_path(@patient) }) do
10
10
  - problems.each do |problem|
@@ -22,6 +22,6 @@ table#current_problems.manual-stripes.auto-layout
22
22
  - problem.notes.each do |note|
23
23
  li= note.description
24
24
  td= l problem.updated_on
25
- td= problem.created_by.full_name
25
+ td= problem.updated_by.full_name
26
26
  td.handle
27
27
  i.fas.fa-bars
@@ -0,0 +1,13 @@
1
+ p= l(Time.zone.now)
2
+ p= "XML files archived to #{summary.archive_folder} and took #{summary.elapsed_time} seconds"
3
+ p= "Count of patients changed since the last time they were sent: #{summary.num_changed_patients}"
4
+
5
+ - if summary.results.empty?
6
+ p Nothing to send
7
+ - else
8
+ ul
9
+ - summary.results.each do |key, value|
10
+ li= "#{key.to_s.humanize}: #{value}"
11
+
12
+
13
+ p= "Count of files waiting in outgoing folder: #{summary.count_of_files_in_outgoing_folder}"
@@ -0,0 +1,11 @@
1
+ <%= l(Time.zone.now) %>
2
+
3
+ XML files archived to <%=summary.archive_folder%> and took <%=summary.elapsed_time %> seconds.
4
+
5
+ Results
6
+
7
+ <% summary.results.each do |key, value| %>
8
+ <%= %"#{key.to_s.humanize}: #{value}" %>
9
+ <% end %>
10
+
11
+ Count of files waiting in outgoing folder: <%= summary.count_of_files_in_outgoing_folder %>
@@ -0,0 +1,9 @@
1
+ class AddUKRDCHelperColumnToPatients < ActiveRecord::Migration[5.2]
2
+ include MigrationHelper
3
+
4
+ def change
5
+ within_renalware_schema do
6
+ add_column :patients, :checked_for_ukrdc_changes_at, :datetime
7
+ end
8
+ end
9
+ end
@@ -9,7 +9,7 @@ select
9
9
  (select max(created_at) from delayed_jobs) as delayed_jobs_latest_entry,
10
10
  (select count(*) from delayed_jobs where created_at >= now()::date) as delayed_jobs_added_today,
11
11
  (select json_object_agg(priority, count) from (select priority, count(*) from delayed_jobs group by priority ) query) as delayed_jobs_priority_counts,
12
- (select json_object_agg(queue, count) from (select queue, count(*) from delayed_jobs group by queue ) query) as delayed_jobs_queue_counts,
12
+ (select json_object_agg(coalesce(queue, 'unset'), count) from (select queue, count(*) from delayed_jobs group by queue ) query) as delayed_jobs_queue_counts,
13
13
  (select json_object_agg(attempts, count) from (select attempts, count(*) from delayed_jobs group by attempts ) query) as delayed_jobs_attempts_counts,
14
14
  (select count(*) from feed_messages) as feed_messages_total,
15
15
  (select count(*) from feed_messages where created_at >= now()::date) as feed_messages_added_today,
@@ -18,14 +18,19 @@ class GpgEncryptFolder
18
18
  pattr_initialize [:folder!, :options!]
19
19
 
20
20
  def call
21
- Dir.glob(folder.join("xml", "*.xml")) do |file|
21
+ Dir.glob(folder.join("*.xml")) do |file|
22
22
  GpgEncryptFile.new(file: file, options: options).call
23
23
  end
24
24
  end
25
25
  end
26
26
 
27
27
  class GpgOptions
28
- attr_reader_initialize [:recipient, :keyring, :homedir, :filename_transform]
28
+ attr_reader_initialize [
29
+ :recipient,
30
+ :keyring,
31
+ :homedir,
32
+ :destination_folder,
33
+ :timestamp]
29
34
  end
30
35
 
31
36
  class GpgEncryptFile
@@ -67,8 +72,15 @@ class GpgCommand
67
72
  "--keyring #{options.keyring}"
68
73
  end
69
74
 
70
- # Calls a proc to transform the in file name into an out file name
75
+ def filename_with_extension
76
+ File.basename(file, ".xml")
77
+ end
78
+
79
+ def output_filename
80
+ "#{options.timestamp}_#{filename_with_extension}.gpg"
81
+ end
82
+
71
83
  def encrypted_filename
72
- options.filename_transform.call(file)
84
+ options.destination_folder.join(output_filename)
73
85
  end
74
86
  end
@@ -61,6 +61,9 @@ module Renalware
61
61
  config_accessor(:ukrdc_gpg_keyring) do
62
62
  ENV.fetch("UKRDC_GPG_KEYRING", Engine.root.join("config", "gpg", "renalware_test.gpg"))
63
63
  end
64
+ config_accessor(:ukrdc_working_path) do
65
+ ENV.fetch("UKRDC_WORKING_PATH", File.join("/var", "ukrdc"))
66
+ end
64
67
 
65
68
  # We override this in some tests as a means of getting wicked_Pdf to generate an HTML version
66
69
  # of the PDF so we can examine its content
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Renalware
4
- VERSION = "2.0.53"
4
+ VERSION = "2.0.54"
5
5
  end
@@ -33,10 +33,10 @@ namespace :ukrdc do
33
33
  bundle exec rake ukrdc:export\["2018-02-23","1 2"\]
34
34
  DESC
35
35
  task :export, [:changed_since, :patient_ids] => [:environment] do |_task, args|
36
- logger = Logger.new(STDOUT)
36
+ logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
37
37
  logger.level = Logger::INFO
38
38
  Rails.logger = logger
39
- Renalware::UKRDC::SendPatients.new(
39
+ Renalware::UKRDC::CreateEncryptedPatientXMLFiles.new(
40
40
  changed_since: args[:changed_since],
41
41
  patient_ids: args.fetch(:patient_ids, "").split(" ").map(&:to_i)
42
42
  ).call
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: renalware-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.53
4
+ version: 2.0.54
5
5
  platform: ruby
6
6
  authors:
7
7
  - Airslie
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-24 00:00:00.000000000 Z
11
+ date: 2018-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: active_type
@@ -1297,8 +1297,10 @@ files:
1297
1297
  - app/mailers/renalware/letters/delivery/errors.rb
1298
1298
  - app/mailers/renalware/letters/delivery/practice_mailer.rb
1299
1299
  - app/mailers/renalware/previews/report_preview.rb
1300
+ - app/mailers/renalware/previews/ukrdc_summary_preview.rb
1300
1301
  - app/mailers/renalware/previews/user_mailer_preview.rb
1301
1302
  - app/mailers/renalware/reporting/report_mailer.rb
1303
+ - app/mailers/renalware/ukrdc/summary_mailer.rb
1302
1304
  - app/models/application_record.rb
1303
1305
  - app/models/concerns/renalware/accountable.rb
1304
1306
  - app/models/concerns/renalware/broadcasting.rb
@@ -1757,9 +1759,11 @@ files:
1757
1759
  - app/models/renalware/transplants/registrations/wait_list_query.rb
1758
1760
  - app/models/renalware/transplants/version.rb
1759
1761
  - app/models/renalware/ukrdc.rb
1762
+ - app/models/renalware/ukrdc/create_encrypted_patient_xml_files.rb
1763
+ - app/models/renalware/ukrdc/create_patient_xml_file.rb
1764
+ - app/models/renalware/ukrdc/export_summary.rb
1765
+ - app/models/renalware/ukrdc/paths.rb
1760
1766
  - app/models/renalware/ukrdc/patients_query.rb
1761
- - app/models/renalware/ukrdc/send_patient.rb
1762
- - app/models/renalware/ukrdc/send_patients.rb
1763
1767
  - app/models/renalware/ukrdc/transmission_log.rb
1764
1768
  - app/models/renalware/user.rb
1765
1769
  - app/models/renalware/version.rb
@@ -2764,6 +2768,8 @@ files:
2764
2768
  - app/views/renalware/transplants/registrations/show.html.slim
2765
2769
  - app/views/renalware/transplants/wait_lists/_registration.html.slim
2766
2770
  - app/views/renalware/transplants/wait_lists/show.html.slim
2771
+ - app/views/renalware/ukrdc/summary_mailer/export_summary.html.slim
2772
+ - app/views/renalware/ukrdc/summary_mailer/export_summary.text.erb
2767
2773
  - app/views/renalware/virology/dashboards/_actions.html.slim
2768
2774
  - app/views/renalware/virology/dashboards/show.html.slim
2769
2775
  - app/views/renalware/virology/profiles/_summary.html.slim
@@ -3330,6 +3336,7 @@ files:
3330
3336
  - db/migrate/20181008144324_update_daily_letters_report_view_to_version_2.rb
3331
3337
  - db/migrate/20181008145159_create_reporting_daily_ukrdc_view.rb
3332
3338
  - db/migrate/20181013115138_update_reporting_daily_pathology_view_to_v2.rb
3339
+ - db/migrate/20181025170410_add_ukrdc_helper_column_to_patients.rb
3333
3340
  - db/seeds.rb
3334
3341
  - db/seeds/default/accesses/access_pd_catheter_insertion_techniques.csv
3335
3342
  - db/seeds/default/accesses/access_pd_catheter_insertion_techniques.rb
@@ -3713,7 +3720,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
3713
3720
  version: '0'
3714
3721
  requirements: []
3715
3722
  rubyforge_project:
3716
- rubygems_version: 2.7.4
3723
+ rubygems_version: 2.7.6
3717
3724
  signing_key:
3718
3725
  specification_version: 4
3719
3726
  summary: Renalware core functionality as a mountable engine.
@@ -1,112 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_dependency "renalware/ukrdc"
4
- require "gpg_encrypt_folder"
5
-
6
- module Renalware
7
- module UKRDC
8
- class SendPatients
9
- attr_reader :patient_ids, :changed_since, :logger, :request_uuid
10
-
11
- def initialize(changed_since: nil, patient_ids: nil, logger: nil)
12
- @changed_since = Time.zone.parse(changed_since) if changed_since.present?
13
- @patient_ids = Array(patient_ids)
14
- @logger = logger || Rails.logger
15
- @request_uuid = SecureRandom.uuid # helps group logs together
16
- end
17
-
18
- def call
19
- logger.info "Request #{request_uuid}"
20
-
21
- ms = Benchmark.ms do
22
- send_patients
23
- end
24
- print_summary(ms)
25
- rescue StandardError => exception
26
- notify_exception(exception)
27
- raise exception
28
- end
29
-
30
- private
31
-
32
- def notify_exception(exception)
33
- Engine.exception_notifier.notify(exception)
34
- end
35
-
36
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
37
- def send_patients
38
- logger.info("Generating XML files for #{patient_ids&.any? ? patient_ids : 'all'} patients")
39
- query = Renalware::UKRDC::PatientsQuery.new.call(changed_since: changed_since)
40
- query = query.where(id: Array(patient_ids)) if patient_ids.present?
41
-
42
- if changed_since.present?
43
- logger.info("#{query.count} patients have changed since #{changed_since}")
44
- else
45
- logger.info("#{query.count} patients have changed since the last send")
46
- end
47
-
48
- folder_name = within_new_folder do |dir|
49
- xml_path = dir.join("xml")
50
- query.all.find_each do |patient|
51
- SendPatient.new(
52
- patient: patient,
53
- dir: xml_path,
54
- changes_since: changed_since,
55
- request_uuid: request_uuid,
56
- logger: logger
57
- ).call
58
- end
59
- encrypt_xml_files_in(dir)
60
- end
61
- logger.info("Files saved to #{folder_name}")
62
- end
63
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
64
-
65
- def timestamp
66
- Time.zone.now.strftime("%Y%m%d%H%M%S%L")
67
- end
68
-
69
- def within_new_folder
70
- dir = Pathname(File.join("/var", "ukrdc", timestamp))
71
- FileUtils.mkdir_p File.join(dir, "xml")
72
- FileUtils.mkdir_p File.join(dir, "encrypted")
73
- yield dir if block_given?
74
- dir
75
- end
76
-
77
- def filepath_for(patient, dir, sub_folder)
78
- raise(ArgumentError, "Patient has no ukrdc_external_id") if patient.ukrdc_external_id.blank?
79
-
80
- filename = "#{patient.ukrdc_external_id}.xml"
81
- File.join(dir, sub_folder.to_s, filename)
82
- end
83
-
84
- def print_summary(ms)
85
- logger.info "*** Summary ***"
86
- logger.info "Took #{ms.to_i / 1000} seconds"
87
- results = TransmissionLog.where(request_uuid: request_uuid).group(:status).count(:status)
88
- results.to_h.map{ |key, value| logger.info("#{key}: #{value}") }
89
- end
90
-
91
- def encrypt_xml_files_in(dir)
92
- logger.info "Encrypting..."
93
- GpgEncryptFolder.new(folder: dir, options: gpg_options).call
94
- end
95
-
96
- def config
97
- Renalware.config
98
- end
99
-
100
- def gpg_options
101
- GpgOptions.new(
102
- recipient: config.ukrdc_gpg_recipient,
103
- keyring: config.ukrdc_gpg_keyring,
104
- homedir: config.ukrdc_gpg_homedir,
105
- filename_transform: lambda do |filename|
106
- filename.to_s.gsub("/xml/", "/encrypted/").gsub(".xml", ".gpg")
107
- end
108
- )
109
- end
110
- end
111
- end
112
- end