Class: Aliquot
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Aliquot
- Includes:
- DataForSubstitution, AliquotIndexer::AliquotScopes, Api::AliquotIo::Extensions, Api::Messages::FlowcellIo::AliquotExtensions, Api::Messages::QcResultIo::AliquotExtensions, Uuid::Uuidable
- Defined in:
- app/models/aliquot.rb
Overview
A note on tags: Aliquots can have up to two tags attached, the i7 (tag) and the i5(tag2) Tags are short DNA sequences which can be used to track samples following pooling. If two samples with the same tags are pooled together it becomes impossible to distinguish between them. To avoid this we have an index which ensures unique tags are maintained per pool. (Limitation: This restriction assumes that each oligo sequence is represented only once in the database. This is not the case, so additional slower checks are required where cross tag group pools are possible) MySQL indexes treat NULL values as non identical, so -1 (UNASSIGNED_TAG) is used to represent an untagged well. We have some performance optimizations in place to avoid trying to look up tag -1
Defined Under Namespace
Modules: Aliquotable, DataForSubstitution, DeprecatedBehaviours, Remover Classes: InsertSize, TagClash
Constant Summary collapse
- TAG_COUNT_NAMES =
%w[Untagged Single Dual].freeze
- UNASSIGNED_TAG =
It may have a tag but not necessarily. If it does, however, that tag needs to be unique within the receptacle. To ensure that there can only be one untagged aliquot present in a receptacle we use a special value for tag_id, rather than NULL which does not work in MySQL. It also works because the unassigned tag ID never gets matched for a Tag and so the result is nil!
-1
Class Method Summary collapse
-
.count_by_project_cost_code ⇒ Object
returns a hash, where keys are cost_codes and values are number of aliquots related to particular cost code {'cost_code_1' => 20, 'cost_code_2' => 3, 'cost_code_3' => 8 } this one does not work, as project is not always there: joins(project: :project_metadata).group(“project_metadata.project_cost_code”).count.
-
.equivalent_attributes ⇒ Object
Returns a list of attributes which must be the same for two Aliquots to be considered #equivalent? Generated dynamically to avoid accidental introduction of false positives when new columns are added.
Instance Method Summary collapse
- #aliquot_index_value ⇒ Object
- #created_with_request_options ⇒ Object
-
#dup(params = {}) ⇒ Object
Cloning an aliquot should unset the receptacle ID because otherwise it won't get reassigned.
-
#equivalent?(other, list_of_aliquot_attributes_to_consider_a_duplicate = nil) ⇒ Boolean
Unlike the above methods, which allow untagged to match with tagged, this looks for exact matches only.
-
#matches?(object) ⇒ Boolean
rubocop:todo Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/AbcSize.
-
#no_tag1? ⇒ Boolean
Validating the uniqueness of tags in rails was causing issues, as it was resulting the in the preform_transfer_of_contents in transfer request to fail, without any visible sign that something had gone wrong.
- #no_tag2? ⇒ Boolean
- #no_tags? ⇒ Boolean
-
#poly_metadata ⇒ ActiveRecord::Relation
A collection of PolyMetadatum records.
- #set_library(force: false) ⇒ Object
-
#tag ⇒ Object
Optimization: Avoids us hitting the database for untagged aliquots.
- #tag1? ⇒ Boolean
- #tag2 ⇒ Object
- #tag2? ⇒ Boolean
- #tag_count_name ⇒ Object
- #tags? ⇒ Boolean
- #tags_and_tag_depth_combination ⇒ Object
- #tags_combination ⇒ Object
- #update_quality(suboptimal_quality) ⇒ Object
Methods included from DataForSubstitution
#changes, #generate_substitution_hash, #original_tag2_id, #original_tag_id, #other_attributes_for_substitution, #substitute_tag2_id, #substitute_tag_id, #substitution_hash, #tag2_id_substitution, #tag_id_substitution
Methods included from Api::AliquotIo::Extensions
Methods included from AliquotIndexer::AliquotScopes
Methods included from Uuid::Uuidable
included, #unsaved_uuid!, #uuid
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
.count_by_project_cost_code ⇒ Object
returns a hash, where keys are cost_codes and values are number of aliquots related to particular cost code {'cost_code_1' => 20, 'cost_code_2' => 3, 'cost_code_3' => 8 } this one does not work, as project is not always there: joins(project: :project_metadata).group(“project_metadata.project_cost_code”).count
102 103 104 105 106 107 |
# File 'app/models/aliquot.rb', line 102 def self.count_by_project_cost_code joins('LEFT JOIN projects ON aliquots.project_id = projects.id') .joins('LEFT JOIN project_metadata ON project_metadata.project_id = projects.id') .group('project_metadata.project_cost_code') .count end |
.equivalent_attributes ⇒ Object
Returns a list of attributes which must be the same for two Aliquots to be considered #equivalent? Generated dynamically to avoid accidental introduction of false positives when new columns are added
112 113 114 |
# File 'app/models/aliquot.rb', line 112 def self.equivalent_attributes @equivalent_attributes ||= attribute_names - %w[id receptacle_id created_at updated_at] end |
Instance Method Details
#aliquot_index_value ⇒ Object
116 117 118 |
# File 'app/models/aliquot.rb', line 116 def aliquot_index_value aliquot_index.try(:aliquot_index) end |
#created_with_request_options ⇒ Object
120 121 122 123 124 125 126 |
# File 'app/models/aliquot.rb', line 120 def { fragment_size_required_from: insert_size_from, fragment_size_required_to: insert_size_to, library_type: library_type } end |
#dup(params = {}) ⇒ Object
Cloning an aliquot should unset the receptacle ID because otherwise it won't get reassigned. We should also reset the timestamp information as this is a new aliquot really. Any options passed in as parameters will override the aliquot defaults
184 185 186 |
# File 'app/models/aliquot.rb', line 184 def dup(params = {}) super().tap { |cloned_aliquot| cloned_aliquot.assign_attributes(params) } end |
#equivalent?(other, list_of_aliquot_attributes_to_consider_a_duplicate = nil) ⇒ Boolean
Unlike the above methods, which allow untagged to match with tagged, this looks for exact matches only. By default only id, timestamps and receptacles are excluded, but this can be overridden by passing in a specific list of attributes to check against.
221 222 223 224 |
# File 'app/models/aliquot.rb', line 221 def equivalent?(other, list_of_aliquot_attributes_to_consider_a_duplicate = nil) attributes_to_check = list_of_aliquot_attributes_to_consider_a_duplicate || Aliquot.equivalent_attributes attributes_to_check.all? { |attrib| send(attrib) == other.send(attrib) } end |
#matches?(object) ⇒ Boolean
rubocop:todo Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/AbcSize
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'app/models/aliquot.rb', line 194 def matches?(object) # rubocop:todo Metrics/CyclomaticComplexity # NOTE: This function is directional, and assumes that the downstream aliquot # is checking the upstream aliquot if sample_id != object.sample_id false # The samples don't match elsif object.library_id.present? && (library_id != object.library_id) false # Our libraries don't match. elsif object.bait_library_id.present? && (bait_library_id != object.bait_library_id) false # We have different bait libraries elsif (no_tag1? && object.tag1?) || (no_tag2? && object.tag2?) # rubocop:todo Layout/LineLength raise StandardError, 'Tag missing from downstream aliquot' # The downstream aliquot is untagged, but is tagged upstream. Something is wrong! # rubocop:enable Layout/LineLength elsif object. true # The upstream aliquot was untagged, we don't need to check tags else # rubocop:todo Layout/LineLength (object.no_tag1? || (tag_id == object.tag_id)) && (object.no_tag2? || (tag2_id == object.tag2_id)) # Both aliquots are tagged, we need to check if they match # rubocop:enable Layout/LineLength end end |
#no_tag1? ⇒ Boolean
Validating the uniqueness of tags in rails was causing issues, as it was resulting the in the preform_transfer_of_contents in transfer request to fail, without any visible sign that something had gone wrong. This essentially meant that tag clashes would result in sample dropouts. (presumably because << triggers save not save!)
132 133 134 |
# File 'app/models/aliquot.rb', line 132 def no_tag1? tag_id == UNASSIGNED_TAG || (tag_id.nil? && tag.nil?) end |
#no_tag2? ⇒ Boolean
140 141 142 |
# File 'app/models/aliquot.rb', line 140 def no_tag2? tag2_id == UNASSIGNED_TAG || (tag2_id.nil? && tag2.nil?) end |
#no_tags? ⇒ Boolean
152 153 154 |
# File 'app/models/aliquot.rb', line 152 def no_tag1? && no_tag2? end |
#poly_metadata ⇒ ActiveRecord::Relation
Returns a collection of PolyMetadatum records.
227 228 229 |
# File 'app/models/aliquot.rb', line 227 def PolyMetadatum.where(metadatable_id: id, metadatable_type: self.class.name) end |
#set_library(force: false) ⇒ Object
177 178 179 |
# File 'app/models/aliquot.rb', line 177 def set_library(force: false) self.library = receptacle if library.nil? || force end |
#tag ⇒ Object
Optimization: Avoids us hitting the database for untagged aliquots
169 170 171 |
# File 'app/models/aliquot.rb', line 169 def tag super unless tag_id == UNASSIGNED_TAG end |
#tag1? ⇒ Boolean
136 137 138 |
# File 'app/models/aliquot.rb', line 136 def tag1? !no_tag1? end |
#tag2 ⇒ Object
173 174 175 |
# File 'app/models/aliquot.rb', line 173 def tag2 super unless tag2_id == UNASSIGNED_TAG end |
#tag2? ⇒ Boolean
144 145 146 |
# File 'app/models/aliquot.rb', line 144 def tag2? !no_tag2? end |
#tag_count_name ⇒ Object
164 165 166 |
# File 'app/models/aliquot.rb', line 164 def tag_count_name TAG_COUNT_NAMES[tag_count] end |
#tags? ⇒ Boolean
148 149 150 |
# File 'app/models/aliquot.rb', line 148 def ! end |
#tags_and_tag_depth_combination ⇒ Object
160 161 162 |
# File 'app/models/aliquot.rb', line 160 def [tag.try(:oligo), tag2.try(:oligo), tag_depth] end |
#tags_combination ⇒ Object
156 157 158 |
# File 'app/models/aliquot.rb', line 156 def [tag.try(:oligo), tag2.try(:oligo)] end |
#update_quality(suboptimal_quality) ⇒ Object
188 189 190 191 |
# File 'app/models/aliquot.rb', line 188 def update_quality(suboptimal_quality) self.suboptimal = suboptimal_quality save! end |