Class: Order

Inherits:
ApplicationRecord show all
Includes:
ModelExtensions::Order, Submission::AccessionBehaviour, Submission::AssetGroupBehaviour, Submission::ProjectValidation, Submission::RequestOptionsBehaviour, Uuid::Uuidable
Defined in:
app/models/order.rb

Overview

And Order is used as the main means of requesting work in Sequencescape. Its key components are: Assets/AssetGroup: The assets on which the work will be conducted Study: The study for which work is being undertaken Project: The project who will be charged for the work Request options: The parameters for the request which will be built. eg. read length Request Types: An array of request type ids which will be built by the order.

This is populated based on the submission template used.

Submission: Multiple orders may be grouped together in a submission. This

associates the two sets of requests, and is usually used to determine
what gets pooled together during multiplexing. As a result, sequencing
requests may be shared between multiple orders.

Constant Summary collapse

AssetTypeError =

Ensure order methods behave correctly

Class.new(StandardError)
DEFAULT_ASSET_INPUT_METHODS =
['select an asset group'].freeze

Constants included from Submission::ProjectValidation

Submission::ProjectValidation::Error

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Submission::AccessionBehaviour

#can_check_data_release_and_accession?, #check_data_release_and_accession_for_submission, included, #request_types_require_accessioning?

Methods included from Submission::RequestOptionsBehaviour

included, #request_options=

Methods included from Submission::ProjectValidation

#checking_project?, included, #save_after_unmarshalling, #submittable?, #validating?

Methods included from Submission::AssetGroupBehaviour

#complete_building_asset_group, included

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

Instance Attribute Details

#asset_input_methodsObject


212
213
214
# File 'app/models/order.rb', line 212

def asset_input_methods
  @asset_input_methods ||= DEFAULT_ASSET_INPUT_METHODS
end

#info_differential=(value) ⇒ Object (writeonly)

Unused. Maintained because some submission templates attempt to set the info


36
37
38
# File 'app/models/order.rb', line 36

def info_differential=(value)
  @info_differential = value
end

#input_field_infosObject

Return the list of input fields to edit when creating a new submission Unless you are doing something fancy, fall back on the defaults


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

def input_field_infos
  @input_field_infos ||= FieldInfo.for_request_types(request_types_list.flatten)
end

#request_type_ids_listObject


208
209
210
# File 'app/models/order.rb', line 208

def request_type_ids_list
  @request_type_ids_list ||= [[]]
end

Class Method Details

.render_classObject


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

def render_class
  Api::OrderIO
end

Instance Method Details

#add_comment(comment_str, user) ⇒ Object


266
267
268
269
270
271
272
273
# File 'app/models/order.rb', line 266

def add_comment(comment_str, user)
  update!(comments: [comments, comment_str].compact.join('; '))

  submission
    .requests
    .for_order_including_submission_based_requests(self)
    .map { |request| request.add_comment(comment_str, user) }
end

#all_assetsObject


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

def all_assets
  pull_assets_from_asset_group if assets.empty? && asset_group.present?
  assets
end

#all_samplesObject


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

def all_samples
  # slightly less naive way
  all_assets.map(&:samples).flatten.uniq
end

#asset_uuidsObject


158
159
160
# File 'app/models/order.rb', line 158

def asset_uuids
  assets&.select(&:present?)&.map(&:uuid)
end

#assets=(assets_to_add) ⇒ Object


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

def assets=(assets_to_add)
  super(assets_to_add.map { |a| a.is_a?(Receptacle) ? a : a.receptacle })
end

#building?Boolean

Are we still able to modify this instance?

Returns:

  • (Boolean)

253
254
255
# File 'app/models/order.rb', line 253

def building?
  submission.nil?
end

#building_submission?Boolean

We can't destroy orders once the submission has been finalized for building

Returns:

  • (Boolean)

110
111
112
# File 'app/models/order.rb', line 110

def building_submission?
  throw :abort unless submission.building?
end

#collect_gigabases_expected?Boolean

Returns:

  • (Boolean)

262
263
264
# File 'app/models/order.rb', line 262

def collect_gigabases_expected?
  input_field_infos.any? { |k| k.key == :gigabases_expected }
end

#complete_buildingObject


100
101
102
103
# File 'app/models/order.rb', line 100

def complete_building
  check_project_details!
  complete_building_asset_group
end

#create_request_of_type!(request_type, attributes = {}) ⇒ Object

rubocop:todo Metrics/MethodLength


171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'app/models/order.rb', line 171

def create_request_of_type!(request_type, attributes = {}) # rubocop:todo Metrics/AbcSize
  em = request_type.(request_options)
  request_type.create!(attributes) do |request|
    request.submission_id = submission_id
    request.study = study
    request.initial_project = project
    request.user = user
    request. = em
    request.state = request_type.initial_state
    request.order = self

    if request.asset.present?
      unless asset_applicable_to_type?(request_type, request.asset)
        raise AssetTypeError, 'Asset type does not match that expected by request type.'
      end
    end
  end
end

#cross_compatible?Boolean

Returns:

  • (Boolean)

133
134
135
# File 'app/models/order.rb', line 133

def cross_compatible?
  false
end

#cross_project_allowedObject


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

def cross_project_allowed
  false
end

#cross_study_allowedObject


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

def cross_study_allowed
  false
end

#duplicates_within(timespan) {|matching_samples, matching_orders, matching_submissions| ... } ⇒ Object

rubocop:enable Metrics/MethodLength

Yields:

  • (matching_samples, matching_orders, matching_submissions)

192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'app/models/order.rb', line 192

def duplicates_within(timespan) # rubocop:todo Metrics/AbcSize
  matching_orders =
    Order
      .containing_samples(all_samples)
      .where(template_name: template_name)
      .includes(:submission, assets: :samples)
      .where.not(orders: { id: id })
      .where('orders.created_at > ?', Time.current - timespan)
  return false if matching_orders.empty?

  matching_samples = matching_orders.map(&:samples).flatten & all_samples
  matching_submissions = matching_orders.map(&:submission).uniq
  yield matching_samples, matching_orders, matching_submissions if block_given?
  true
end

#first_request_typeObject


237
238
239
# File 'app/models/order.rb', line 237

def first_request_type
  @first_request_type ||= RequestType.find(request_types.first)
end

#friendly_nameObject


275
276
277
# File 'app/models/order.rb', line 275

def friendly_name
  asset_group.try(:name) || asset_group_name || id
end

#generate_broadcast_eventObject


283
284
285
# File 'app/models/order.rb', line 283

def generate_broadcast_event
  BroadcastEvent::OrderMade.create!(seed: self, user: user)
end

#json_rootObject


154
155
156
# File 'app/models/order.rb', line 154

def json_root
  'order'
end

#multiplexed?Boolean

Returns:

  • (Boolean)

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

def multiplexed?
  RequestType.where(id: request_types).for_multiplexing.exists?
end

#multiplier_for(request_type_id) ⇒ Object


166
167
168
# File 'app/models/order.rb', line 166

def multiplier_for(request_type_id)
  (request_options.dig(:multiplier, request_type_id.to_s) || 1).to_i
end

#next_request_type_id(request_type_id) ⇒ Object


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

def next_request_type_id(request_type_id)
  request_type_ids = request_types.map(&:to_i)
  request_type_ids[request_type_ids.index(request_type_id) + 1]
end

#not_ready_samplesObject

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


292
293
294
# File 'app/models/order.rb', line 292

def not_ready_samples
  all_samples.reject(&:can_be_included_in_submission?)
end

#on_delete_destroy_submissionObject


114
115
116
117
118
119
120
121
122
123
# File 'app/models/order.rb', line 114

def on_delete_destroy_submission
  if building_submission?
    # After destroying an order, if it is the last order on it's submission
    # destroy the submission too.
    orders = submission.orders
    submission.destroy unless orders.size > 1
    return true
  end
  false
end

#request_types_listObject

request_type_ids_list is set for orders created by submission templates It is used by the input_field_infos section, which controls rendering form fields appropriate to each request type in the submission interface request_type_ids is calculated from this in the various sub-classes and gets persisted to the database, and used for the actual construction. TODO: Simplify this

  • There are a few attributes which all refer to loosely the same thing, a list of request type ids:

    • request_type_ids_list - Set by submission templates, but also recalculated on the fly and used in various

      methods
      
    • request_types_ids - Setter on order subclasses.

    • request_types - Serialized version on order, persisted in the database

  • The request_types on the database should become the authoritative source.

  • request_type_ids_list should just be a setter, which populates request_types. It may need to transform the input slightly. Ideally we eliminate this entirely, and be consistent between templates and orders

  • There appear to be several methods which essentially do the same thing. They should be unified.

  • I'm not even 100% how request_types_ids factors in.


233
234
235
# File 'app/models/order.rb', line 233

def request_types_list
  request_type_ids_list.map { |ids| RequestType.find(ids) }
end

#samplesObject

TODO: Figure out why eager loading aliquots/samples returns [] even when we limit order_assets to receptacles.


139
140
141
142
# File 'app/models/order.rb', line 139

def samples
  # naive way
  assets.map(&:samples).flatten.uniq
end

#sequencing_order?Boolean

Returns true if this is an order for sequencing

Returns:

  • (Boolean)

258
259
260
# File 'app/models/order.rb', line 258

def sequencing_order?
  RequestType.find(request_types).any?(&:sequencing?)
end

#study_is_activeObject


287
288
289
# File 'app/models/order.rb', line 287

def study_is_active
  errors.add(:study, 'is not active') if study.present? && !study.active?
end

#subject_typeObject


279
280
281
# File 'app/models/order.rb', line 279

def subject_type
  'order'
end