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, StateMachine Classes: 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

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


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

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)

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

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


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

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

#cross_project?Boolean

Returns:

  • (Boolean)

226
227
228
# File 'app/models/submission.rb', line 226

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

#cross_study?Boolean

Returns:

  • (Boolean)

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

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)

132
133
134
135
136
137
138
139
140
141
# File 'app/models/submission.rb', line 132

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

#json_rootObject


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

def json_root
  'submission'
end

#multiplexed?Boolean

Returns:

  • (Boolean)

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

def multiplexed?
  orders.any? { |o| RequestType.find(o.request_types).any?(&:for_multiplexing?) }
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.


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

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


210
211
212
# File 'app/models/submission.rb', line 210

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


178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'app/models/submission.rb', line 178

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


144
145
146
# File 'app/models/submission.rb', line 144

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

#not_ready_samples_namesObject


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

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


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

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)

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

def prevent_destruction_unless_building?
  return 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


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

def request_type_ids
  return [] if orders.blank?

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

#requests_cancellable?Boolean

Returns:

  • (Boolean)

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

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

#study_namesObject


214
215
216
217
218
219
220
221
222
223
224
# File 'app/models/submission.rb', line 214

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


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

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)


238
239
240
# File 'app/models/submission.rb', line 238

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