sanger/sequencescape

View on GitHub
app/models/sample_manifest/core_behaviour.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
94%
# frozen_string_literal: true
module SampleManifest::CoreBehaviour
  BEHAVIOURS = %w[1dtube plate multiplexed_library library library_plate tube_rack].freeze

  # Include in cores which exhibit the default behaviour
  module NoSpecializedValidation
    def validate_specialized_fields(*args)
    end

    def specialized_fields(*_args)
      {}
    end
  end

  module Shared
    def self.included(base)
      base.class_eval do
        delegate :create_sample, to: :@manifest
        delegate :samples, :sample_manifest_assets, :barcodes, :study, to: :@manifest
        delegate :count, to: :@manifest
        delegate :study, to: :@manifest
        delegate :purpose, to: :@manifest
      end
    end

    def generate_sanger_ids(count = 1)
      Array.new(count) { SangerSampleId::Factory.instance.next! }
    end

    def details(&block)
      details_array.each(&block)
    end
  end

  # The samples get registered in the stock resource table at the end of manifest upload and processing
  # (It used to happen here)
  module StockAssets
    # Used in manifest upload code to insert the sample and aliquot into the database.
    # The receptacle and sanger_sample_id already exist as they are inserted upfront when the manifest is generated.
    # tag_depth is set on the aliquot to avoid tag clash if a) pools are present, and b) if the samples are not tagged.
    # The assumption is made that samples passed to the below method are never tagged,
    # because we're in the 'StockAssets' module rather than the 'LibraryAssets' module.
    def generate_sample_and_aliquot(sanger_sample_id, receptacle)
      create_sample(sanger_sample_id).tap do |sample|
        tag_depth = tag_depth_for_sample(@manifest.pools, receptacle, sanger_sample_id)

        receptacle.aliquots.create!(sample: sample, study: study, tag_depth: tag_depth)

        study.samples << sample
      end
    end

    def stocks?
      true
    end

    # Assigns a tag_depth to a sample in a pool.
    # Tag_depth just needs to be a unique integer for each sample in the pool,
    # So we just use the index in the list of sample manifest assets in this receptacle.
    def tag_depth_for_sample(pools, receptacle, sanger_sample_id)
      return nil unless pools

      pools[receptacle].find_index { |sma| sma.sanger_sample_id == sanger_sample_id }
    end
  end

  # Used for ready-made libraries. Ensures that the library_id gets set
  module LibraryAssets
    def generate_sample_and_aliquot(sanger_sample_id, receptacle)
      create_sample(sanger_sample_id).tap do |sample|
        receptacle.aliquots.create!(sample: sample, study: study, library: receptacle)
        study.samples << sample
      end
    end

    def stocks?
      false
    end
  end

  def self.included(base)
    base.class_eval do
      delegate :details, :details_array, :validate_specialized_fields, :specialized_fields, to: :core_behaviour

      def self.supported_asset_type?(asset_type)
        asset_type.nil? || BEHAVIOURS.include?(asset_type)
      end
    end
  end

  def core_behaviour
    @core_behaviour ||= "::SampleManifest::#{behaviour_module}::Core".constantize.new(self)
  end

  private

  # rubocop:todo Metrics/MethodLength
  def behaviour_module # rubocop:todo Metrics/CyclomaticComplexity
    case asset_type
    when '1dtube'
      'SampleTubeBehaviour'
    when 'plate'
      'PlateBehaviour'
    when 'tube_rack'
      'TubeRackBehaviour'
    when 'multiplexed_library'
      'MultiplexedLibraryBehaviour'
    when 'library'
      'LibraryTubeBehaviour'
    when 'library_plate'
      'LibraryPlateBehaviour'
    when nil
      'UnspecifiedBehaviour'
    else
      raise StandardError, "Unknown core behaviour (#{asset_type.inspect}) for sample manifest"
    end
  end
  # rubocop:enable Metrics/MethodLength
end