Class: Plate
- Inherits:
-
Labware
- Object
- ActiveRecord::Base
- ApplicationRecord
- Asset
- Labware
- Plate
- Extended by:
- Metadata, QcFile::Associations
- Includes:
- Api::PlateIo::Extensions, Asset::Ownership::Owned, Barcode::Barcodeable, ModelExtensions::Plate, FluidigmBehaviour, PoolingMetadata, PlateCreation::CreationChild, SubmissionPool::Association::Plate, Transfer::Associations, Transfer::State::PlateState
- Defined in:
- app/models/plate.rb
Overview
A plate is a piece of labware made up of a number of wells. This class represents the physical piece of plastic.
-
PlatePurpose: describes the role a plate has in the lab. In some cases a plate’s purpose may change as it gets processed.
-
Well: Plates can have multiple wells (most often 96 or 384) each of which can contain multiple samples.
-
PlateType: Identifies the plates form factor, typically provided to robots to ensure tips are positioned correctly.
Direct Known Subclasses
ControlPlate, DilutionPlate, PicoAssayPlate, PlateTemplate, SequenomQcPlate, StripTube
Defined Under Namespace
Modules: FluidigmBehaviour, PoolingMetadata Classes: Creator, CreatorParameters, QuadCreator, SampleTubeFactory
Constant Summary
Constants included from Metadata
Instance Attribute Summary
Attributes inherited from Labware
Class Method Summary collapse
- .create_with_barcode!(*args) ⇒ Object
- .plate_ids_from_requests(requests) ⇒ Object
- .search_for_plates(params) ⇒ Object
Instance Method Summary collapse
- #after_comment_addition(comment) ⇒ Object
-
#all_submission_ids ⇒ Object
Prioritised the submissions that have been made from the plate then falls back onto the ones under which the plate was made.
- #buffer_required? ⇒ Boolean
-
#cherrypick_completed ⇒ Void
Called when cherrypicking is completed to allow the plate to trigger any callbacks, such as broadcasting Fluidigm plates to the warehouse.
- #comments ⇒ Object
- #compatible_purposes ⇒ Object
- #convert_to(new_purpose) ⇒ Object
- #details ⇒ Object
- #find_well_by_name(well_name) ⇒ Object (also: #find_well_by_map_description)
-
#generate_barcode ⇒ Object
Plates use a different counter to tubes, and prior to the foreign barcodes update this method would have fallen back to Barcodable#generate tubes, and potentially generated an invalid plate barcode.
- #height ⇒ Object
-
#invalid_positions(positions) ⇒ Array
Given a list of well map_descriptions (eg. A1), returns those not present on the plate.
- #iteration ⇒ Object
- #maps ⇒ Object
- #name_for_label ⇒ Object
- #number_of_blank_samples ⇒ Object
-
#occupied_well_count ⇒ Integer
Counts the number of wells containing one or more aliquots.
-
#pick_as_control? ⇒ false
When Cherrypicking, especially on the Hamilton, control plates get placed on a seperate bed.
- #plate_columns ⇒ Object
- #plate_rows ⇒ Object
- #plate_type ⇒ Object
- #plate_type=(plate_type) ⇒ Object
- #priority ⇒ Object
-
#receptacles_with_position ⇒ Object
Used to unify interface with TubeRacks.
- #related_studies ⇒ Object
- #sanger_barcode=(barcode) ⇒ Object
- #scored? ⇒ Boolean
-
#state ⇒ String
The state of a plate loosely defines what has happened to it.
-
#stock_plate ⇒ Plate?
deprecated
Deprecated.
Do not use this for new behaviour.
- #stock_plate? ⇒ Boolean
-
#stock_wells ⇒ Object
This method returns a map from the wells on the plate to their stock well.
- #subject_type ⇒ Object
- #submission_ids ⇒ Object
- #submission_ids_as_source ⇒ Object
- #submissions ⇒ Object
-
#team ⇒ Object
Finds the product line (= team) of the requests coming out of this plate’s ‘stock plate’.
- #unique_positions_on_plate ⇒ Object
- #update_qc_values_with_parser(parser) ⇒ Object
-
#update_volume(volume_change) ⇒ Void
Modifies the recorded volume information of all wells on a plate by volume_change.
- #well_hash ⇒ Object
- #wells_in_column_order ⇒ Object
- #wells_in_row_order ⇒ Object
- #width ⇒ Object
Methods included from QcFile::Associations
Methods included from Metadata
Methods included from SubmissionPool::Association::Plate
Methods included from Barcode::Barcodeable
#any_barcode_matching?, #barcode_format, #barcode_number, #cgap_barcode, #cgap_barcode=, #external_barcode, #external_barcode=, #external_identifier, #fluidigm_barcode, #fluidigm_barcode=, #foreign_barcode=, included, #infinium_barcode, #infinium_barcode=, #prefix, #primary_barcode, #printable_target, #sanger_barcode
Methods included from PlateCreation::CreationChild
Methods included from PoolingMetadata
Methods included from FluidigmBehaviour
#apply_fluidigm_data, included, #retrieve_fluidigm_data
Methods included from Asset::Ownership::Owned
Methods included from Transfer::State::PlateState
Methods included from Transfer::Associations
Methods included from Api::PlateIo::Extensions
Methods inherited from Labware
#ancestor_of_purpose, #ancestors_of_purpose, #child, #display_name, #external_identifier, find_by_barcode, find_from_any_barcode, #generate_name, #labware, #labwhere_location, labwhere_locations, #parent, #received_date, #retention_instructions, #role, #scanned_in_date, #source_plate, #source_plates, #spiked_in_buffer, #storage_location
Methods included from SharedBehaviour::Named
Methods included from AssetLink::Associations
Methods included from Uuid::Uuidable
included, #unsaved_uuid!, #uuid
Methods inherited from Asset
#ancestor_of_purpose, #asset_type_for_request_types, #barcode_number, #contained_samples, #get_qc_result_value_for, #has_stock_asset?, #label, #label=, #original_stock_plates, #prefix, #printable?, #printable_target, #register_stock!, #request_types, #type, #update_from_qc
Methods included from EventfulRecord
#has_many_events, #has_many_lab_events, #has_one_event_with_family
Methods included from Event::PlateEvents
#event_date, #fluidigm_stamp_date, #gel_qc_date, #pico_date, #qc_started_date, #sequenom_stamp_date
Methods inherited from ApplicationRecord
alias_association, convert_labware_to_receptacle_for, find_by_id_or_name, find_by_id_or_name!
Methods included from Squishify
Class Method Details
.create_with_barcode!(*args) ⇒ Object
342 343 344 345 346 |
# File 'app/models/plate.rb', line 342 def self.(*args, &) attributes = args. attributes[:sanger_barcode] ||= PlateBarcode. create!(attributes, &) end |
.plate_ids_from_requests(requests) ⇒ Object
311 312 313 |
# File 'app/models/plate.rb', line 311 def self.plate_ids_from_requests(requests) with_requests(requests).pluck(:id) end |
.search_for_plates(params) ⇒ Object
255 256 257 258 259 260 261 262 |
# File 'app/models/plate.rb', line 255 def self.search_for_plates(params) with_faculty_sponsor_ids(params[:faculty_sponsor_ids] || nil) .with_study_id(params[:study_id] || nil) .with_plate_purpose_ids(params[:plate_purpose_ids] || nil) .created_between(params[:start_date], params[:end_date]) .(params[:barcodes] || nil) .distinct end |
Instance Method Details
#after_comment_addition(comment) ⇒ Object
474 475 476 |
# File 'app/models/plate.rb', line 474 def after_comment_addition(comment) comments.add_comment_to_submissions(comment) end |
#all_submission_ids ⇒ Object
Prioritised the submissions that have been made from the plate then falls back onto the ones under which the plate was made
183 184 185 |
# File 'app/models/plate.rb', line 183 def all_submission_ids submission_ids_as_source.presence || submission_ids end |
#buffer_required? ⇒ Boolean
356 357 358 |
# File 'app/models/plate.rb', line 356 def buffer_required? wells.any?(&:buffer_required?) end |
#cherrypick_completed ⇒ Void
Called when cherrypicking is completed to allow the plate to trigger any callbacks, such as broadcasting Fluidigm plates to the warehouse. This behaviour varies based on the PlatePurpose
160 161 162 |
# File 'app/models/plate.rb', line 160 def cherrypick_completed plate_purpose.cherrypick_completed(self) end |
#comments ⇒ Object
201 202 203 |
# File 'app/models/plate.rb', line 201 def comments @comments ||= CommentsProxy::Plate.new(self) end |
#compatible_purposes ⇒ Object
411 412 413 |
# File 'app/models/plate.rb', line 411 def compatible_purposes PlatePurpose.compatible_with_purpose(purpose) end |
#convert_to(new_purpose) ⇒ Object
407 408 409 |
# File 'app/models/plate.rb', line 407 def convert_to(new_purpose) update!(plate_purpose: new_purpose) end |
#details ⇒ Object
307 308 309 |
# File 'app/models/plate.rb', line 307 def details purpose.try(:name) || 'Unknown plate purpose' end |
#find_well_by_name(well_name) ⇒ Object Also known as: find_well_by_map_description
286 287 288 |
# File 'app/models/plate.rb', line 286 def find_well_by_name(well_name) wells.loaded? ? wells.indexed_by_location[well_name] : wells.located_at_position(well_name).first end |
#generate_barcode ⇒ Object
Plates use a different counter to tubes, and prior to the foreign barcodes update this method would have fallen back to Barcodable#generate tubes, and potentially generated an invalid plate barcode. In the future we probably want to scrap this approach entirely, and generate all barcodes in the plate style. (That is, as part of the factory on, eg. plate purpose)
465 466 467 468 |
# File 'app/models/plate.rb', line 465 def raise StandardError, "#generate_barcode has been called on plate, which wasn't supposed to happen, and probably indicates a bug." end |
#height ⇒ Object
382 383 384 |
# File 'app/models/plate.rb', line 382 def height asset_shape.plate_height(size) end |
#invalid_positions(positions) ⇒ Array
Given a list of well map_descriptions (eg. A1), returns those not present on the plate
367 368 369 |
# File 'app/models/plate.rb', line 367 def invalid_positions(positions) (positions.uniq - unique_positions_on_plate).sort end |
#iteration ⇒ Object
191 192 193 194 195 196 197 198 199 |
# File 'app/models/plate.rb', line 191 def iteration iter = siblings # assets sharing the same parent .where(plate_purpose_id:, sti_type:) # of the same purpose and type .where("#{self.class.table_name}.created_at <= ?", created_at) # created before or at the same time .count(:id) # count the siblings. iter.zero? ? nil : iter # Maintains compatibility with legacy version end |
#maps ⇒ Object
282 283 284 |
# File 'app/models/plate.rb', line 282 def maps Map.where_plate_size(size).where_plate_shape(asset_shape) end |
#name_for_label ⇒ Object
375 376 377 |
# File 'app/models/plate.rb', line 375 def name_for_label name end |
#number_of_blank_samples ⇒ Object
348 349 350 |
# File 'app/models/plate.rb', line 348 def number_of_blank_samples wells.with_blank_samples.count end |
#occupied_well_count ⇒ Integer
Does not take into account the Sample#empty_supplier_sample_name flag on older samples
Counts the number of wells containing one or more aliquots.
150 151 152 |
# File 'app/models/plate.rb', line 150 def occupied_well_count wells.with_contents.count end |
#pick_as_control? ⇒ false
When Cherrypicking, especially on the Hamilton, control plates get placed on a seperate bed. ControlPlates overide this.
493 494 495 |
# File 'app/models/plate.rb', line 493 def pick_as_control? false end |
#plate_columns ⇒ Object
295 296 297 |
# File 'app/models/plate.rb', line 295 def plate_columns (1..width) end |
#plate_rows ⇒ Object
291 292 293 |
# File 'app/models/plate.rb', line 291 def plate_rows ('A'..('A'.getbyte(0) + height - 1).chr.to_s).to_a end |
#plate_type ⇒ Object
299 300 301 |
# File 'app/models/plate.rb', line 299 def plate_type labware_type&.name || Sequencescape::Application.config.plate_default_type end |
#plate_type=(plate_type) ⇒ Object
303 304 305 |
# File 'app/models/plate.rb', line 303 def plate_type=(plate_type) self.labware_type = PlateType.find_by(name: plate_type) end |
#priority ⇒ Object
205 206 207 |
# File 'app/models/plate.rb', line 205 def priority waiting_submissions.maximum(:priority) || in_progress_submissions.maximum(:priority) || 0 end |
#receptacles_with_position ⇒ Object
Used to unify interface with TubeRacks. Returns a list of all receptacles wells with position information included for aid performance
117 118 119 |
# File 'app/models/plate.rb', line 117 def receptacles_with_position wells.includes(:map) end |
#related_studies ⇒ Object
478 479 480 |
# File 'app/models/plate.rb', line 478 def studies end |
#sanger_barcode=(barcode) ⇒ Object
470 471 472 |
# File 'app/models/plate.rb', line 470 def () << end |
#scored? ⇒ Boolean
352 353 354 |
# File 'app/models/plate.rb', line 352 def scored? wells.any?(&:get_gel_pass) end |
#state ⇒ String
The state of a plate loosely defines what has happened to it. In most cases it is determined by aggregating the state of transfer requests into the wells, although exact behaviour is determined by the PlatePurpose. State typically only works for pipeline application plates. In general:
-
pending: The plate has been registered, but it empty.
-
started: The plate contains samples, but required further processing
-
passed: Work on the plate is complete, and it can be transferred to another target
-
failed: The plate failed QC and can not be progressed further
-
cancelled: The plate is no longer required and should be ignored.
132 133 134 |
# File 'app/models/plate.rb', line 132 def state plate_purpose.state_of(self) end |
#stock_plate ⇒ Plate?
Do not use this for new behaviour.
Attempts to find the ‘stock_plate’ for the plate. However this is a fairly nebulous concept. Often it means the plate that first entered a pipeline, but in other cases it can be the XP plate part way through the process. Further complication comes from tubes which pool across multiple plates, where identifying a single stock plate is meaningless. In other scenarios, you split plates out again and the asset link graph is insufficient.
JG: 2021-02-11: See github.com/sanger/sequencescape/issues/3040 for more information
337 338 339 |
# File 'app/models/plate.rb', line 337 def stock_plate @stock_plate ||= stock_plate? ? self : lookup_stock_plate end |
#stock_plate? ⇒ Boolean
315 316 317 318 319 |
# File 'app/models/plate.rb', line 315 def stock_plate? return true if plate_purpose.nil? plate_purpose.stock_plate? && plate_purpose.attached?(self) end |
#stock_wells ⇒ Object
This method returns a map from the wells on the plate to their stock well.
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
# File 'app/models/plate.rb', line 391 def stock_wells # rubocop:todo Metrics/AbcSize, Metrics/MethodLength # Optimisation: if the plate is a stock plate then it's wells are it's stock wells!] if stock_plate? wells.with_pool_id.index_with { |w| [w] } else wells .include_stock_wells .with_pool_id .each_with_object({}) do |w, store| storted_stock_wells = w.stock_wells.sort_by { |sw| sw.map.column_order } store[w] = storted_stock_wells unless storted_stock_wells.empty? end .tap { |stock_wells_hash| raise "No stock plate associated with #{id}" if stock_wells_hash.empty? } end end |
#subject_type ⇒ Object
457 458 459 |
# File 'app/models/plate.rb', line 457 def subject_type 'plate' end |
#submission_ids ⇒ Object
173 174 175 |
# File 'app/models/plate.rb', line 173 def submission_ids @submission_ids ||= in_progress_submissions.ids end |
#submission_ids_as_source ⇒ Object
177 178 179 |
# File 'app/models/plate.rb', line 177 def submission_ids_as_source @submission_ids_as_source ||= waiting_submissions.ids end |
#submissions ⇒ Object
187 188 189 |
# File 'app/models/plate.rb', line 187 def submissions waiting_submissions.presence || in_progress_submissions end |
#team ⇒ Object
Finds the product line (= team) of the requests coming out of this plate’s ‘stock plate’. Written at a time when requests weren’t recorded on the aliquot, so could be re-written in a less convoluted way.
442 443 444 445 446 447 448 449 450 451 452 453 454 |
# File 'app/models/plate.rb', line 442 def team ProductLine .joins( [ 'INNER JOIN request_types ON request_types.product_line_id = product_lines.id', 'INNER JOIN requests ON requests.request_type_id = request_types.id', 'INNER JOIN well_links ON well_links.source_well_id = requests.asset_id AND well_links.type = "stock"', 'INNER JOIN receptacles AS re ON re.id = well_links.target_well_id' ] ) .find_by(['re.labware_id = ?', id]) .try(:name) || 'UNKNOWN' end |
#unique_positions_on_plate ⇒ Object
371 372 373 |
# File 'app/models/plate.rb', line 371 def unique_positions_on_plate maps.distinct.pluck(:description) end |
#update_qc_values_with_parser(parser) ⇒ Object
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
# File 'app/models/plate.rb', line 419 def update_qc_values_with_parser(parser) ActiveRecord::Base.transaction do qc_assay = QcAssay.new parser.each_well_and_parameters do |position, well_updates| # We might have a nil well if a plate was only partially cherrypicked well = well_hash[position] || next well_updates.each do |attribute, value| QcResult.create!( asset: well, key: attribute, unit_value: value, assay_type: parser.assay_type, assay_version: parser.assay_version, qc_assay: qc_assay ) end end end true end |
#update_volume(volume_change) ⇒ Void
Modifies the recorded volume information of all wells on a plate by volume_change
141 142 143 |
# File 'app/models/plate.rb', line 141 def update_volume(volume_change) ActiveRecord::Base.transaction { wells.each { |well| well.update_volume(volume_change) } } end |
#well_hash ⇒ Object
415 416 417 |
# File 'app/models/plate.rb', line 415 def well_hash @well_hash ||= wells.include_map.includes(:well_attribute).index_by(&:map_description) end |
#wells_in_column_order ⇒ Object
486 487 488 |
# File 'app/models/plate.rb', line 486 def wells_in_column_order wells.loaded? ? wells.sort_by(&:column_order) : wells.in_column_major_order end |
#wells_in_row_order ⇒ Object
482 483 484 |
# File 'app/models/plate.rb', line 482 def wells_in_row_order wells.loaded? ? wells.sort_by(&:row_order) : wells.in_row_major_order end |
#width ⇒ Object
386 387 388 |
# File 'app/models/plate.rb', line 386 def width asset_shape.plate_width(size) end |