Class: Operation
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Operation
show all
- Includes:
- DataAssociator, FieldValuer, OperationPlanner
- Defined in:
- app/models/operation.rb
Overview
Class that represents an operation in the lab
Some very important methods include #input, #output, OperationStatus#error, #pass
Class Method Summary
collapse
Instance Method Summary
collapse
-
#activate ⇒ Object
-
#add_input(name, sample, container) ⇒ Object
Adds a new input to an operation, even if that operation doesn't specify the input in its definition.
-
#add_successor(opts) ⇒ Object
-
#create_field_type(name) ⇒ Object
TODO: this belongs elsewhere.
-
#create_field_value(name, item, sample, field_type) ⇒ Object
-
#create_input(name:, item:, sample:) ⇒ Object
-
#deactivate ⇒ Object
-
#destroy_field_values ⇒ Object
-
#find(name) ⇒ Object
-
#get_field_value(name, role = 'input') ⇒ FieldValue
-
#get_input(name) ⇒ FieldValue
-
#get_output(name) ⇒ FieldValue
-
#input(name) ⇒ FieldValue
-
#input_array(name) ⇒ Array<FieldValue>
Inputs as Array extended with IOList.
-
#input_data(input_name, data_name) ⇒ Object
-
#inputs ⇒ Array<FieldValue>
-
#leaf? ⇒ Boolean
-
#name ⇒ String
-
#nominal_cost ⇒ Object
-
#on_the_fly ⇒ Bool
Whether OperationType is on-the-fly.
-
#output(name) ⇒ FieldValue
-
#output_array(name) ⇒ Array<FieldValue>
Outputs as Array extended with IOList.
-
#output_data(output_name, data_name) ⇒ Object
-
#outputs ⇒ Array<FieldValue>
-
#parent_type ⇒ Object
interface with FieldValuer.
-
#pass(input_name, output_name = nil) ⇒ Operation
-
#plan ⇒ Plan
The plan that contains this Operation.
-
#precondition_value ⇒ Object
-
#predecessors ⇒ Object
-
#primed_predecessors ⇒ Object
-
#recurse(&block) ⇒ Object
-
#set_input(name, val, aft = nil) ⇒ Object
Assigns a Sample to an input.
-
#set_input_data(input_name, data_name, value) ⇒ Object
-
#set_output(name, val, aft = nil) ⇒ Object
Assigns a Sample to an output.
-
#set_output_data(output_name, data_name, value) ⇒ Object
-
#set_status_recursively(str) ⇒ Object
-
#siblings ⇒ Object
-
#successors ⇒ Object
-
#temporary ⇒ Object
-
#to_s ⇒ Object
-
#virtual? ⇒ Boolean
-
#with_input(name, sample) ⇒ Operation
Assigns a Sample to an input, choosing an appropriate allowable_field_type.
-
#with_output(name, sample) ⇒ Object
Assigns a Sample to an output, choosing an appropriate allowable_field_type.
-
#with_property(name, value) ⇒ Object
Assigns a value to an input parameter.
#append_notes, #associate, #associations, #data_associations, #get, #get_association, #lazy_associate, #modify, #notes, #notes=, #upload
Class Method Details
.step(ops = nil) ⇒ Object
337
338
339
340
341
342
|
# File 'app/models/operation.rb', line 337
def self.step(ops = nil)
ops ||= Operation.includes(:operation_type)
.where("status = 'waiting' OR status = 'deferred' OR status = 'delayed' OR status = 'pending'")
ops.each(&:step)
end
|
Instance Method Details
#activate ⇒ Object
317
318
319
320
321
322
323
324
325
|
# File 'app/models/operation.rb', line 317
def activate
set_status 'planning'
outputs.each do |output|
output.wires_as_source.each do |wire|
wire.active = true
wire.save
end
end
end
|
Adds a new input to an operation, even if that operation doesn't specify the input
in its definition. Useful for example when an operation determines which enzymes it
will use once launched.
127
128
129
130
131
132
133
134
135
|
# File 'app/models/operation.rb', line 127
def add_input(name, sample, container)
items = Item.where(sample_id: sample.id, object_type_id: container.id).reject(&:deleted?)
return nil if items.empty?
item = items.first
create_input(name: name, item: item, sample: sample)
item
end
|
#add_successor(opts) ⇒ Object
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
|
# File 'app/models/operation.rb', line 414
def add_successor(opts)
ot = OperationType.find_by(name: opts[:type])
op = ot.create_operation(
status: 'waiting',
user_id: user_id
)
plan.plan_associations.create(operation_id: op.id)
opts[:routing].each do |r|
ot.field_types.select { |ft| ft.routing == r[:symbol] }.each do |ft|
aft = ft.allowable_field_types[0]
op.set_property(ft.name, r[:sample], ft.role, false, aft)
end
end
raise "Could not find output #{opts[:from]} of #{operation_type.name}" unless output(opts[:from])
raise "Could not find input #{opts[:to]} of #{opts[:type]} (inputs = #{op.field_values.inspect})" unless op.input(opts[:to])
wire = Wire.new(
from_id: output(opts[:from]).id,
to_id: op.input(opts[:to]).id,
active: true
)
wire.save
end
|
#create_field_type(name) ⇒ Object
TODO: this belongs elsewhere
146
147
148
149
150
151
152
153
|
# File 'app/models/operation.rb', line 146
def create_field_type(name)
FieldType.new(
name: name,
ftype: 'sample',
parent_class: 'OperationType',
parent_id: nil
)
end
|
#create_field_value(name, item, sample, field_type) ⇒ Object
155
156
157
158
159
160
161
162
163
164
165
|
# File 'app/models/operation.rb', line 155
def create_field_value(name, item, sample, field_type)
FieldValue.new(
name: name,
child_item_id: item.id,
child_sample_id: sample.id,
role: 'input',
parent_class: 'Operation',
parent_id: id,
field_type_id: field_type.id
)
end
|
137
138
139
140
141
142
143
|
# File 'app/models/operation.rb', line 137
def create_input(name:, item:, sample:)
field_type = create_field_type(name)
field_type.save
field_value = create_field_value(name, item, sample, field_type)
field_value.save
end
|
#deactivate ⇒ Object
327
328
329
330
331
332
333
334
335
|
# File 'app/models/operation.rb', line 327
def deactivate
set_status 'unplanned'
outputs.each do |output|
output.wires_as_source.each do |wire|
wire.active = false
wire.save
end
end
end
|
#destroy_field_values ⇒ Object
27
28
29
30
31
32
33
34
35
36
37
38
|
# File 'app/models/operation.rb', line 27
def destroy_field_values
msg = "Cannot destroy operation #{id} because it has jobs associated with it"
raise msg unless JobAssociation.where(operation_id: id).empty?
fvs = FieldValue.where(parent_class: 'Operation', parent_id: id)
fvs.each do |fv|
Wire.where("from_id = #{fv.id} OR to_id = #{fv.id}").each do |wire|
wire.destroy
end
fv.destroy
end
end
|
#find(name) ⇒ Object
253
254
255
256
257
258
259
|
# File 'app/models/operation.rb', line 253
def find(name)
ops = []
recurse do |op|
ops << op if op.operation_type.name == name
end
ops
end
|
#get_field_value(name, role = 'input') ⇒ FieldValue
218
219
220
|
# File 'app/models/operation.rb', line 218
def get_field_value(name, role = 'input')
field_values.find { |fv| fv.name == name && fv.role == role }
end
|
179
180
181
|
# File 'app/models/operation.rb', line 179
def get_input(name)
inputs.find { |i| i.name == name }
end
|
185
186
187
|
# File 'app/models/operation.rb', line 185
def get_output(name)
outputs.find { |o| o.name == name }
end
|
191
192
193
|
# File 'app/models/operation.rb', line 191
def input(name)
get_input(name)
end
|
Inputs as Array extended with IOList
204
205
206
|
# File 'app/models/operation.rb', line 204
def input_array(name)
inputs.select { |i| i.name == name }.extend(IOList)
end
|
367
368
369
370
|
# File 'app/models/operation.rb', line 367
def input_data(input_name, data_name)
fv = input(input_name)
fv.child_data(data_name) if fv
end
|
168
169
170
|
# File 'app/models/operation.rb', line 168
def inputs
field_values.select { |ft| ft.role == 'input' }
end
|
#leaf? ⇒ Boolean
446
447
448
449
450
451
452
453
454
455
456
457
458
459
|
# File 'app/models/operation.rb', line 446
def leaf?
inputs.each do |i|
next unless i.predecessors.count > 0
i.predecessors.each do |pred|
return pred.operation.leaf? if pred.operation.on_the_fly
return false
end
end
true
end
|
#name ⇒ String
Returns OperationType name.
49
|
# File 'app/models/operation.rb', line 49
delegate :name, to: :operation_type
|
#nominal_cost ⇒ Object
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
|
# File 'app/models/operation.rb', line 344
def nominal_cost
return 0 unless operation_type.cost_model?
begin
operation_type.cost_model.load(binding: empty_binding)
rescue ScriptError, StandardError => e
raise "Error loading cost function for #{operation_type.name}: " + e.to_s
end
temp = status
self.status = 'done'
begin
c = cost(self)
rescue SystemStackError, ScriptError, StandardError => e
self.status = temp
raise "Error evaluating cost function for #{operation_type.name}: " + e.to_s
end
self.status = temp
c
end
|
#on_the_fly ⇒ Bool
Returns Whether OperationType is on-the-fly.
52
|
# File 'app/models/operation.rb', line 52
delegate :on_the_fly, to: :operation_type
|
197
198
199
|
# File 'app/models/operation.rb', line 197
def output(name)
get_output name
end
|
#output_array(name) ⇒ Array<FieldValue>
Outputs as Array extended with IOList
211
212
213
|
# File 'app/models/operation.rb', line 211
def output_array(name)
outputs.select { |o| o.name == name }.extend(IOList)
end
|
#output_data(output_name, data_name) ⇒ Object
372
373
374
375
|
# File 'app/models/operation.rb', line 372
def output_data(output_name, data_name)
fv = output(output_name)
fv.child_data(data_name) if fv
end
|
173
174
175
|
# File 'app/models/operation.rb', line 173
def outputs
field_values.select { |ft| ft.role == 'output' }
end
|
#parent_type ⇒ Object
interface with FieldValuer
40
41
42
|
# File 'app/models/operation.rb', line 40
def parent_type operation_type
end
|
#pass(input_name, output_name = nil) ⇒ Operation
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
|
# File 'app/models/operation.rb', line 226
def pass(input_name, output_name = nil)
output_name ||= input_name
fv_in = input(input_name)
fv_out = output(output_name)
raise "Could not find input '#{input_name}' in pass" unless fv_in
raise "Could not find input '#{output_name}' in pass" unless fv_out
fv_out.child_sample_id = fv_in.child_sample_id
fv_out.child_item_id = fv_in.child_item_id
fv_out.save
self
end
|
#plan ⇒ Plan
Returns The plan that contains this Operation.
55
56
57
|
# File 'app/models/operation.rb', line 55
def plan
plans[0] unless plans.empty?
end
|
#precondition_value ⇒ Object
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
|
# File 'app/models/operation.rb', line 387
def precondition_value
unless operation_type.precondition?
message = "No precondition found for #{operation_type.name} (id: #{id})"
Rails.logger.info message
plan.associate 'Precondition load error', message
return false
end
begin
operation_type.precondition.load(binding: empty_binding)
rescue ScriptError, StandardError => e
Rails.logger.info "Error loading precondition for #{operation_type.name} (id: #{id})"
plan.associate 'Precondition load error', e.message.to_s + ': ' + e.backtrace[0].to_s.sub('(eval)', 'line')
return false
end
begin
precondition(self)
rescue SystemStackError, ScriptError, StandardError => e
Rails.logger.info "PRECONDITION FOR OPERATION #{id} CRASHED"
plan.associate 'Precondition Evaluation Error', e.message.to_s + ': ' + e.backtrace[0].to_s.sub('(eval)', 'line')
false
end
end
|
#predecessors ⇒ Object
285
286
287
288
289
290
291
292
293
294
|
# File 'app/models/operation.rb', line 285
def predecessors
predecessor_list = []
inputs.each do |input|
input.predecessors.each do |predecessor|
predecessor_list << predecessor.operation
end
end
predecessor_list
end
|
#primed_predecessors ⇒ Object
296
297
298
299
300
301
302
303
304
305
|
# File 'app/models/operation.rb', line 296
def primed_predecessors
ops = []
inputs.each do |input|
input.predecessors.each do |predecessor|
ops << predecessor.operation if predecessor.operation && predecessor.operation.primed?
end
end
ops
end
|
#recurse(&block) ⇒ Object
244
245
246
247
248
249
250
251
|
# File 'app/models/operation.rb', line 244
def recurse(&block)
block.call(self)
inputs.each do |input|
input.predecessors.each do |predecessor|
predecessor.operation.recurse(&block)
end
end
end
|
Assigns a Sample to an input
99
100
101
|
# File 'app/models/operation.rb', line 99
def set_input(name, val, aft = nil)
set_property(name, val, 'input', false, aft)
end
|
377
378
379
380
|
# File 'app/models/operation.rb', line 377
def set_input_data(input_name, data_name, value)
fv = input(input_name)
fv.set_child_data(data_name, value) if fv
end
|
#set_output(name, val, aft = nil) ⇒ Object
Assigns a Sample to an output
107
108
109
|
# File 'app/models/operation.rb', line 107
def set_output(name, val, aft = nil)
set_property(name, val, 'output', false, aft)
end
|
#set_output_data(output_name, data_name, value) ⇒ Object
382
383
384
385
|
# File 'app/models/operation.rb', line 382
def set_output_data(output_name, data_name, value)
fv = output(output_name)
fv.set_child_data(data_name, value) if fv
end
|
#set_status_recursively(str) ⇒ Object
261
262
263
264
265
266
|
# File 'app/models/operation.rb', line 261
def set_status_recursively(str)
recurse do |op|
op.status = str
op.save
end
end
|
#siblings ⇒ Object
307
308
309
310
311
312
313
314
315
|
# File 'app/models/operation.rb', line 307
def siblings
ops = outputs.collect do |output|
output.wires_as_source.collect do |wire|
wire.to.predecessors.collect(&:operation)
end
end
ops.flatten
end
|
#successors ⇒ Object
274
275
276
277
278
279
280
281
282
283
|
# File 'app/models/operation.rb', line 274
def successors
successor_list = []
outputs.each do |output|
output.successors.each do |suc|
successor_list << suc.operation
end
end
successor_list
end
|
#temporary ⇒ Object
461
462
463
464
|
# File 'app/models/operation.rb', line 461
def temporary
@temporary ||= {}
@temporary
end
|
#to_s ⇒ Object
268
269
270
271
272
|
# File 'app/models/operation.rb', line 268
def to_s
ins = (inputs.collect { |fv| "#{fv.name}: #{fv.child_sample ? fv.child_sample.name : 'NO SAMPLE'}" }).join(', ')
outs = (outputs.collect { |fv| "#{fv.name}: #{fv.child_sample ? fv.child_sample.name : 'NO SAMPLE'}" }).join(', ')
"#{operation_type.name} #{id} ( " + ins + ' ) ==> ( ' + outs + ' )'
end
|
#virtual? ⇒ Boolean
44
45
46
|
# File 'app/models/operation.rb', line 44
def virtual?
false
end
|
Assigns a Sample to an input, choosing an appropriate allowable_field_type.
66
67
68
69
70
71
72
|
# File 'app/models/operation.rb', line 66
def with_input(name, sample)
ft = operation_type.input(name)
aft = ft.choose_aft_for(sample)
set_input(name, sample, aft)
self
end
|
#with_output(name, sample) ⇒ Object
Assigns a Sample to an output, choosing an appropriate allowable_field_type.
78
79
80
81
82
83
84
|
# File 'app/models/operation.rb', line 78
def with_output(name, sample)
ft = operation_type.output(name)
aft = ft.choose_aft_for(sample)
set_output(name, sample, aft)
self
end
|
#with_property(name, value) ⇒ Object
Assigns a value to an input parameter
89
90
91
|
# File 'app/models/operation.rb', line 89
def with_property(name, value)
set_property(name, value, 'input', false, nil)
end
|