Class: Well

Inherits:
Receptacle show all
Includes:
Api::Messages::FluidigmPlateIO::WellExtensions, Api::Messages::QcResultIO::WellExtensions, Api::WellIO::Extensions, Cherrypick::VolumeByMicroLitre, Cherrypick::VolumeByNanoGrams, Cherrypick::VolumeByNanoGramsPerMicroLitre, ModelExtensions::Well, StudyReport::WellDetails, Tag::Associations
Defined in:
app/models/well.rb

Overview

A Well is a Receptacle on a Plate, it can contain one or more aliquots. A plate may have multiple wells, with the two most common sizes being 12*8 (96) and 24*26 (384). The wells are differentiated via their Map which corresponds to a row and column. Most well locations are identified by a letter-number combination, eg. A1, H12.

Defined Under Namespace

Modules: AttributeUpdater Classes: Link

Constant Summary

Constants inherited from Receptacle

Receptacle::QC_STATE_ALIASES

Constants included from Transfer::State

Transfer::State::ALL_STATES

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Tag::Associations

#untag!

Methods included from StudyReport::WellDetails

included, #qc_report

Methods included from Cherrypick::VolumeByMicroLitre

#volume_to_cherrypick_by_micro_litre

Methods included from Cherrypick::VolumeByNanoGramsPerMicroLitre

#volume_to_cherrypick_by_nano_grams_per_micro_litre

Methods included from Cherrypick::VolumeByNanoGrams

#volume_to_cherrypick_by_nano_grams

Methods included from Api::WellIO::Extensions

included

Methods inherited from Receptacle

#any_barcode_matching?, #api_asset_type, #assign_tag2, #attach_tag, #been_through_qc?, #compatible_qc_state, #created_with_request_options, #friendly_name, #labware_comment_count, #legacy_asset_type, #library_information, #library_types, #primary_aliquot_if_unique, #related_studies, #role, #set_as_library, #set_qc_state, #tag_range, #total_comment_count, #update_aliquot_quality

Methods included from StudyReport::AssetDetails

#qc_report

Methods included from Aliquot::Remover

#on_downstream_aliquots, #process_aliquots, #remove_downstream_aliquots, #remove_matching_aliquots

Methods included from Transfer::State

#default_state, #state, #state_from, state_helper

Methods included from Commentable

#after_comment_addition

Methods included from Uuid::Uuidable

included, #unsaved_uuid!, #uuid

Methods inherited from Asset

#ancestor_of_purpose, #barcode_number, #compatible_purposes, #contained_samples, #generate_barcode, #get_qc_result_value_for, #has_stock_asset?, #label, #label=, #original_stock_plates, #prefix, #printable?, #printable_target, #register_stock!, #request_types, #summary_hash, #type

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

convert_labware_to_receptacle_for, find_by_id_or_name, find_by_id_or_name!

Methods included from Squishify

extended

Class Method Details

.delegate_to_well_attribute(attribute, options = {}) ⇒ Object


165
166
167
168
169
170
171
# File 'app/models/well.rb', line 165

def delegate_to_well_attribute(attribute, options = {})
  class_eval <<-END_OF_METHOD_DEFINITION
    def get_#{attribute}
      self.well_attribute.#{attribute} || #{options[:default].inspect}
    end
  END_OF_METHOD_DEFINITION
end

.hash_stock_with_targets(wells, purpose_names) ⇒ Object


181
182
183
184
185
186
187
188
189
190
# File 'app/models/well.rb', line 181

def hash_stock_with_targets(wells, purpose_names)
  return {} unless purpose_names

  purposes = PlatePurpose.where(name: purpose_names)

  # We might need to be careful about this line in future.
  target_wells = Well.target_wells_for(wells).on_plate_purpose(purposes).preload(:well_attribute).with_concentration

  target_wells.group_by(&:stock_well_id)
end

.writer_for_well_attribute_as_float(attribute) ⇒ Object


173
174
175
176
177
178
179
# File 'app/models/well.rb', line 173

def writer_for_well_attribute_as_float(attribute)
  class_eval <<-END_OF_METHOD_DEFINITION
    def set_#{attribute}(value)
      self.well_attribute.update!(:#{attribute} => value.to_f)
    end
  END_OF_METHOD_DEFINITION
end

Instance Method Details

#absolute_position_nameObject

Returns the name of the position (eg. A1) of the well


307
308
309
# File 'app/models/well.rb', line 307

def absolute_position_name
  map_description
end

#asset_type_for_request_typesObject


345
346
347
# File 'app/models/well.rb', line 345

def asset_type_for_request_types
  self.class
end

#buffer_required?Boolean

Returns:

  • (Boolean)

315
316
317
# File 'app/models/well.rb', line 315

def buffer_required?
  get_buffer_volume > 0.0
end

#detailsObject


332
333
334
335
336
# File 'app/models/well.rb', line 332

def details
  return 'Not yet picked' if plate.nil?

  plate.purpose.try(:name) || 'Unknown plate purpose'
end

#display_nameObject

Note:

Be very wary of changing this as we have places in limber (github.com/sanger/limber/blob/develop/app/helpers/exports_helper.rb) where it is assumed to contain the barcode and well location. It is highly likely that we aren't the only ones making this assumption.

Returns a name for the well in the format HumanBarcode:Location eg. DN12345S:A1


325
326
327
328
329
330
# File 'app/models/well.rb', line 325

def display_name
  source = association_cached?(:plate) ? plate : labware
  plate_name = source.present? ? source.human_barcode : '(not on a plate)'
  plate_name ||= source.display_name # In the even the plate is barcodeless (ie strip tubes) use its name
  "#{plate_name}:#{map_description}"
end

#external_identifierObject


228
229
230
# File 'app/models/well.rb', line 228

def external_identifier
  display_name
end

#generate_name(_) ⇒ Object


224
225
226
# File 'app/models/well.rb', line 224

def generate_name(_)
  # Do nothing
end

#get_sequenom_passObject

The sequenom pass value is either the string 'Unknown' or it is the combination of gender marker values.


298
299
300
301
# File 'app/models/well.rb', line 298

def get_sequenom_pass
  markers = well_attribute.gender_markers
  markers.is_a?(Array) ? markers.join : markers
end

#latest_stock_metrics(product) ⇒ Object


338
339
340
341
342
343
# File 'app/models/well.rb', line 338

def latest_stock_metrics(product)
  # If we don't have any stock wells, use ourself. If it is a stock well, we'll find our
  # qc metric. If its not a stock well, then a metric won't be present anyway
  metric_wells = stock_wells.empty? ? [self] : stock_wells
  metric_wells.filter_map { |stock_well| stock_well.qc_metrics.for_product(product).most_recent_first.first }.uniq
end

#library_nameObject


357
358
359
# File 'app/models/well.rb', line 357

def library_name
  nil
end

#nameObject


353
354
355
# File 'app/models/well.rb', line 353

def name
  nil
end

#outer_request(submission_id) ⇒ Object


201
202
203
# File 'app/models/well.rb', line 201

def outer_request(submission_id)
  outer_requests.order(id: :desc).find_by(submission_id: submission_id)
end

#qc_dataObject


311
312
313
# File 'app/models/well.rb', line 311

def qc_data
  { pico: get_pico_pass, gel: get_gel_pass, sequenom: get_sequenom_pass, concentration: get_concentration }
end

#qc_result_for(key) ⇒ Object


209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'app/models/well.rb', line 209

def qc_result_for(key)
  result =
    if key == 'quantity_in_nano_grams'
      well_attribute.quantity_in_nano_grams
    else
      results = qc_results_by_key[key]
      results.first.value if results.present?
    end

  return if result.nil?
  return result.to_f.round(3) if result.to_s.include?('.')

  result.to_i
end

#qc_results_by_keyObject


205
206
207
# File 'app/models/well.rb', line 205

def qc_results_by_key
  @qc_results_by_key ||= qc_results.by_key
end

#stock_wells_for_downstream_wellsObject


193
194
195
# File 'app/models/well.rb', line 193

def stock_wells_for_downstream_wells
  labware&.stock_plate? ? [self] : stock_wells
end

#subject_typeObject


197
198
199
# File 'app/models/well.rb', line 197

def subject_type
  'well'
end

#update_from_qc(qc_result) ⇒ Object


349
350
351
# File 'app/models/well.rb', line 349

def update_from_qc(qc_result)
  Well::AttributeUpdater.update(self, qc_result)
end

#update_gender_markers!(gender_markers, resource) ⇒ Object

rubocop:todo Metrics/AbcSize


277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'app/models/well.rb', line 277

def update_gender_markers!(gender_markers, resource) # rubocop:todo Metrics/AbcSize
  if well_attribute.gender_markers == gender_markers
    gender_marker_event = events.where(family: 'update_gender_markers').order('id desc').first
    if gender_marker_event.blank?
      events.update_gender_markers!(resource)
    elsif resource == 'SNP' && gender_marker_event.content != resource
      events.update_gender_markers!(resource)
    end
  else
    events.update_gender_markers!(resource)
  end

  well_attribute.update!(gender_markers: gender_markers)
end

#update_sequenom_count!(sequenom_count, resource) ⇒ Object


292
293
294
295
# File 'app/models/well.rb', line 292

def update_sequenom_count!(sequenom_count, resource)
  events.update_sequenom_count!(resource) unless well_attribute.sequenom_count == sequenom_count
  well_attribute.update!(sequenom_count: sequenom_count)
end

#update_volume(volume_change) ⇒ Object


255
256
257
258
# File 'app/models/well.rb', line 255

def update_volume(volume_change)
  value_current_volume = get_current_volume.nil? ? 0 : get_current_volume
  set_current_volume([0, value_current_volume + volume_change].max)
end

#well_attributeObject


232
233
234
# File 'app/models/well.rb', line 232

def well_attribute
  super || build_well_attribute
end