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

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.


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

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.


19
20
21
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 19

def tube_rack_barcodes_from_manifest
  @tube_rack_barcodes_from_manifest
end

Instance Method Details

#check_foreign_barcode_unique(foreign_barcode_format, value) ⇒ Object


171
172
173
174
175
176
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 171

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


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

def check_if_tube_racks_present
  @tube_rack_barcodes_from_manifest.each do |barcode|
    existing_barcode_record = Barcode.includes(:asset).find_by(barcode: 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


153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 153

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


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 130

def create_tube_rack_if_not_existing(tube_rack_barcode)
  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!(asset: tube_rack, barcode: tube_rack_barcode, format: barcode_format)
  else
    tube_rack = barcode.asset
  end

  tube_rack
end

107
108
109
110
111
112
113
114
115
116
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 107

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


118
119
120
121
122
123
124
125
126
127
128
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 118

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

178
179
180
181
182
183
184
185
186
187
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 178

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)

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

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

#retrieve_scan_resultsObject


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

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


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

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


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

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)

193
194
195
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/tube_rack.rb', line 193

def tube_rack_information_processed?
  @tube_rack_information_processed || @tube_rack_information_previously_processed
end

#validate_against_scan_resultsObject


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

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


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

def validate_coordinates(rack_size, rack_barcode_to_scan_results)
  list_of_coordinates = rack_barcode_to_scan_results.values.map(&:values).flatten
  list_of_validity = ::TubeRack.check_if_coordinates_valid(rack_size, list_of_coordinates)
  list_of_invalid_coordinates = []

  list_of_validity.each_with_index do |validity, index|
    list_of_invalid_coordinates << list_of_coordinates[index] unless validity
  end
  error_message =
    # rubocop:todo Layout/LineLength
    "The following coordinates in the scan are not valid for a tube rack of size #{rack_size}: #{list_of_invalid_coordinates}."

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

  true
end