Class: Submission

Inherits:
ApplicationRecord show all
Extended by:
StateMachine
Includes:
ModelExtensions::Submission, DelayedJobBehaviour, Priorities, Uuid::Uuidable
Defined in:
app/models/submission.rb

Overview

A Submission collects multiple Orders together, to define a body of work. In the case of non-multiplexed requests the submission is largely redundant, but for multiplexed requests it usually helps define which assets will get pooled together at multiplexing. There are two Order subclasses which are important when it comes to submissions:

LinearSubmission: Most orders fall in this category. If the submission is multiplexed results in a single pool for the whole submissions.

FlexibleSubmission: Allows request types to specify their own pooling rules, which are used to define pools at the submission level.

While orders are mostly in charge of building their own requests, Submissions trigger this behaviour, and handle multiplexing between orders.

Defined Under Namespace

Modules: AssetGroupBehaviour, AssetSubmissionFinder, Crossable, DelayedJobBehaviour, FlexibleRequestGraph, LinearRequestGraph, Priorities, ProjectValidation, RequestOptionsBehaviour, ScrnaCoreCdnaPrepFeasibilityCalculator, ScrnaCoreCdnaPrepFeasibilityValidator, ScrnaCoreCdnaPrepPoolingPlanGenerator, StateMachine, ValidationsByTemplateName Classes: OrderPresenter, PresenterSkeleton, SubmissionCreator, SubmissionPresenter

Constant Summary collapse

PER_ORDER_REQUEST_OPTIONS =
%w[pre_capture_plex_level gigabases_expected].freeze
SCRNA_CORE_CDNA_PREP_GEM_X_5P =
'Limber-Htp - scRNA Core cDNA Prep GEM-X 5p'

Constants included from StateMachine

StateMachine::UNPROCESSED_STATES

Class Method Summary collapse

Instance Method Summary collapse

Methods included from StateMachine

extended

Methods included from Priorities

included, options, priorities

Methods included from DelayedJobBehaviour

#build_batch, #default_priority, #finalize_build!, #queue_submission_builder

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

extended

Class Method Details

.render_classObject

The class used to render warehouse messages



83
84
85
# File 'app/models/submission.rb', line 83

def self.render_class
  Api::SubmissionIo
end

Instance Method Details

#add_comment(description, user, title = nil) ⇒ Void

Adds the given comment to all requests in the submission

Parameters:

  • description (String)

    The comment to add to the submission

  • user (User)

    The user making the comment

Returns:

  • (Void)


105
106
107
# File 'app/models/submission.rb', line 105

def add_comment(description, user, title = nil)
  requests.each { |request| request.add_comment(description, user, title) }
end

#commentsObject

As mentioned above, comments are broken. Not quite sure why we're overriding it here



96
97
98
# File 'app/models/submission.rb', line 96

def comments
  orders.pluck(:comments).compact
end

#cross_project?Boolean

Returns:

  • (Boolean)


236
237
238
# File 'app/models/submission.rb', line 236

def cross_project?
  multiplexed? && orders.map(&:project_id).uniq.size > 1
end

#cross_study?Boolean

Returns:

  • (Boolean)


240
241
242
# File 'app/models/submission.rb', line 240

def cross_study?
  multiplexed? && orders.map(&:study_id).uniq.size > 1
end

#each_submission_warning {|store[:samples].uniq, store[:submissions].uniq| ... } ⇒ Object

Yields:

  • (store[:samples].uniq, store[:submissions].uniq)


141
142
143
144
145
146
147
148
149
150
# File 'app/models/submission.rb', line 141

def each_submission_warning
  store = { samples: [], submissions: [] }
  orders.each do |order|
    order.duplicates_within(1.month) do |samples, _orders, submissions|
      store[:samples].concat(samples)
      store[:submissions].concat(submissions)
    end
  end
  yield store[:samples].uniq, store[:submissions].uniq unless store[:samples].empty?
end

#friendly_nameObject



121
122
123
# File 'app/models/submission.rb', line 121

def friendly_name
  name
end

#json_rootObject



113
114
115
# File 'app/models/submission.rb', line 113

def json_root
  'submission'
end

#multiplexed?Boolean

Returns:

  • (Boolean)


125
126
127
# File 'app/models/submission.rb', line 125

def multiplexed?
  orders.any?(&:multiplexed?)
end

#multiplexed_labwareObject

Attempts to find the multiplexed asset (usually a multiplexed library tube) associated with the submission. Useful when trying to pool requests into a pre-existing tube at the end of the process.



136
137
138
139
# File 'app/models/submission.rb', line 136

def multiplexed_labware
  # All our multiplexed requests end up in a single asset, so we don't care which one we find.
  requests.joins(:request_type).find_by(request_types: { for_multiplexing: true })&.target_labware
end

#nameObject

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



220
221
222
# File 'app/models/submission.rb', line 220

def name
  super.presence || "##{id} #{study_names.truncate(128)}"
end

#next_requests_via_submission(request) ⇒ Array<Request>

You probably just want to call next_requests on request.

Returns the next requests in the submission along from the one provides. Eg. Providing a library creation request will return multiplexing requests, and multiplexing requests return sequencing requests. You may get back more than one request. This makes certain assumptions about request number in submissions, and uses request offsets and request types to tie requests together. rubocop:todo Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/AbcSize

Parameters:

  • request (Request)

    The request to find the next request for

Returns:

  • (Array<Request>)

    An array of downstream requests



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'app/models/submission.rb', line 188

def next_requests_via_submission(request) # rubocop:todo Metrics/CyclomaticComplexity
  raise "Request #{request.id} is not part of submission #{id}" unless request.submission_id == id

  # Pick out the siblings of the request, so we can work out where it is in the list, and all of
  # the requests in the subsequent request type, so that we can tie them up.  We order by ID
  # here so that the earliest requests, those created by the submission build, are always first;
  # any additional requests will have come from a sequencing batch being reset.
  all_requests = request_cache_for(request.request_type_id, request.next_request_type_id)
  sibling_requests = all_requests[request.request_type_id]
  next_possible_requests = all_requests[request.next_request_type_id]

  if request.for_multiplexing?
    # If we have no pooling behaviour specified, then we're pooling by submission.
    # We keep to the existing behaviour, to isolate risk
    return next_possible_requests if request.request_type.pooling_method.nil?

    # If we get here we've got custom pooling behaviour defined.
    index = request.request_type.pool_index_for_request(request)
    number_to_return = next_possible_requests.count / request.request_type.pool_count
    next_possible_requests.slice(index * number_to_return, number_to_return)
  else
    multiplier = multiplier_for(request.next_request_type_id)
    index = sibling_requests.select { |npr| npr.order_id.nil? || (npr.order_id == request.order_id) }.index(request)
    next_possible_requests.select { |npr| npr.order_id.nil? || (npr.order_id == request.order_id) }[
      index * multiplier,
      multiplier
    ]
  end
end

#not_ready_samplesObject

returns an array of samples, that potentially can not be included in submission



153
154
155
# File 'app/models/submission.rb', line 153

def not_ready_samples
  @not_ready_samples ||= orders.map(&:not_ready_samples).flatten
end

#not_ready_samples_namesObject



157
158
159
# File 'app/models/submission.rb', line 157

def not_ready_samples_names
  @not_ready_samples_names ||= not_ready_samples.map(&:name).join(', ')
end

#order_request_type_idsObject

Logged calls from: app/models/pre_capture_pool.rb:74



172
173
174
# File 'app/models/submission.rb', line 172

def order_request_type_ids
  orders.flat_map(&:request_types).uniq.compact
end

#prevent_destruction_unless_building?Boolean

Once submissions progress beyond building, destruction is a risky action and should be prevented.

Returns:

  • (Boolean)


88
89
90
91
92
93
# File 'app/models/submission.rb', line 88

def prevent_destruction_unless_building?
  return false if destroyable?

  errors.add(:base, "can only be destroyed when in the 'building' stage. Later submissions should be cancelled.")
  throw :abort
end

#request_type_idsObject

Deprecated.

This is no longer valid. Orders may now have different request_types



162
163
164
165
166
# File 'app/models/submission.rb', line 162

def request_type_ids
  return [] if orders.blank?

  orders.first.request_types.map(&:to_i)
end

#requests_cancellable?Boolean

Returns:

  • (Boolean)


109
110
111
# File 'app/models/submission.rb', line 109

def requests_cancellable?
  requests.all?(&:cancellable?)
end

#scrna_core_cdna_prep_gem_x_5p_submission?Boolean

Returns:

  • (Boolean)


129
130
131
# File 'app/models/submission.rb', line 129

def scrna_core_cdna_prep_gem_x_5p_submission?
  orders.first.template_name == SCRNA_CORE_CDNA_PREP_GEM_X_5P
end

#study_namesObject



224
225
226
227
228
229
230
231
232
233
234
# File 'app/models/submission.rb', line 224

def study_names
  # TODO: Should probably be re-factored, although we'll only fall back to the intensive code in the case of cross
  # study re-requests
  orders
    .map { |o| o.study.try(:name) || o.assets.map { |a| a.studies.pluck(:name) } }
    .flatten
    .compact
    .sort
    .uniq
    .join('|')
end

#subject_typeObject



117
118
119
# File 'app/models/submission.rb', line 117

def subject_type
  'submission'
end

#used_tagsArray<String,String>

Used tags returns an array of unique [i7_oligo, i5_oligo] used as part of the submission

Returns:

  • (Array<String,String>)

    Array of arrays of two strings, the i7 oligo (tag) followed by the i5 (tag2)



248
249
250
# File 'app/models/submission.rb', line 248

def used_tags
  aliquots.includes(:tag, :tag2).any_tags.distinct.pluck('tags.oligo', 'tag2s_aliquots.oligo')
end