Class: SampleManifestExcel::Upload::Processor::TubeRack

Inherits:
Base
  • Object
show all
Includes:
ActiveModel::Validations, CsvParserClient
Defined in:
app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb

Overview

Used for processing the upload of sample manifests. Contains behaviour specific to processing ‘Tube Rack’ manifests. Had to explicitly specify the namespace for Base here otherwise it picks up Upload::Base

Constant Summary

Constants inherited from Base

Base::MANDATORY_FIELDS

Instance Attribute Summary collapse

Attributes inherited from Base

#upload

Instance Method Summary collapse

Methods included from CsvParserClient

get_tube_rack_scan_results, no_read?, remove_no_read_results

Methods inherited from Base

#aliquots_updated?, #create_samples_if_not_present, #downstream_aliquots_updated?, #sample_manifest_updated?, #samples_updated?, #samples_valid?, #substitutions, #type, #update_sample_manifest, #update_samples_and_aliquots

Constructor Details

#initialize(upload) ⇒ TubeRack

Returns a new instance of TubeRack.



19
20
21
22
23
24
25
26
27
28
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 19

def initialize(upload)
  super(upload)
  @tube_rack_information_processed = false
  @tube_rack_barcodes_from_manifest = retrieve_tube_rack_barcodes_from_manifest
  return if @tube_rack_barcodes_from_manifest.nil?

  # The following assumes that the first column of the manifest contains the tube barcode
  @tube_barcodes_from_manifest = upload.data.column(1).compact
  @tube_rack_information_previously_processed = check_if_tube_racks_present
end

Instance Attribute Details

#tube_rack_barcodes_from_manifestObject (readonly)

Returns the value of attribute tube_rack_barcodes_from_manifest.



17
18
19
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 17

def tube_rack_barcodes_from_manifest
  @tube_rack_barcodes_from_manifest
end

Instance Method Details

#check_foreign_barcode_unique(foreign_barcode_format, value) ⇒ Object



167
168
169
170
171
172
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 167

def check_foreign_barcode_unique(foreign_barcode_format, value)
  return true unless Barcode.exists_for_format?(foreign_barcode_format, value)

  upload.errors.add(:base, 'foreign barcode is already in use.')
  false
end

#check_if_tube_racks_presentObject

if a tube rack record already exists for any of the rack barcodes in the manifest, it has been processed before and should not be re-processed



57
58
59
60
61
62
63
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 57

def check_if_tube_racks_present
  @tube_rack_barcodes_from_manifest.each do |barcode|
    existing_barcode_record = Barcode.includes(:asset).find_by(barcode:)
    return true if !existing_barcode_record.nil? && !existing_barcode_record.asset.nil?
  end
  false
end

#create_barcodes_for_existing_tubesObject

rubocop:todo Metrics/MethodLength



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 149

def create_barcodes_for_existing_tubes # rubocop:todo Metrics/MethodLength
  upload.rows.each do |row|
    tube_barcode = row.value('tube_barcode')
    tube = row.labware

    # TODO: the below foreign barcode checks are duplicated in sanger_tube_id specialised field file - refactor
    barcode_format = Barcode.matching_barcode_format(tube_barcode)
    if barcode_format.nil?
      error_message = "The tube barcode '#{tube_barcode}' is not a recognised format."
      upload.errors.add(:base, error_message)
      return false
    else
      return false unless check_foreign_barcode_unique(barcode_format, tube_barcode)
    end
    Barcode.create!(asset_id: tube.id, barcode: tube_barcode, format: barcode_format)
  end
end

#create_tube_rack_if_not_existing(tube_rack_barcode) ⇒ Object

rubocop:todo Metrics/MethodLength



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 126

def create_tube_rack_if_not_existing(tube_rack_barcode) # rubocop:todo Metrics/MethodLength
  barcode = Barcode.includes(:asset).find_by(asset_id: tube_rack_barcode)

  if barcode.nil?
    # TODO: Purpose should be set based on what's selected when generating the manifest
    # https://github.com/sanger/sequencescape/issues/2469
    purpose = Purpose.where(target_type: 'TubeRack', size: @rack_size).first
    tube_rack = ::TubeRack.create!(size: @rack_size, plate_purpose_id: purpose&.id)

    barcode_format = Barcode.matching_barcode_format(tube_rack_barcode)
    if barcode_format.nil?
      error_message = "The tube rack barcode '#{tube_rack_barcode}' is not a recognised format."
      upload.errors.add(:base, error_message)
      return nil
    end
    Barcode.create!(labware: tube_rack, barcode: tube_rack_barcode, format: barcode_format)
  else
    tube_rack = barcode.labware
  end

  tube_rack
end


103
104
105
106
107
108
109
110
111
112
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 103

def create_tube_racks_and_link_tubes
  rack_barcode_to_tube_rack = create_tube_racks_if_not_existing
  return false if rack_barcode_to_tube_rack.nil?

  success = create_barcodes_for_existing_tubes
  return false unless success

  link_tubes_to_racks(rack_barcode_to_tube_rack)
  true
end

#create_tube_racks_if_not_existingObject



114
115
116
117
118
119
120
121
122
123
124
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 114

def create_tube_racks_if_not_existing
  rack_barcode_to_tube_rack = {}
  @tube_rack_barcodes_from_manifest.each do |tube_rack_barcode|
    created_rack = create_tube_rack_if_not_existing(tube_rack_barcode)
    return nil if created_rack.nil?

    rack_barcode_to_tube_rack[tube_rack_barcode] = created_rack
  end

  rack_barcode_to_tube_rack
end


174
175
176
177
178
179
180
181
182
183
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 174

def link_tubes_to_racks(rack_barcode_to_tube_rack)
  upload.rows.each do |row|
    tube_barcode = row.value('tube_barcode')
    tube = row.labware
    tube_rack_barcode = @tube_barcode_to_rack_barcode[tube_barcode]
    tube_rack = rack_barcode_to_tube_rack[tube_rack_barcode]
    tube_barcode_to_coordinate = @rack_barcode_to_scan_results[tube_rack_barcode]
    RackedTube.create!(tube_rack: tube_rack, tube: tube, coordinate: tube_barcode_to_coordinate[tube_barcode])
  end
end

#processed?Boolean

Returns:

  • (Boolean)


185
186
187
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 185

def processed?
  samples_updated? && sample_manifest_updated? && aliquots_updated? && tube_rack_information_processed?
end

#retrieve_scan_resultsObject



65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 65

def retrieve_scan_results
  @rack_barcode_to_scan_results = {}
  @tube_barcode_to_rack_barcode = {}
  @tube_rack_barcodes_from_manifest.each do |tube_rack_barcode|
    results = ::CsvParserClient.get_tube_rack_scan_results(tube_rack_barcode, upload)
    return false if results.nil?

    @rack_barcode_to_scan_results[tube_rack_barcode] = results
    results.keys.each { |tube_barcode| @tube_barcode_to_rack_barcode[tube_barcode] = tube_rack_barcode }
  end

  true
end

#retrieve_tube_rack_barcodes_from_manifestObject



48
49
50
51
52
53
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 48

def retrieve_tube_rack_barcodes_from_manifest
  rack_barcodes_list = upload.data.description_info.select { |key| key.start_with?('Rack barcode (') }.values
  return nil if rack_barcodes_list.any?(nil)

  rack_barcodes_list
end

#run(tag_group) ⇒ Object

rubocop:todo Metrics/MethodLength



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 30

def run(tag_group) # rubocop:todo Metrics/MethodLength
  return unless valid?

  unless @tube_rack_information_previously_processed
    @rack_size = upload.sample_manifest.tube_rack_purpose.size
    unless retrieve_scan_results && validate_against_scan_results &&
             validate_coordinates(@rack_size, @rack_barcode_to_scan_results)
      return
    end

    success = create_tube_racks_and_link_tubes

    @tube_rack_information_processed = true if success
  end
  update_samples_and_aliquots(tag_group)
  update_sample_manifest
end

#tube_rack_information_processed?Boolean

Returns:

  • (Boolean)


189
190
191
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 189

def tube_rack_information_processed?
  @tube_rack_information_processed || @tube_rack_information_previously_processed
end

#validate_against_scan_resultsObject



79
80
81
82
83
84
85
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 79

def validate_against_scan_results
  return true if @tube_barcodes_from_manifest.sort == @tube_barcode_to_rack_barcode.keys.sort

  error_message = 'The scan and the manifest do not contain identical tube barcodes.'
  upload.errors.add(:base, error_message)
  false
end

#validate_coordinates(rack_size, rack_barcode_to_scan_results) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 87

def validate_coordinates(rack_size, rack_barcode_to_scan_results)
  coordinates = rack_barcode_to_scan_results.values.map(&:values).flatten
  invalid_coordinates = ::TubeRack.invalid_coordinates(rack_size, coordinates)

  return true if invalid_coordinates.empty?

  error_message =
    # rubocop:todo Layout/LineLength
    "The following coordinates in the scan are not valid for a tube rack of size #{rack_size}: #{invalid_coordinates}."

  # rubocop:enable Layout/LineLength
  upload.errors.add(:base, error_message)

  false
end