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, #complete_building, #default_priority, #finalize_build!

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 Warren::BroadcastMessages

#broadcast, included, #queue_associated_for_broadcast, #queue_for_broadcast, #warren

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


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

def add_comment(description, user, title = nil)
  requests.each do |request|
    request.add_comment(description, user, title)
  end
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


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

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

#cross_study?Boolean


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

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)

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

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


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

def json_root
  'submission'
end

#multiplexed?Boolean


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

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.


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

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


206
207
208
# File 'app/models/submission.rb', line 206

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.


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

def next_requests_via_submission(request)
  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
    return 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


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

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

#not_ready_samples_namesObject


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

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.


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

def prevent_destruction_unless_building?
  return if building?

  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


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

def request_type_ids
  return [] if orders.blank?

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

#requests_cancellable?Boolean


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

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

#study_namesObject


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

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.aliquots.map { |al| al.study.try(: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


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

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