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

An 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

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



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

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



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

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

#request_type_ids_listObject



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

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

Class Method Details

.render_classObject



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

def render_class
  Api::OrderIo
end

Instance Method Details

#add_comment(comment_str, user) ⇒ Object



285
286
287
288
289
290
291
292
# File 'app/models/order.rb', line 285

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



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

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

#all_samplesObject



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

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

#asset_uuidsObject



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

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

#assets=(assets_to_add) ⇒ Object



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

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

#autodetection_defaultObject



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

def autodetection_default
  false
end

#building?Boolean

Are we still able to modify this instance?

Returns:

  • (Boolean)


272
273
274
# File 'app/models/order.rb', line 272

def building?
  submission.nil?
end

#building_submission?Boolean

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

Returns:

  • (Boolean)


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

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

#calculated_request_metadata_by_request_key(request_type_key) ⇒ Object

Determines and returns calculated request metadata based on the request type key. This is to cover calculated metadata that does not require user input



177
178
179
180
181
182
183
184
185
186
# File 'app/models/order.rb', line 177

def (request_type_key)
  case request_type_key
  # Checking for the presence of allowance_band, as it is calculated for
  # the bulk submission only
  when 'limber_scrna_core_cdna_prep_gem_x_5p'
    request_options['allowance_band'].present? ? { 'allowance_band' => request_options['allowance_band'] } : {}
  else
    {}
  end
end

#collect_gigabases_expected?Boolean

Returns:

  • (Boolean)


281
282
283
# File 'app/models/order.rb', line 281

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

#complete_buildingObject



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

def complete_building
  check_project_details!
  complete_building_asset_group
end

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

rubocop:todo Metrics/MethodLength



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

def create_request_of_type!(request_type, attributes = {}) # rubocop:todo Metrics/AbcSize
   = request_type.(request_options)
   = .merge((request_type.key))
  request_type.create!(attributes) do |request|
    request.submission_id = submission_id
    request.study = study
    request.initial_project = project
    request.user = user
    request. = 
    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)


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

def cross_compatible?
  false
end

#cross_project_allowedObject



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

def cross_project_allowed
  false
end

#cross_study_allowedObject



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

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)


211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'app/models/order.rb', line 211

def duplicates_within(timespan) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
  matching_orders =
    Order
      .containing_samples(all_samples)
      .where(template_name:)
      .includes(:submission, assets: :samples)
      .where.not(orders: { 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



256
257
258
# File 'app/models/order.rb', line 256

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

#friendly_nameObject



294
295
296
# File 'app/models/order.rb', line 294

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

#generate_broadcast_eventObject



302
303
304
# File 'app/models/order.rb', line 302

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

#json_rootObject



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

def json_root
  'order'
end

#multiplexed?Boolean

Returns:

  • (Boolean)


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

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

#multiplier_for(request_type_id) ⇒ Object



171
172
173
# File 'app/models/order.rb', line 171

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



266
267
268
269
# File 'app/models/order.rb', line 266

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



311
312
313
# File 'app/models/order.rb', line 311

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

#on_delete_destroy_submissionObject



119
120
121
122
123
124
125
126
127
128
# File 'app/models/order.rb', line 119

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.



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

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.



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

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

#sequencing_order?Boolean

Returns true if this is an order for sequencing

Returns:

  • (Boolean)


277
278
279
# File 'app/models/order.rb', line 277

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

#study_is_activeObject



306
307
308
# File 'app/models/order.rb', line 306

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

#subject_typeObject



298
299
300
# File 'app/models/order.rb', line 298

def subject_type
  'order'
end