Class: Plate
- Inherits:
-
Labware
show all
- 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
https://github.com/sanger/sequencescape/raw/master/docs/images/plate.jpg
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.
Defined Under Namespace
Modules: FluidigmBehaviour, PoolingMetadata
Classes: Creator, CreatorParameters, QuadCreator, SampleTubeFactory
Constant Summary
Constants included
from Metadata
Metadata::SECTION_FIELDS
Instance Attribute Summary
Attributes inherited from Labware
#storage_location_service
Class Method Summary
collapse
Instance Method Summary
collapse
has_qc_files
Methods included from Metadata
has_metadata
included
#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
included
#change_owner_to, included
included
included
included, #json_root
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, search_for_count_of_labware, search_for_labware, #source_plate, #source_plates, #spiked_in_buffer, #storage_location
included
included
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
#has_many_events, #has_many_lab_events, #has_one_event_with_family
#event_date, #fluidigm_stamp_date, #gel_qc_date, #pico_date, #qc_started_date, #sequenom_stamp_date
alias_association, convert_labware_to_receptacle_for, find_by_id_or_name, find_by_id_or_name!
Methods included from Squishify
extended
Class Method Details
.create_with_barcode!(*args) ⇒ Object
316
317
318
319
320
|
# File 'app/models/plate.rb', line 316
def self.create_with_barcode!(*args, &)
attributes = args.
attributes[:sanger_barcode] ||= PlateBarcode.create_barcode
create!(attributes, &)
end
|
.plate_ids_from_requests(requests) ⇒ Object
284
285
286
|
# File 'app/models/plate.rb', line 284
def self.plate_ids_from_requests(requests)
with_requests(requests).pluck(:id)
end
|
Instance Method Details
449
450
451
|
# File 'app/models/plate.rb', line 449
def ()
.()
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
330
331
332
|
# File 'app/models/plate.rb', line 330
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
|
201
202
203
|
# File 'app/models/plate.rb', line 201
def
@comments ||= CommentsProxy::Plate.new(self)
end
|
#compatible_purposes ⇒ Object
386
387
388
|
# File 'app/models/plate.rb', line 386
def compatible_purposes
PlatePurpose.compatible_with_purpose(purpose)
end
|
#convert_to(new_purpose) ⇒ Object
382
383
384
|
# File 'app/models/plate.rb', line 382
def convert_to(new_purpose)
update!(plate_purpose: new_purpose)
end
|
#details ⇒ Object
280
281
282
|
# File 'app/models/plate.rb', line 280
def details
purpose.try(:name) || 'Unknown plate purpose'
end
|
#find_well_by_name(well_name) ⇒ Object
Also known as:
find_well_by_map_description
259
260
261
|
# File 'app/models/plate.rb', line 259
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)
440
441
442
443
|
# File 'app/models/plate.rb', line 440
def generate_barcode
raise StandardError,
"#generate_barcode has been called on plate, which wasn't supposed to happen, and probably indicates a bug."
end
|
#height ⇒ Object
357
358
359
|
# File 'app/models/plate.rb', line 357
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
341
342
343
|
# File 'app/models/plate.rb', line 341
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 .where(plate_purpose_id:, sti_type:) .where("#{self.class.table_name}.created_at <= ?", created_at) .count(:id)
iter.zero? ? nil : iter end
|
#maps ⇒ Object
255
256
257
|
# File 'app/models/plate.rb', line 255
def maps
Map.where_plate_size(size).where_plate_shape(asset_shape)
end
|
#name_for_label ⇒ Object
349
350
351
|
# File 'app/models/plate.rb', line 349
def name_for_label
name
end
|
#number_of_blank_samples ⇒ Object
322
323
324
|
# File 'app/models/plate.rb', line 322
def number_of_blank_samples
wells.with_blank_samples.count
end
|
#occupied_well_count ⇒ Integer
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.
468
469
470
|
# File 'app/models/plate.rb', line 468
def pick_as_control?
false
end
|
#plate_columns ⇒ Object
268
269
270
|
# File 'app/models/plate.rb', line 268
def plate_columns
(1..width)
end
|
#plate_rows ⇒ Object
264
265
266
|
# File 'app/models/plate.rb', line 264
def plate_rows
('A'..('A'.getbyte(0) + height - 1).chr.to_s).to_a
end
|
#plate_type ⇒ Object
272
273
274
|
# File 'app/models/plate.rb', line 272
def plate_type
labware_type&.name || Sequencescape::Application.config.plate_default_type
end
|
#plate_type=(plate_type) ⇒ Object
276
277
278
|
# File 'app/models/plate.rb', line 276
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
|
453
454
455
|
# File 'app/models/plate.rb', line 453
def related_studies
studies
end
|
#sanger_barcode=(barcode) ⇒ Object
445
446
447
|
# File 'app/models/plate.rb', line 445
def sanger_barcode=(barcode)
barcodes << barcode
end
|
#scored? ⇒ Boolean
326
327
328
|
# File 'app/models/plate.rb', line 326
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?
Deprecated. 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 https://github.com/sanger/sequencescape/issues/3040 for more information
310
311
312
|
# File 'app/models/plate.rb', line 310
def stock_plate
@stock_plate ||= stock_plate? ? self : lookup_stock_plate
end
|
#stock_plate? ⇒ Boolean
288
289
290
291
292
|
# File 'app/models/plate.rb', line 288
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.
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
|
# File 'app/models/plate.rb', line 366
def 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
432
433
434
|
# File 'app/models/plate.rb', line 432
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.
417
418
419
420
421
422
423
424
425
426
427
428
429
|
# File 'app/models/plate.rb', line 417
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
345
346
347
|
# File 'app/models/plate.rb', line 345
def unique_positions_on_plate
maps.distinct.pluck(:description)
end
|
#update_qc_values_with_parser(parser) ⇒ Object
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
|
# File 'app/models/plate.rb', line 394
def update_qc_values_with_parser(parser)
ActiveRecord::Base.transaction do
qc_assay = QcAssay.new
parser.each_well_and_parameters do |position, well_updates|
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
390
391
392
|
# File 'app/models/plate.rb', line 390
def well_hash
@well_hash ||= wells.include_map.includes(:well_attribute).index_by(&:map_description)
end
|
#wells_in_column_order ⇒ Object
461
462
463
|
# File 'app/models/plate.rb', line 461
def wells_in_column_order
wells.loaded? ? wells.sort_by(&:column_order) : wells.in_column_major_order
end
|
#wells_in_row_order ⇒ Object
457
458
459
|
# File 'app/models/plate.rb', line 457
def wells_in_row_order
wells.loaded? ? wells.sort_by(&:row_order) : wells.in_row_major_order
end
|
#width ⇒ Object
361
362
363
|
# File 'app/models/plate.rb', line 361
def width
asset_shape&.plate_width(size)
end
|