Class: LabwareCreators::DonorPoolingPlate
- Defined in:
- app/models/labware_creators/donor_pooling_plate.rb
Overview
This labware creator receives barcodes for a configured number of source plates from the user. It pools samples from the passed wells into a destination plate. It’s used for scRNA Donor Pooling to create ‘LRC PBMC Pools’ plates from ‘LRC PBMC Defrost PBS’ plates.
The creator imposes restrictions:
-
It doesn’t allow combining samples from different studies or projects.
-
It doesn’t allow samples with the same donor_id in the same pool.
-
All wells must have cell count data unless they are failed.
-
The number of pools must not exceed the number configured for the samples.
The number of pools is determined by a lookup table based on sample count. Tag depth index is added to aliquot attributes to avoid tag clashes.
Constant Summary collapse
- SOURCE_PLATE_INCLUDES =
Define related objects to be included when retrieving source plates using the Sequencescape::API::V2.Plate.find_all method. The ‘includes’ argument of the method is expected to be an array of strings.
%w[ purpose wells.aliquots.study wells.aliquots.project wells.aliquots.request wells.aliquots.sample.sample_metadata wells.requests_as_source wells.qc_results ].freeze
Instance Attribute Summary collapse
-
#barcodes ⇒ Object
Returns the value of attribute barcodes.
-
#minimal_barcodes ⇒ Object
readonly
Returns the value of attribute minimal_barcodes.
Instance Method Summary collapse
-
#build_pools ⇒ Array<Array<Well>>
Builds the pools for the destination plate.
-
#default_number_of_pools ⇒ Integer
The default number of pools to be created if the count is not found in the lookup table.
-
#distribute_groups_across_pools(groups, number_of_pools) ⇒ Array<Array<Well>>
included
from DonorPoolingCalculator
Distributes samples across pools based on group sizes.
-
#labware_wells ⇒ Array<Well>
Returns all passed wells from the source plates in column order.
-
#max_number_of_source_plates ⇒ Integer
Returns the number of source plates from the purpose configuration.
-
#number_of_pools ⇒ Integer
Returns the number of pools based on the sample count from the lookup table.
-
#number_of_pools_must_not_exceed_configured ⇒ void
included
from DonorPoolingValidator
Validates that the number of calculated pools does not exceed the configured number of pools.
- #parent ⇒ Object included from SupportParent::PlateOnly
-
#pools ⇒ Array<Pool>
Returns the pools for the destination plate.
-
#request_hash(source_well, dest_plate, additional_parameters) ⇒ Hash
Generates a hash representing a transfer request from a source well to a destination well.
-
#source_barcodes_must_be_different ⇒ void
included
from DonorPoolingValidator
Validates that all source barcodes are unique.
-
#source_barcodes_must_be_entered ⇒ void
included
from DonorPoolingValidator
Validates that at least one source barcode has been entered.
-
#source_plates ⇒ Array<Plate>
Returns all source plates associated with the minimal barcodes.
-
#source_plates_must_exist ⇒ void
included
from DonorPoolingValidator
Validates that all source plates corresponding to the minimal barcodes exist.
-
#source_wells_for_pooling ⇒ Array<Well>
Returns the source wells for pooling.
-
#source_wells_to_plates ⇒ Hash
Returns a hash mapping each source well to its source plate.
-
#split_groups_by_unique_donor_ids(groups) ⇒ Array<Array<Well>>
included
from DonorPoolingCalculator
Splits groups ensuring unique donor_ids within each group.
-
#split_single_group_by_study_and_project(group) ⇒ Array<Array<Well>>
included
from DonorPoolingCalculator
Splits wells into groups by study and project.
-
#split_single_group_by_unique_donor_ids(group) ⇒ Array<Array<Well>>
included
from DonorPoolingCalculator
Splits a single group of wells by donor_ids.
-
#tag_depth_hash ⇒ Hash
Returns a hash mapping each source well to its index in its pool plus one.
-
#transfer_hash ⇒ Hash
Returns a mapping between each source well to a destination location.
-
#transfer_material_from_parent!(dest_uuid) ⇒ Boolean
Creates transfer requests from source wells to the destination plate in Sequencescape.
-
#transfer_request_attributes(dest_plate) ⇒ Array<Hash>
Generates the attributes for transfer requests from the source wells to the destination plate.
-
#unique_donor_ids(group) ⇒ Array<String>
included
from DonorPoolingCalculator
Returns the unique donor_ids from a group of wells.
-
#well_filter ⇒ WellFilter
Returns the WellFilter instance associated with this creator.
-
#wells_with_aliquots_must_have_cell_count ⇒ void
included
from DonorPoolingValidator
Validates that wells with aliquots have a latest_live_cell_count.
-
#wells_with_aliquots_must_have_donor_id ⇒ void
included
from DonorPoolingValidator
Validates that all wells with aliquots must have a donor_id.
Constructor Details
This class inherits a constructor from LabwareCreators::Base
Instance Attribute Details
#barcodes ⇒ Object
Returns the value of attribute barcodes.
34 35 36 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 34 def @barcodes end |
#minimal_barcodes ⇒ Object (readonly)
Returns the value of attribute minimal_barcodes.
40 41 42 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 40 def @minimal_barcodes end |
Instance Method Details
#build_pools ⇒ Array<Array<Well>>
Builds the pools for the destination plate. The wells are first grouped by study and project, then split by donor_ids, and finally distributed across pools.
218 219 220 221 222 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 218 def build_pools groups = split_single_group_by_study_and_project(source_wells_for_pooling) groups = split_groups_by_unique_donor_ids(groups) distribute_groups_across_pools(groups, number_of_pools) end |
#default_number_of_pools ⇒ Integer
The default number of pools to be created if the count is not found in the lookup table.
59 60 61 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 59 def default_number_of_pools purpose_config.dig(:creator_class, :args, :default_number_of_pools) end |
#distribute_groups_across_pools(groups, number_of_pools) ⇒ Array<Array<Well>> Originally defined in module DonorPoolingCalculator
Distributes samples across pools based on group sizes. It sorts the groups by size and splits the largest group into two until the number of groups equals the number of pools or until all groups have a size of 1. The input groups are the result of applying conditions, hence they cannot be mixed.
If the request number of pools is 6 and the input groups are
- [1, 2, 3], [4, 5], [6, 7, 8, 9]
-
where the numbers denote wells,
the result will be:
- [3], [1], [2], [4, 5], [6, 7], [8, 9]
-
for which the steps are:
- [1, 2, 3], [4, 5], [6, 7, 8, 9]
-
-> 3 pools (input)
- [4, 5], [6, 7], [8, 9], [1, 2, 3]
-
-> 4 pools
- [3], [4, 5], [6, 7], [8, 9], [1, 2]
-
-> 5 pools
- [3], [1], [2], [4, 5], [6, 7], [8, 9]
-
-> 6 pools (output)
#labware_wells ⇒ Array<Well>
Returns all passed wells from the source plates in column order.
83 84 85 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 83 def labware_wells source_plates.flat_map { |plate| plate.wells_in_columns.select(&:passed?) } end |
#max_number_of_source_plates ⇒ Integer
Returns the number of source plates from the purpose configuration.
66 67 68 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 66 def max_number_of_source_plates @max_number_of_source_plates ||= purpose_config.dig(:creator_class, :args, :max_number_of_source_plates) end |
#number_of_pools ⇒ Integer
Returns the number of pools based on the sample count from the lookup table.
134 135 136 137 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 134 def number_of_pools id = purpose_config.dig(:creator_class, :args, :pooling) Settings.poolings[id][:number_of_pools][source_wells_for_pooling.count] || default_number_of_pools end |
#number_of_pools_must_not_exceed_configured ⇒ void Originally defined in module DonorPoolingValidator
This method returns an undefined value.
Validates that the number of calculated pools does not exceed the configured number of pools. If the number of calculated pools is greater, an error is added to the :source_plates attribute.
#parent ⇒ Object Originally defined in module SupportParent::PlateOnly
#pools ⇒ Array<Pool>
Returns the pools for the destination plate.
116 117 118 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 116 def pools @pools ||= build_pools end |
#request_hash(source_well, dest_plate, additional_parameters) ⇒ Hash
Generates a hash representing a transfer request from a source well to a destination well. Additional parameters generated by the well filter are merged into the request hash, i.e.‘outer_request’ and ‘submission_id’. tag_depth is added to the aliquot attributes.
174 175 176 177 178 179 180 181 182 183 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 174 def request_hash(source_well, dest_plate, additional_parameters) dest_location = transfer_hash[source_well][:dest_locn] { 'source_asset' => source_well.uuid, 'target_asset' => dest_plate.well_at_location(dest_location)&.uuid, :aliquot_attributes => { 'tag_depth' => tag_depth_hash[source_well] } }.merge(additional_parameters) end |
#source_barcodes_must_be_different ⇒ void Originally defined in module DonorPoolingValidator
This method returns an undefined value.
Validates that all source barcodes are unique. If any barcodes are duplicated, an error is added to the :source_barcodes attribute.
#source_barcodes_must_be_entered ⇒ void Originally defined in module DonorPoolingValidator
This method returns an undefined value.
Validates that at least one source barcode has been entered. If no barcodes are entered, an error is added to the :source_barcodes attribute.
#source_plates ⇒ Array<Plate>
Returns all source plates associated with the minimal barcodes.
90 91 92 93 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 90 def source_plates @source_plates ||= Sequencescape::Api::V2::Plate.find_all({ barcode: }, includes: SOURCE_PLATE_INCLUDES) end |
#source_plates_must_exist ⇒ void Originally defined in module DonorPoolingValidator
This method returns an undefined value.
Validates that all source plates corresponding to the minimal barcodes exist. If the number of source plates does not match the number of minimal barcodes, an error is added to the :source_plates attribute.
#source_wells_for_pooling ⇒ Array<Well>
Returns the source wells for pooling. The wells are filtered using the well_filter.
99 100 101 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 99 def source_wells_for_pooling well_filter.filtered.map(&:first) # The first element is the well. end |
#source_wells_to_plates ⇒ Hash
Returns a hash mapping each source well to its source plate. The hash contains all source wells independent of the filtering.
108 109 110 111 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 108 def source_wells_to_plates @source_wells_to_plates ||= source_plates.each_with_object({}) { |plate, hash| plate.wells.each { |well| hash[well] = plate } } end |
#split_groups_by_unique_donor_ids(groups) ⇒ Array<Array<Well>> Originally defined in module DonorPoolingCalculator
Splits groups ensuring unique donor_ids within each group. Iterates over each group, creating subgroups with wells from a unique donor. The first occurrences of unique donor_ids are grouped, then the second occurrences, and so on. This prevents combining samples with the same donor_id. The result is flattened to a single array of subgroups.
If the input groups are [[w1, w2, w3, w4], [w5, w6, w7], [w8, w9]] where w1, w2, w3, w4, w5, w6, w7, w8, and w9 are wells with (donor_id),
w1(1) w2(2) w3(3) w4(1) w5(4) w6(4) w7(5) w8(6) w9(7)
the result will be:
- [w1, w2, w3], [w4], [w5, w7], [w6], [w8, w9]
-
Note that the input groups are not mixed. donor_ids are unique within each result subgroup.
#split_single_group_by_study_and_project(group) ⇒ Array<Array<Well>> Originally defined in module DonorPoolingCalculator
Splits wells into groups by study and project. Wells are grouped based on the study and project of the first aliquot in each well (only one aliquot is expected per well). Returns an array of groups, where each group is an array of wells with the same study and project.
If the input group is [w1, w2, w3, w4, w5, w6, w7, w8, w9] where w1, w2, w3, w4, w5, w6, w7, w8, and w9 are wells with (study_id, project_id),
w1(1,1) w2(1,2) w3(1,3) w4(1,1) w5(1,2) w6(1,3) w7(1,1) w8(2,1) w9(2,2)
the result will be:
- [w1, w4, w7], [w2, w5], [w3, w6], [w8], [w9]
#split_single_group_by_unique_donor_ids(group) ⇒ Array<Array<Well>> Originally defined in module DonorPoolingCalculator
Splits a single group of wells by donor_ids. This method is used by the ‘split_groups_by_unique_donor_ids’ method. It iteratively segregates wells with the first encountered instance of each unique donor_id into a separate subgroup. This process continues until there are no wells left in the original group. The result is a collection of subgroups, each containing wells from distinct donors.
If the input group is [w1, w2, w3, w4, w5, w6, w7, w8, w9] where w1, w2, w3, w4, w5, w6, w7, w8, and w9 are wells with (donor_id),
w1(1) w2(2) w3(3) w4(1) w5(2) w6(4) w7(5) w8(5) w9(5)
the result will be:
- [w1, w2, w3, w6, w7], [w4, w5, w8], [w9]
#tag_depth_hash ⇒ Hash
Returns a hash mapping each source well to its index in its pool plus one. The tag depth is used as an aliquot attribute in the transfer request. It is recorded in Sequencescape to avoid tag clashes.
204 205 206 207 208 209 210 211 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 204 def tag_depth_hash @tag_depth_hash ||= pools .each_with_index .with_object({}) do |(pool, _pool_index), hash| pool.each_with_index { |well, index| hash[well] = (index + 1).to_s } end end |
#transfer_hash ⇒ Hash
Returns a mapping between each source well to a destination location.
189 190 191 192 193 194 195 196 197 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 189 def transfer_hash @transfer_hash ||= pools .each_with_index .with_object({}) do |(pool, index), result| dest_location = WellHelpers.well_at_column_index(index) # column order, 96 wells pool.each { |source_well| result[source_well] = { dest_locn: dest_location } } end end |
#transfer_material_from_parent!(dest_uuid) ⇒ Boolean
Creates transfer requests from source wells to the destination plate in Sequencescape.
144 145 146 147 148 149 150 151 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 144 def transfer_material_from_parent!(dest_uuid) dest_plate = Sequencescape::Api::V2::Plate.find_by(uuid: dest_uuid) api.transfer_request_collection.create!( user: user_uuid, transfer_requests: transfer_request_attributes(dest_plate) ) true end |
#transfer_request_attributes(dest_plate) ⇒ Array<Hash>
Generates the attributes for transfer requests from the source wells to the destination plate.
159 160 161 162 163 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 159 def transfer_request_attributes(dest_plate) well_filter.filtered.filter_map do |source_well, additional_parameters| request_hash(source_well, dest_plate, additional_parameters) end end |
#unique_donor_ids(group) ⇒ Array<String> Originally defined in module DonorPoolingCalculator
Returns the unique donor_ids from a group of wells. Used by the ‘split_single_group_by_unique_donor_ids’ method.
If the input group is [w1, w2, w3, w4, w5, w6, w7, w8, w9] where w1, w2, w3, w4, w5, w6, w7, w8, and w9 are wells with (donor_id),
w1(1) w2(2) w3(3) w4(1) w5(2) w6(4) w7(5) w8(5) w9(5)
the result will be:
- 1, 2, 3, 4, 5
#well_filter ⇒ WellFilter
Returns the WellFilter instance associated with this creator. The filter uses the callback method ‘labware_wells’ to get the list of wells to filter, which specifies wells in ‘passed’ state from the source plates. The ‘source_wells_for_pooling’ method is used to get the filtered wells.
76 77 78 |
# File 'app/models/labware_creators/donor_pooling_plate.rb', line 76 def well_filter @well_filter ||= WellFilter.new(creator: self) end |
#wells_with_aliquots_must_have_cell_count ⇒ void Originally defined in module DonorPoolingValidator
This method returns an undefined value.
Validates that wells with aliquots have a latest_live_cell_count. It uses the locations_with_missing_cell_count method to find any wells that are missing a cell count. If any such wells are found, it adds an error message to the source_plates attribute, formatted with the barcodes of the plates and the wells that are missing a cell count. Note that the well filter already excludes failed wells. This validation ensures that all wells with aliquots have a cell count unless they are failed.
#wells_with_aliquots_must_have_donor_id ⇒ void Originally defined in module DonorPoolingValidator
This method returns an undefined value.
Validates that all wells with aliquots must have a donor_id. It uses the locations_with_missing_donor_id method to find any wells that are missing a donor_id. If any such wells are found, it adds an error message to the source_plates attribute, formatted with the barcodes of the plates and the wells that are missing a donor_id.