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: AccessionBehaviour, AssetGroupBehaviour, AssetSubmissionFinder, Crossable, DelayedJobBehaviour, FlexibleRequestGraph, LinearRequestGraph, Priorities, ProjectValidation, RequestOptionsBehaviour, ScrnaCoreCdnaPrepFeasibilityCalculator, ScrnaCoreCdnaPrepFeasibilityValidator, StateMachine, ValidationsByTemplateName Classes: OrderPresenter, PresenterSkeleton, SubmissionCreator, SubmissionPresenter

Constant Summary collapse

PER_ORDER_REQUEST_OPTIONS =
%w[pre_capture_plex_level gigabases_expected].freeze

Constants included from StateMachine

StateMachine::UnprocessedStates

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



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

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)


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

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



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

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

#cross_project?Boolean

Returns:

  • (Boolean)


231
232
233
# File 'app/models/submission.rb', line 231

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

#cross_study?Boolean

Returns:

  • (Boolean)


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

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)


136
137
138
139
140
141
142
143
144
145
# File 'app/models/submission.rb', line 136

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



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

def friendly_name
  name
end

#json_rootObject



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

def json_root
  'submission'
end

#multiplexed?Boolean

Returns:

  • (Boolean)


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

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.



131
132
133
134
# File 'app/models/submission.rb', line 131

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



215
216
217
# File 'app/models/submission.rb', line 215

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



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'app/models/submission.rb', line 183

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



148
149
150
# File 'app/models/submission.rb', line 148

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

#not_ready_samples_namesObject



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

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



167
168
169
# File 'app/models/submission.rb', line 167

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)


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

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



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

def request_type_ids
  return [] if orders.blank?

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

#requests_cancellable?Boolean

Returns:

  • (Boolean)


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

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

#study_namesObject



219
220
221
222
223
224
225
226
227
228
229
# File 'app/models/submission.rb', line 219

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



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

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)



243
244
245
# File 'app/models/submission.rb', line 243

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