diff --git a/app/controllers/ultima_sample_sheet/sample_sheet_generator.rb b/app/controllers/ultima_sample_sheet/sample_sheet_generator.rb index 2bc5b509d3..212b3738c4 100644 --- a/app/controllers/ultima_sample_sheet/sample_sheet_generator.rb +++ b/app/controllers/ultima_sample_sheet/sample_sheet_generator.rb @@ -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 @@ -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), @@ -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] 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] 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. diff --git a/spec/controllers/ultima_sample_sheet/sample_sheet_generator_spec.rb b/spec/controllers/ultima_sample_sheet/sample_sheet_generator_spec.rb index a1da05bb06..923a12a490 100644 --- a/spec/controllers/ultima_sample_sheet/sample_sheet_generator_spec.rb +++ b/spec/controllers/ultima_sample_sheet/sample_sheet_generator_spec.rb @@ -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) } @@ -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) @@ -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