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.
- 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
|