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, 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)


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

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

#cross_study?Boolean

Returns:

  • (Boolean)


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

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)


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

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



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

def json_root
  'submission'
end

#multiplexed?Boolean

Returns:

  • (Boolean)


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

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.



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

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



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

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



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
207
# File 'app/models/submission.rb', line 179

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



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

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

#not_ready_samples_namesObject



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

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



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

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



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

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



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

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)



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

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