Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Y24-382: Add UAT action to generate plate volumes #4529

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions app/uat_actions/uat_actions/generate_plate_volumes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# frozen_string_literal: true

# Will generate volumes for a given plate
class UatActions::GeneratePlateVolumes < UatActions
self.title = 'Generate volumes for a plate'
self.description = 'Generate a set of randomised volumes for a plate.'
self.category = :quality_control

form_field :plate_barcode,
:text_field,
label: 'Plate barcode',
help:
'Enter the barcode of the plate for which you want to add volumes. ' \
'NB. only well containing aliquots will have volumes set.'
form_field :minimum_volume,
:number_field,
label: 'Minimum volume (µl)',
help: 'The minimum volume the wells should have.',
options: {
minimum: 0
}
form_field :maximum_volume,
:number_field,
label: 'Maximum volume (µl)',
help: 'The maximum volume the wells should have.',
options: {
minimum: 0
}

#
# Returns a default copy of the UatAction which will be used to fill in the form, with values
# for the units, and min and max volumes.
#
# @return [UatActions::GeneratePlateVolumes] A default object for rendering a form
def self.default
new(minimum_volume: 0, maximum_volume: 100)
end

validates :plate_barcode, presence: { message: 'could not be found' }
validates :minimum_volume, numericality: { only_integer: false }
validates :maximum_volume, numericality: { greater_than: 0, only_integer: false }
validate :maximum_greater_than_minimum

def perform
qc_assay_results = construct_qc_assay
report['number_well_volumes_written'] = qc_assay_results[:num_wells_written]
qc_assay_results[:qc_assay_success]
end

private

def maximum_greater_than_minimum
return true if max_vol > min_vol

Check warning on line 53 in app/uat_actions/uat_actions/generate_plate_volumes.rb

View check run for this annotation

Codecov / codecov/patch

app/uat_actions/uat_actions/generate_plate_volumes.rb#L53

Added line #L53 was not covered by tests

errors.add(:maximum_volume, 'needs to be greater than minimum volume')
false

Check warning on line 56 in app/uat_actions/uat_actions/generate_plate_volumes.rb

View check run for this annotation

Codecov / codecov/patch

app/uat_actions/uat_actions/generate_plate_volumes.rb#L55-L56

Added lines #L55 - L56 were not covered by tests
end

def labware
@labware ||= Plate.find_by_barcode(plate_barcode.strip)
end

def key
@key ||= 'volume'
end

def min_vol
@min_vol ||= minimum_volume.to_f
end

def max_vol
@max_vol ||= maximum_volume.to_f
end

def create_random_volume
value = (rand * (max_vol - min_vol)) + min_vol
format('%.3f', value)
end

def construct_qc_assay
qc_assay = QcAssay.new
num_wells_written = 0

labware.wells.each do |well|
next if well.aliquots.empty?

QcResult.create!(
asset: well,
key: key,
value: create_random_volume,
units: 'µl',
assay_type: 'UAT_Testing',
assay_version: 'Binning',
qc_assay: qc_assay
)
num_wells_written += 1
end
qc_assay_success = qc_assay.save
{ qc_assay_success:, num_wells_written: }
end
end
56 changes: 56 additions & 0 deletions spec/uat_actions/generate_plate_volumes_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# frozen_string_literal: true

require 'rails_helper'

describe UatActions::GeneratePlateVolumes do
context 'with valid options' do
let(:plate) { create(:plate_with_untagged_wells, sample_count: 3) }
let(:uat_action) { described_class.new(parameters) }
let!(:performed_action) { uat_action.perform }
let(:report) do
# A report is a hash of key value pairs which get returned to the user.
# It should include information such as barcodes and identifiers
{ 'number_well_volumes_written' => 3 }
end

let(:parameters) { { plate_barcode: plate.barcodes.first.barcode, minimum_volume: 0, maximum_volume: 30 } }

it 'can be performed' do
expect(performed_action).to be true
end

it 'generates the correct report' do
expect(uat_action.report).to eq report
end

it 'creates the correct number of QC results' do
expect(plate.wells.map(&:qc_results).size).to eq 3
end

it 'sets the correct assay type for the first QC result' do
expect(plate.wells.first.qc_results.first.assay_type).to eq 'UAT_Testing'
end

it 'sets the volumes to be within the specified range' do
expect(plate.wells.map { |well| well.qc_results.first.value.to_f }).to all(be_between(0, 30))
end
end

context 'with default options' do
it 'returns an instance of described_class' do
expect(described_class.default).to be_a described_class
end

it 'has a nil plate_barcode' do
expect(described_class.default.plate_barcode).to be_nil
end

it 'has a minimum_volume of 0' do
expect(described_class.default.minimum_volume).to eq 0
end

it 'has a maximum_volume of 100' do
expect(described_class.default.maximum_volume).to eq 100
end
end
end
Loading