# frozen_string_literal: true
require_dependency "renalware/ukrdc"
require "attr_extras"
module Renalware
module UKRDC
class SendPatient
pattr_initialize [:patient!, :dir!, :request_uuid!, :renderer, :changes_since, :logger]
def call
UKRDC::TransmissionLog.with_logging(patient, request_uuid) do |log|
logger.info " Patient #{patient.to_param}"
xml_payload = build_payload(log)
if xml_payload_same_as_last_sent_payload?(xml_payload)
logger.info " skipping as no change in XML file"
log.unsent_no_change_since_last_send!
else
send_file(xml_payload, log)
# Important we use update_column here so we don't trigger updated_at to change
# on the patient, which affects the results of PatientsQuery next time.
patient.update_column(:sent_to_ukrdc_at, Time.zone.now)
logger.info(
" sending file and setting patient.sent_to_ukrdc_at = #{patient.sent_to_ukrdc_at}"
)
end
logger.info " Status: #{log.status}"
end
end
private
def logger
@logger ||= Rails.logger
end
def xml_payload_same_as_last_sent_payload?(payload)
payload.to_md5_hash == last_sent_transmission_log.payload_hash
end
def send_file(payload, log)
File.open(xml_filepath, "w") { |file| file.write(payload) }
log.file_path = xml_filepath
log.sent!
end
def last_sent_transmission_log
@last_sent_transmission_log ||= begin
TransmissionLog.where(patient: patient, status: :sent).ordered.last || NullObject.instance
end
end
def build_payload(log)
Payload.new(render_xml_for(patient)).tap do |payload|
log.payload = payload.to_s
log.payload_hash = payload.to_md5_hash
end
end
def render_xml_for(patient)
renderer.render(
"renalware/api/ukrdc/patients/show",
locals: { patient: presenter_for(patient) }
)
end
def presenter_for(patient)
Renalware::UKRDC::PatientPresenter.new(
patient,
changes_since: changes_since
)
end
# Note a test might have passed in a mock renderer
def renderer
@renderer ||= Renalware::API::UKRDC::PatientsController.renderer
end
def xml_filepath
raise(ArgumentError, "Patient has no ukrdc_external_id") if patient.ukrdc_external_id.blank?
filename = "#{patient.ukrdc_external_id}.xml"
File.join(dir, filename)
end
class Payload
pattr_initialize :payload
delegate :to_s, to: :payload
def to_md5_hash
@to_md5_hash ||= Digest::MD5.hexdigest(time_neutral_payload)
end
# Remove the time elements from SendingFacility
# e.g.
#
# becomes
#
# This allows us to do payload comparisons independent of the time they were sent.
def time_neutral_payload
payload
.gsub(/[^<]*<\/Stream>/, "removed")
.gsub(/ (time|start|stop)=["'][^'"]*['"]/, '')
.gsub(/[^<]*<\/UpdatedOn>/, "removed")
end
end
end
end
end