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.
- checksums.yaml +4 -4
- data/app/documents/renalware/hd/session_document.rb +1 -1
- data/app/inputs/clock_picker_input.rb +1 -1
- data/app/inputs/date_picker_input.rb +1 -1
- data/app/inputs/simple_time_input.rb +1 -1
- data/app/mailers/renalware/previews/report_preview.rb +1 -1
- data/app/mailers/renalware/previews/ukrdc_summary_preview.rb +20 -0
- data/app/mailers/renalware/previews/user_mailer_preview.rb +1 -0
- data/app/mailers/renalware/ukrdc/summary_mailer.rb +19 -0
- data/app/models/renalware/ukrdc/create_encrypted_patient_xml_files.rb +136 -0
- data/app/models/renalware/ukrdc/{send_patient.rb → create_patient_xml_file.rb} +24 -11
- data/app/models/renalware/ukrdc/export_summary.rb +22 -0
- data/app/models/renalware/ukrdc/paths.rb +58 -0
- data/app/models/renalware/ukrdc/patients_query.rb +10 -0
- data/app/presenters/renalware/ukrdc/patient_presenter.rb +1 -1
- data/app/validators/renalware/patients/hdf_presence_validator.rb +1 -1
- data/app/views/renalware/api/ukrdc/patients/_documents.xml.builder +0 -2
- data/app/views/renalware/api/ukrdc/patients/_medications.xml.builder +0 -2
- data/app/views/renalware/api/ukrdc/patients/encounters/_hd_session.xml.builder +0 -8
- data/app/views/renalware/api/ukrdc/patients/lab_orders/_lab_order.xml.builder +0 -3
- data/app/views/renalware/api/ukrdc/patients/lab_orders/_result_item.xml.builder +0 -6
- data/app/views/renalware/problems/problems/_archived_table.html.slim +1 -1
- data/app/views/renalware/problems/problems/_current_table.html.slim +3 -3
- data/app/views/renalware/ukrdc/summary_mailer/export_summary.html.slim +13 -0
- data/app/views/renalware/ukrdc/summary_mailer/export_summary.text.erb +11 -0
- data/db/migrate/20181025170410_add_ukrdc_helper_column_to_patients.rb +9 -0
- data/db/views/reporting_daily_pathology_v02.sql +1 -1
- data/lib/gpg_encrypt_folder.rb +16 -4
- data/lib/renalware/configuration.rb +3 -0
- data/lib/renalware/version.rb +1 -1
- data/lib/tasks/ukrdc.rake +2 -2
- metadata +12 -5
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 850fb8d5f1245a54162eaa4fb6e075f272563bac0729d30d4209fbf34c3fd378
|
4
|
+
data.tar.gz: 90e3476b1530fb2d49447bd9a882e456bcdabe533004be2d060993b361b6c076
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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,
|
@@ -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='
|
33
|
+
"<span class='prefix'><i class='far fa-calendar'></i></span>".html_safe
|
34
34
|
end
|
35
35
|
|
36
36
|
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
|
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
|
@@ -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
|
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
|
-
|
20
|
-
|
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
|
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(
|
115
|
+
.gsub(%r{<Stream>[^<]*<\/Stream>}, "<Stream>removed</Stream>")
|
103
116
|
.gsub(/ (time|start|stop)=["'][^'"]*['"]/, "")
|
104
|
-
.gsub(
|
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?
|
@@ -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 =
|
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
|
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
|
7
|
-
td.nowrap
|
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.
|
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 %>
|
@@ -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,
|
data/lib/gpg_encrypt_folder.rb
CHANGED
@@ -18,14 +18,19 @@ class GpgEncryptFolder
|
|
18
18
|
pattr_initialize [:folder!, :options!]
|
19
19
|
|
20
20
|
def call
|
21
|
-
Dir.glob(folder.join("
|
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 [
|
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
|
-
|
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.
|
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
|
data/lib/renalware/version.rb
CHANGED
data/lib/tasks/ukrdc.rake
CHANGED
@@ -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::
|
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.
|
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-
|
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.
|
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
|