Skip to content
Merged
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
34 changes: 22 additions & 12 deletions app/controllers/ultima_sample_sheet/sample_sheet_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ class Generator # rubocop:disable Metrics/ClassLength
study_id
].freeze
NUM_COLUMS = SAMPLES_HEADERS.size
# The names of the Ultima tag groups are mapped to the index numbers for
# the Barcode_Plate_Num column, i.e. 1 or 2. The number is also used for
# determining the consistent starting index number for the
# Index_Barcode_Num column, i.e. Z0001 or Z097.
ULTIMA_TAG_GROUPS = {
'Ultima P1' => 1,
'Ultima P2' => 2
}.freeze

# Initializes the generator with the given batch.
# @param batch [UltimaSequencingBatch] the batch to generate sample sheets for
Expand Down Expand Up @@ -115,7 +123,7 @@ def add_global_section(csv, _request)
def add_samples_section(csv, request)
csv << pad(SAMPLES_TITLE)
csv << pad(SAMPLES_HEADERS)
request.asset.aliquots.each do |aliquot|
request.asset.aliquots.sort_by(&:id).each do |aliquot|
csv << [
sample_id_for(aliquot),
library_name_for(aliquot),
Expand Down Expand Up @@ -187,28 +195,30 @@ def study_id_for(aliquot)
aliquot.study_id.to_s
end

# Returns a mapping of tags to their respective 1-based index numbers.
# This sorts the tags by their tag group ID and map ID to ensure consistent ordering.
# Returns a mapping of all Ultima tags to their respective 1-based index
# numbers. This sorts the tags by their tag group ID and map ID to ensure
# consistent ordering. The index numbers run across all Ultima tag groups,
# i.e. the index is 1 for the first tag in the first tag group and 97 for
# the first tag in the second tag group.
# @return [Hash{Tag => Integer}] mapping of tags to index numbers
def tag_index_map
@tag_index_map ||= begin
tags = batch_tag_groups.flat_map { |tg| tg.tags.sort_by(&:map_id) }
tags = ultima_tag_groups.flat_map { |tg| tg.tags.sort_by(&:map_id) }
tags.each_with_index.to_h { |tag, i| [tag, i + 1] }
end
end

# Returns a mapping of tag groups to 1-based index numbers.
# Returns a mapping of all Ultima tag groups to 1-based index numbers.
# This indexes the tag groups as given in the ULTIMA_TAG_GROUPS hash.
# @return [Hash{TagGroup => Integer}] mapping of tag groups to index numbers
def tag_group_index_map
@tag_group_index_map ||= batch_tag_groups.each_with_index.to_h { |tg, i| [tg, i + 1] }
@tag_group_index_map ||= ultima_tag_groups.index_with { |tg| ULTIMA_TAG_GROUPS[tg.name] }
end

# Returns all unique tag groups used in the batch.
# This sorts the tag groups by their ID to ensure consistent ordering.
# @return [Array<TagGroup>] the tag groups of the batch's requests
def batch_tag_groups
@batch_tag_groups ||= batch_requests
.flat_map { |request| request.asset.aliquots.map { |aliquot| aliquot.tag.tag_group } }.uniq.sort_by(&:id)
# Returns all unique tag groups used for Ultima sequencing from database.
# @return [Array<TagGroup>] the tag groups used for Ultima sequencing
def ultima_tag_groups
@ultima_tag_groups ||= TagGroup.where(name: ULTIMA_TAG_GROUPS.keys)
end

# Returns the requests associated with the batch.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,28 @@
# 6,Sample6,Z0099,TCAG,2,C1,native,6
#
# rubocop:enable Layout/LineLength
# rubocop:disable RSpec/MultipleExpectations, RSpec/ExampleLength
RSpec.describe UltimaSampleSheet::SampleSheetGenerator do
# Eagerly create the global section record.
before { create(:ultima_global) }

# First oligo sequences for the two tag groups.
let(:plate1_first_oligo) { 'CAGCTCGAATGCGAT' }
let(:plate2_first_oligo) { 'CAGTCAGTTGCAGAT' }

# Eagerly create tag groups and tags to get consistent IDs.
let!(:tag_group1) { create(:tag_group_with_tags, tag_count: 96) }
let!(:tag_group2) { create(:tag_group_with_tags, tag_count: 96) }
let!(:tag_group1) do
create(:tag_group_with_tags, tag_count: 96, name: 'Ultima P1').tap do |tg|
# To test Z0001 matching with the oligo sequence.
tg.tags.first.update!(oligo: plate1_first_oligo)
end
end
let!(:tag_group2) do
create(:tag_group_with_tags, tag_count: 96, name: 'Ultima P2').tap do |tg|
# To test Z0097 matching with the oligo sequence.
tg.tags.first.update!(oligo: plate2_first_oligo)
end
end
let(:tag_groups) { [tag_group1, tag_group2] }

let(:request_type) { create(:ultima_sequencing) }
Expand Down Expand Up @@ -210,7 +225,7 @@ def map_description(map_id)
expect(csv2[1].compact_blank).to eq(["Batch #{batch.id} #{tube2.human_barcode}"]) # Second CSV
end

it 'generates global sections' do # rubocop:disable RSpec/MultipleExpectations
it 'generates global sections' do
# Test: Add the following hardcoded values, Application(WGS native gDNA),
# sequencing_recipe(UG_116cycles_Baseline_1.8.5.2) and analysis_recipe(wgs1)
expect(csv1[3].compact_blank).to eq(generator.class::GLOBAL_TITLE)
Expand All @@ -219,11 +234,24 @@ def map_description(map_id)
expect(csv1[6].compact_blank).to eq([])
end

it 'generates samples sections' do # rubocop:disable RSpec/MultipleExpectations
it 'generates samples sections' do
expect(csv1[7].compact_blank).to eq(generator.class::SAMPLES_TITLE)
expect(csv1[8].compact_blank).to eq(generator.class::SAMPLES_HEADERS)
expect(csv1[9..]).to eq(csv1_samples) # First CSV
expect(csv2[9..]).to eq(csv2_samples) # Second CSV
end

it 'matches the z-indexes, oligo sequences, and plate numbers' do
# First CSV
expect(csv1[9][2]).to eq('Z0001') # Index_Barcode_Num
expect(csv1[9][3]).to eq(plate1_first_oligo) # Index_Barcode_Sequence
expect(csv1[9][4]).to eq('1') # Barcode_Plate_Num

# Second CSV
expect(csv2[9][2]).to eq('Z0097') # Index_Barcode_Num
expect(csv2[9][3]).to eq(plate2_first_oligo) # Index_Barcode_Sequence
expect(csv2[9][4]).to eq('2') # Barcode_Plate_Num
end
end
end
# rubocop:enable RSpec/MultipleExpectations, RSpec/ExampleLength
Loading