Class: SampleManifestExcel::Upload::Processor::Plate

Inherits:
Base
  • Object
show all
Includes:
RetentionInstructionHelper
Defined in:
app/sample_manifest_excel/sample_manifest_excel/upload/processor/plate.rb

Overview

TODO: had to explicitly specify the namespace for Base here otherwise it picks up Upload::Base

Processor to handle plate manifest uploads.

Constant Summary

Constants inherited from Base

Base::MANDATORY_FIELDS

Instance Attribute Summary

Attributes inherited from Base

#upload

Instance Method Summary collapse

Methods included from RetentionInstructionHelper

#find_retention_instruction_from_key, #find_retention_instruction_key_for_value, #find_retention_instruction_to_display, #retention_instruction_option_for_select

Methods inherited from Base

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

Constructor Details

This class inherits a constructor from SampleManifestExcel::Upload::Processor::Base

Instance Method Details

#check_for_barcodes_uniqueObject

For plate manifests the barcodes (sanger plate id column) should be the same for each well from the same plate, and different for each plate. Uniqueness of foreign barcodes in the database is checked in the specialised field sanger_plate_id.



17
18
19
20
21
22
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/plate.rb', line 17

def check_for_barcodes_unique
  duplicated_barcode_row, err_msg = duplicate_barcodes
  return if duplicated_barcode_row.nil?

  errors.add(:base, "Barcode duplicated at row: #{duplicated_barcode_row.number}. #{err_msg}")
end

#check_for_retention_instruction_by_plateObject

A subset of the plate manifests (stock plates not library plates) are required to have a retention instruction column to describe how they should be disposed of. The column should have the same value for all manifest rows for the same plate.



70
71
72
73
74
75
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/plate.rb', line 70

def check_for_retention_instruction_by_plate
  retention_error_row, err_msg = non_matching_retention_instructions_for_plates
  return if retention_error_row.nil?

  errors.add(:base, "Retention instruction checks failed at row: #{retention_error_row.number}. #{err_msg}")
end

#check_row_retention_value(row, plate_barcode, retention_instructions) ⇒ Object

rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/plate.rb', line 104

def check_row_retention_value(row, plate_barcode, retention_instructions) # rubocop:todo Metrics/MethodLength
  # if present the column is mandatory
  row_retention_value = row.value('retention_instruction')
  return 'Value cannot be blank.' if row_retention_value.nil?

  # Check that a plate has only one retention instruction value
  retention_instruction_key = find_retention_instruction_key_for_value(row_retention_value)
  return "Invalid retention instruction #{retention_instruction_key}" if retention_instruction_key.blank?
  if retention_instructions.key?(plate_barcode)
    if retention_instructions[plate_barcode] != retention_instruction_key
      return "Plate (#{plate_barcode}) cannot have different retention instruction values."
    end
  else
    # first time we are seeing this plate, add it to plate retentions hash
    retention_instructions[plate_barcode] = retention_instruction_key
  end
  nil
end

#duplicate_barcodesObject

Return the row of the first encountered barcode mismatch rubocop:todo Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/AbcSize



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/plate.rb', line 26

def duplicate_barcodes # rubocop:todo Metrics/CyclomaticComplexity
  return nil, nil unless upload.respond_to?(:rows)

  unique_bcs = {}
  unique_plates = {}
  upload.rows.each do |row|
    next if row.columns.blank? || row.data.blank?

    plate_barcode = row.value('sanger_plate_id')
    sample_id = row.value('sanger_sample_id')
    next if plate_barcode.nil? || sample_id.nil?

    plate_id_for_sample = find_plate_id_for_sample_id(sample_id)
    next if plate_id_for_sample.nil?

    # Check that a barcode is used for only one plate
    if unique_bcs.key?(plate_barcode)
      err_msg = 'Barcode is used in multiple plates.'
      return row, err_msg unless unique_bcs[plate_barcode] == plate_id_for_sample
    else
      unique_bcs[plate_barcode] = plate_id_for_sample
    end

    # Check that a plate has only one barcode
    if unique_plates.key?(plate_id_for_sample)
      err_msg = 'Plate has multiple barcodes.'
      return row, err_msg unless unique_plates[plate_id_for_sample] == plate_barcode
    else
      unique_plates[plate_id_for_sample] = plate_barcode
    end
  end
  [nil, nil]
end

#find_plate_id_for_sample_id(sample_id) ⇒ Object

rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity



62
63
64
65
# File 'app/sample_manifest_excel/sample_manifest_excel/upload/processor/plate.rb', line 62

def find_plate_id_for_sample_id(sample_id)
  sample_manifest_asset = SampleManifestAsset.find_by(sanger_sample_id: sample_id)
  sample_manifest_asset.labware&.id
end

#non_matching_retention_instructions_for_platesObject

rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength



78
79
80
81
82
83
84
85
86
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/plate.rb', line 78

def non_matching_retention_instructions_for_plates
  return nil, nil unless upload.respond_to?(:rows)

  # Initialize empty retention_instructions hash to store retention instructions
  upload
    .rows
    .each_with_object({}) do |row, retention_instructions|
      # ignore empty rows and skip if the retention column is not present
      if row.columns.blank? || row.data.blank? || row.columns.extract(['retention_instruction']).count.zero?
        next
      end

      plate_barcode = row.value('sanger_plate_id')
      sample_id = row.value('sanger_sample_id')

      # ignore rows where primary sample fields have not been filled in
      next unless plate_barcode.present? && sample_id.present?

      # check the row retention instruction is valid
      err_msg = check_row_retention_value(row, plate_barcode, retention_instructions)
      return row, err_msg if err_msg.present?
    end
  [nil, nil]
end