renalware-core 2.0.53 → 2.0.54

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