Skip to content

Commit

Permalink
Merge pull request #1433 from sanger/dpl-812-fluidx-tube-rack-data-re…
Browse files Browse the repository at this point in the history
…port

DPL-812: FluidX tube rack data report
  • Loading branch information
StephenHulme authored Oct 31, 2023
2 parents 943a722 + 613c3bc commit e1583b1
Show file tree
Hide file tree
Showing 13 changed files with 843 additions and 9 deletions.
4 changes: 4 additions & 0 deletions app/sequencescape/sequencescape/api/v2/well.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ def latest_live_cell_count
latest_qc(key: 'live_cell_count', units: 'cells/ml')
end

def latest_cell_viability
latest_qc(key: 'viability', units: '%')
end

def latest_qc(key:, units:)
qc_results.to_a # Convert to array to resolve any api queries. Otherwise select fails to work.
.select { |qc| qc.key.casecmp(key).zero? }
Expand Down
81 changes: 81 additions & 0 deletions app/views/exports/pbmc_bank_tubes_content_report.csv.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<%# This is a report of the contents of the tubes in the PBMC Bank plate %>
<%# Based on app/views/exports/hamilton_lrc_pbmc_bank_to_lrc_bank_seq_and_spare.csv.erb %>
<%= CSV.generate_line ['Workflow', @workflow], row_sep: '' %>

<%=
CSV.generate_line [
'Well name',
'Donor ID',
'Stock barcode',
'FluidX barcode',
'Extraction and freeze date',
'Sequencing or contingency',
'Cell count (cells/ml)',
'Viability (%)',
'Volume (µl)',
'Study name',
'Collection site'
],
row_sep: ''
%>
<%
rows_array = []
@plate.wells_in_columns.each do |source_well|
# skip if well empty (not all wells have samples in them)
next if source_well.empty?

# skip if well not in passed state
next unless source_well.passed?

# skip if no downstream tubes for this well (not set up children in Limber yet)
next if source_well.downstream_tubes.empty?

child_tube_v1 = source_well.downstream_tubes.last
child_tube_v2 =
Sequencescape::Api::V2.tube_with_custom_includes(
'custom_metadatum_collection',
nil,
barcode: child_tube_v1.labware_barcode.machine
)

next unless child_tube_v2 && child_tube_v2.custom_metadatum_collection

# get tube metadata from child_tube
metadata = child_tube_v2.custom_metadatum_collection.metadata

next unless metadata

tube_rack_barcode = metadata['tube_rack_barcode']
tube_rack_position = metadata['tube_rack_position']

next unless tube_rack_barcode && tube_rack_position

sample_uuid = source_well.aliquots.first.sample.uuid
source_well_posn = source_well.position['name']

sample_metadata = source_well.aliquots.first.sample.sample_metadata

# donor_id is sourced from the sample metadata provided by Sequencescape
donor_id = sample_metadata.respond_to?(:donor_id) ? sample_metadata.donor_id : nils
child_tube_purpose = child_tube_v2.purpose.name
destination = ((child_tube_purpose == 'LRC Blood Seq') ? 'Sequencing' : 'Contingency')
volume = 135

rows_array << [
source_well.name, # Well name
donor_id, # Donor ID
@ancestor_tubes[sample_uuid].labware_barcode.human, # LRC Blood Vac purpose barcode
child_tube_v2.barcode, # FluidX barcode
child_tube_v2.created_at, # Extraction and freeze date
destination, # Sequencing or contingency
source_well.latest_live_cell_count.value, # Cell count
source_well.latest_cell_viability.value, # Viability
volume, # Volume
source_well.aliquots.first.study.name, # Study name
source_well.aliquots.first.sample.sample_metadata&.collected_by # Collection site
]
end
%>
<% rows_array.sort_by{ |a| WellHelpers.well_coordinate(a[0]) }.each do |row| %>
<%= CSV.generate_line row, row_sep: '' %>
<% end %>
9 changes: 9 additions & 0 deletions config/exports/exports.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,12 @@ hamilton_lrc_blood_bank_to_lrc_pbmc_bank:
- wells.transfer_requests_as_target.source_asset
ancestor_tube_purpose: LRC Blood Vac
ancestor_purpose: LRC Blood Bank
pbmc_bank_tubes_content_report:
csv: pbmc_bank_tubes_content_report
workflow: scRNA Core PBMC Bank
plate_includes:
- wells.downstream_tubes
- wells.aliquots
- wells.aliquots.sample
- wells.aliquots.sample.sample_metadata
ancestor_tube_purpose: LRC Blood Vac
2 changes: 2 additions & 0 deletions config/purposes/scrna_core_cell_extraction.wip.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ LRC PBMC Bank:
id: hamilton_lrc_blood_bank_to_lrc_pbmc_bank
- name: 'Download Hamilton LRC PBMC Bank plate to LRC Bank Seq and Spare tubes CSV'
id: hamilton_lrc_pbmc_bank_to_lrc_bank_seq_and_spare
- name: 'Download PBMC Bank Tubes Content Report'
id: pbmc_bank_tubes_content_report
:qc_thresholds:
viability:
units: '%'
Expand Down
7 changes: 6 additions & 1 deletion spec/factories/plate_factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
# associated request. This allows us to over-ride that
aliquots_without_requests { 0 }

study { create :v2_study, name: 'Plate Study' }
project { create :v2_project, name: 'Plate Project' }

# Constructs the wells for the plate. Constructs
# well_count wells using the factory specified in well_factory
# Sets requests on wells by pulling them off the outer_request array
Expand All @@ -73,7 +76,9 @@
outer_request: outer_requests[i],
downstream_tubes: transfer_targets[location],
uuid: well_uuid_result % location,
aliquot_count: outer_requests[i] ? 1 : aliquots_without_requests
aliquot_count: outer_requests[i] ? 1 : aliquots_without_requests,
study: study,
project: project
end
end

Expand Down
2 changes: 2 additions & 0 deletions spec/factories/project_factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
factory :v2_project, class: Sequencescape::Api::V2::Project do
skip_create

id

name { 'Test Project' }

uuid { SecureRandom.uuid }
Expand Down
1 change: 1 addition & 0 deletions spec/factories/sample_factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
collected_by { 'Sanger' }
cohort { 'Cohort' }
sample_description { 'Description' }
sequence(:donor_id) { |i| "donor#{i}" }
end

factory :v2_sample_metadata_for_mbrave, class: Sequencescape::Api::V2::SampleMetadata do
Expand Down
2 changes: 2 additions & 0 deletions spec/factories/study_factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
factory :v2_study, class: Sequencescape::Api::V2::Study do
skip_create

id

name { 'Test Study' }

uuid { SecureRandom.uuid }
Expand Down
20 changes: 13 additions & 7 deletions spec/factories/well_factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,15 @@
# Use the stock well factory if you want the request comming out of the well
outer_request { create request_factory, state: library_state }

study { create :v2_study, name: 'Well Study' }
project { create :v2_project, name: 'Well Project' }

# The factory to use for aliquots
aliquot_factory { :v2_aliquot }
aliquots do
# Conditional to avoid generating requests when not required
if aliquot_count > 0
create_list aliquot_factory, aliquot_count, outer_request: outer_request
create_list aliquot_factory, aliquot_count, outer_request: outer_request, study: study, project: project
else
[]
end
Expand Down Expand Up @@ -256,8 +259,8 @@
# Alias for request: The request set on the aliquot itself
outer_request { create :library_request, state: library_state }
well_location { 'A1' }
study_id { 1 }
project_id { 1 }
study { create :v2_study, name: 'Test Aliquot Study' }
project { create :v2_project, name: 'Test Aliquot Project' }
sample_attributes { {} }
end

Expand All @@ -271,16 +274,19 @@
request { outer_request }

after(:build) do |aliquot, evaluator|
aliquot._cached_relationship(:request) { evaluator.request }
aliquot._cached_relationship(:sample) { evaluator.sample }
# Set up relationships downstream
Sequencescape::Api::V2::Aliquot.associations.each do |association|
aliquot._cached_relationship(association.attr_name) { evaluator.send(association.attr_name) }
end

aliquot.relationships.study = {
'links' => {
'self' => "http://localhost:3000/api/v2/aliquots/#{aliquot.id}/relationships/study",
'related' => "http://localhost:3000/api/v2/aliquots/#{aliquot.id}/study"
},
'data' => {
'type' => 'studies',
'id' => evaluator.study_id.to_s
'id' => evaluator.study.id.to_s
}
}
aliquot.relationships.project = {
Expand All @@ -290,7 +296,7 @@
},
'data' => {
'type' => 'projects',
'id' => evaluator.project_id.to_s
'id' => evaluator.project.id.to_s
}
}
end
Expand Down
84 changes: 84 additions & 0 deletions spec/factory_outputs/plate_factory_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,87 @@
expect(JSON.parse(subject)).to include_json(JSON.parse(json_content))
end
end

RSpec.describe 'v2_plate' do
context 'with specified study and project at plate level' do
subject { create(:v2_plate, aliquots_without_requests: 1, study: study, project: project) }

# study
let(:study_uuid) { SecureRandom.uuid }
let(:study) { create(:v2_study, name: 'Provided Study', uuid: study_uuid) }

# project
let(:project_uuid) { SecureRandom.uuid }
let(:project) { create(:v2_project, name: 'Provided Project', uuid: project_uuid) }

describe 'first aliquot' do
let(:first_aliquot) { subject.wells.first.aliquots.first }

it 'should be a version 2 aliquot' do
expect(first_aliquot.class).to eq(Sequencescape::Api::V2::Aliquot)
end

it 'should have a valid study' do
expect(first_aliquot.study).to be_kind_of(Sequencescape::Api::V2::Study)
end
it 'should have a valid study uuid' do
expect(first_aliquot.study.uuid).to eq(study_uuid)
end
it 'should have a valid study name' do
expect(first_aliquot.study.name).to eq('Provided Study')
end

it 'should have a valid project' do
expect(first_aliquot.project).to be_kind_of(Sequencescape::Api::V2::Project)
end
it 'should have a valid project uuid' do
expect(first_aliquot.project.uuid).to eq(project_uuid)
end
it 'should have a valid project name' do
expect(first_aliquot.project.name).to eq('Provided Project')
end
end
end
end

RSpec.describe 'v2_plate_for_submission' do
context 'with specified study and project at plate level' do
subject { create(:v2_plate_for_submission, aliquots_without_requests: 1, study: study, project: project) }

# study
let(:study_uuid) { SecureRandom.uuid }
let(:study) { create(:v2_study, name: 'Provided Study', uuid: study_uuid) }

# project
let(:project_uuid) { SecureRandom.uuid }
let(:project) { create(:v2_project, name: 'Provided Project', uuid: project_uuid) }

describe 'first aliquot' do
let(:first_aliquot) { subject.wells.first.aliquots.first }

it 'should be a version 2 aliquot' do
expect(first_aliquot.class).to eq(Sequencescape::Api::V2::Aliquot)
end

it 'should have a valid study' do
expect(first_aliquot.study).to be_kind_of(Sequencescape::Api::V2::Study)
end
it 'should have a valid study uuid' do
expect(first_aliquot.study.uuid).to eq(study_uuid)
end
it 'should have a valid study name' do
expect(first_aliquot.study.name).to eq('Provided Study')
end

it 'should have a valid project' do
expect(first_aliquot.project).to be_kind_of(Sequencescape::Api::V2::Project)
end
it 'should have a valid project uuid' do
expect(first_aliquot.project.uuid).to eq(project_uuid)
end
it 'should have a valid project name' do
expect(first_aliquot.project.name).to eq('Provided Project')
end
end
end
end
Loading

0 comments on commit e1583b1

Please sign in to comment.