Skip to content

Commit

Permalink
Merge pull request #4275 from sanger/y24-234-update-missing-event-his…
Browse files Browse the repository at this point in the history
…tory-records

Y24 234 update missing event history records
  • Loading branch information
seenanair authored Aug 21, 2024
2 parents d4e3dd3 + a9a6713 commit ef12d49
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 0 deletions.
83 changes: 83 additions & 0 deletions lib/tasks/add_missing_asset_audit_records.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# frozen_string_literal: true

# Rails task to add missing asset audit records from a CSV file
namespace :asset_audit do
# This rake task is used to add missing asset audit records from a CSV file.
# If there are any errors in the CSV file, no records will be inserted.
# Usage: rails asset_audit:add_missing_records['/path/to/file.csv']
# The CSV file should have the following headers:
# barcode, message, created_by, created_at
# The message column should have one of the following values:
# 'Destroying location', 'Destroying labware'

desc 'Add missing asset audit records'
task :add_missing_records, [:file_path] => :environment do |_, args|
required_csv_headers = %w[barcode message created_by created_at]

# Check if the file path is provided and valid
file_path = args[:file_path] == 'nil' ? nil : args[:file_path]
raise 'Please provide a valid file path' if file_path.nil? || !File.exist?(file_path)

# Read the CSV file and validate the headers
begin
csv_data = CSV.read(file_path, headers: true)
rescue StandardError => e
raise "Failed to read CSV file: #{e.message}"
end

unless csv_data.headers.length == required_csv_headers.length
raise 'Failed to read CSV file: Invalid number of header columns.'
end

# Process the CSV data and create asset audit records data array to insert
asset_audit_data = []
csv_data.each do |row|
missing_columns = required_csv_headers.select { |header| row[header].nil? }

# Check if any of the required columns are missing
raise 'Failed to read CSV file: Missing columns.' unless missing_columns.empty?

# Find the asset by barcode
asset = Labware.find_by_barcode(row['barcode'].strip)
raise "Asset with barcode #{row['barcode']} not found." if asset.nil?

# Check if the message is valid
key =
case row['message']
when 'Destroying location'
'destroy_location'
when 'Destroying labware'
'destroy_labware'
end

raise "Invalid message for asset with barcode #{row['barcode']}." if key.nil?

# Create the asset audit record data
data = {
asset_id: asset.id,
created_by: row['created_by'],
created_at: row['created_at'],
message: "Process '#{row['message']}' performed on instrument Destroying instrument",
key: key
}
asset_audit_data << data
end

# Insert the asset audit records
ActiveRecord::Base.transaction do
asset_audit_data.each do |data|
AssetAudit.create!(
message: data[:message],
created_by: data[:created_by],
created_at: data[:created_at],
asset_id: data[:asset_id],
key: data[:key]
)
end
puts 'All records successfully inserted.'
rescue ActiveRecord::ActiveRecordError => e
puts "Failed to insert records: #{e.message}"
raise ActiveRecord::Rollback
end
end
end
2 changes: 2 additions & 0 deletions spec/data/asset_audits/bad_format.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
barcode,message,created_by,created_at
SQPD-9002;"Destroying location;User1,2021-01-01 12:00:00
2 changes: 2 additions & 0 deletions spec/data/asset_audits/invalid_message.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
barcode,message,created_by,created_at
SQPD-1,Destroying test,User1,2021-01-01 12:00:00
2 changes: 2 additions & 0 deletions spec/data/asset_audits/missing_column.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
barcode,message,created_by,created_at
SQPD-1,Destroying location,2021-01-01 12:00:00
2 changes: 2 additions & 0 deletions spec/data/asset_audits/missing_header.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
barcode,created_by,created_at
SQPD-1,Destroying location,2021-01-01 12:00:00
3 changes: 3 additions & 0 deletions spec/data/asset_audits/valid_data.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
barcode,message,created_by,created_at
SQPD-1,Destroying location,User1,2021-01-01 12:00:00
SQPD-2,Destroying labware,User2,2021-01-02 12:00:00
136 changes: 136 additions & 0 deletions spec/lib/add_missing_asset_audit_records_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe 'asset_audit:add_missing_records', type: :task do
before do
Rake.application.rake_require 'tasks/add_missing_asset_audit_records'
Rake::Task.define_task(:environment)
end

context 'when an invalid file path is given' do
let(:run_rake_task) do
Rake::Task['asset_audit:add_missing_records'].reenable
Rake.application.invoke_task('asset_audit:add_missing_records[nil]')
end

it 'outputs an error message and returns' do
expect { run_rake_task }.to raise_error(RuntimeError, /Please provide a valid file path/)
end
end

describe 'invalid csv file' do
context 'when csv has a bad format' do
let(:file_path) { 'spec/data/asset_audits/bad_format.csv' }
let(:run_rake_task) do
Rake::Task['asset_audit:add_missing_records'].reenable
Rake.application.invoke_task("asset_audit:add_missing_records[#{file_path}]")
end

it 'outputs an error message and return' do
expect { run_rake_task }.to raise_error(RuntimeError, /Failed to read CSV file/)
end
end

context 'when csv columns are mising' do
let(:file_path) { 'spec/data/asset_audits/missing_column.csv' }
let(:run_rake_task) do
Rake::Task['asset_audit:add_missing_records'].reenable
Rake.application.invoke_task("asset_audit:add_missing_records[#{file_path}]")
end

it 'outputs an error message and return' do
expect { run_rake_task }.to raise_error(RuntimeError, 'Failed to read CSV file: Missing columns.')
end
end

context 'when csv with no header given' do
let(:file_path) { 'spec/data/asset_audits/missing_header.csv' }
let(:run_rake_task) do
Rake::Task['asset_audit:add_missing_records'].reenable
Rake.application.invoke_task("asset_audit:add_missing_records[#{file_path}]")
end

it 'outputs an error message and return' do
expect { run_rake_task }.to raise_error(/Failed to read CSV file: Invalid number of header columns./)
end
end
end

describe 'valid csv file' do
context 'when asset with barcode is not found' do
let(:file_path) { 'spec/data/asset_audits/valid_data.csv' }
let(:run_rake_task) do
Rake::Task['asset_audit:add_missing_records'].reenable
Rake.application.invoke_task("asset_audit:add_missing_records[#{file_path}]")
end

it 'does not add records if there is any invalid data' do
create(:plate, barcode: 'SQPD-1')

expect { run_rake_task }.to raise_error(RuntimeError, 'Asset with barcode SQPD-2 not found.')
end
end

context 'when message column is invalid' do
let(:file_path) { 'spec/data/asset_audits/invalid_message.csv' }
let(:run_rake_task) do
Rake::Task['asset_audit:add_missing_records'].reenable
Rake.application.invoke_task("asset_audit:add_missing_records[#{file_path}]")
end

it 'does not add records if there is any invalid data' do
create(:plate, barcode: 'SQPD-1')

expect { run_rake_task }.to raise_error(RuntimeError, 'Invalid message for asset with barcode SQPD-1.')
end
end

context 'when all data is good' do
let(:file_path) { 'spec/data/asset_audits/valid_data.csv' }
let(:run_rake_task) do
Rake::Task['asset_audit:add_missing_records'].reenable
Rake.application.invoke_task("asset_audit:add_missing_records[#{file_path}]")
end

it 'adds missing asset audit records' do
plate1 = create(:plate, barcode: 'SQPD-1')
plate2 = create(:plate, barcode: 'SQPD-2')

expect { run_rake_task }.to output(/All records successfully inserted./).to_stdout

expect(
AssetAudit.where(
asset_id: plate1.id,
key: 'destroy_location',
message: "Process 'Destroying location' performed on instrument Destroying instrument"
)
).to exist

expect(
AssetAudit.where(
asset_id: plate2.id,
key: 'destroy_labware',
message: "Process 'Destroying labware' performed on instrument Destroying instrument"
)
).to exist
end
end

context 'when there is a failed transaction' do
let(:file_path) { 'spec/data/asset_audits/valid_data.csv' }
let(:run_rake_task) do
Rake::Task['asset_audit:add_missing_records'].reenable
Rake.application.invoke_task("asset_audit:add_missing_records[#{file_path}]")
end

it 'rolls back transaction when there is an error in inserting records' do
create(:plate, barcode: 'SQPD-1')
create(:plate, barcode: 'SQPD-2')
allow(AssetAudit).to receive(:create!).and_raise(ActiveRecord::ActiveRecordError, 'Test error')

expect { run_rake_task }.to output(/Failed to insert records: Test error/).to_stdout
end
end
end
end

0 comments on commit ef12d49

Please sign in to comment.