Class: BatchesController

Inherits:
ApplicationController show all
Defined in:
app/controllers/batches_controller.rb

Overview

Batches represent collections of requests processed through a Pipeline at the same time. They are created via selecting requests on the pipelines show page

Constant Summary collapse

VERIFICATION_FLAVOUR_TO_MODEL_ACTION =

WARNING! This filter bypasses security mechanisms in rails 4 and mimics rails 2 behaviour. It should be removed wherever possible and the correct Strong Parameter options applied in its place.

{
  tube: :verify_tube_layout,
  amp_plate: :verify_amp_plate_layout
}.freeze

Constants included from FlashTruncation

FlashTruncation::STRING_OVERHEAD

Instance Method Summary collapse

Methods inherited from ApplicationController

#block_api_access, #evil_parameter_hack!, #extract_header_info, #set_cache_disabled!

Methods included from FlashTruncation

#max_flash_size, #truncate_flash, #truncate_flash_array

Instance Method Details

#allow_sample_sheet_download?Boolean

Checks if the current user is allowed to download the sample sheet for the batch. Ultima sample sheets are allowed to be downloaded without authentication. For all other pipelines, the user must be logged in.

Returns:

  • (Boolean)

    true if download is allowed, false otherwise



391
392
393
# File 'app/controllers/batches_controller.rb', line 391

def allow_sample_sheet_download?
  @batch.pipeline.is_a?(UltimaSequencingPipeline) || logged_in?
end

#batch_parametersObject

rubocop:enable Metrics/MethodLength



140
141
142
# File 'app/controllers/batches_controller.rb', line 140

def batch_parameters
  @batch_parameters ||= params.require(:batch).permit(:assignee_id)
end

#completedObject



180
181
182
183
184
185
# File 'app/controllers/batches_controller.rb', line 180

def completed
  # The params fall-back here reflects an older route where pipeline got passed in as :id. It should be removed
  # in the near future.
  @pipeline = Pipeline.find(params[:pipeline_id] || params[:id])
  @batches = @pipeline.batches.completed.order(id: :desc).includes(%i[user pipeline]).page(params[:page])
end

#createObject

rubocop:todo Metrics/MethodLength



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'app/controllers/batches_controller.rb', line 90

def create # rubocop:todo Metrics/AbcSize
  @pipeline = Pipeline.find(params[:id])

  requests = @pipeline.extract_requests_from_input_params(request_parameters)

  # TODO: These should be different endpoints
  case params[:action_on_requests]
  when 'cancel_requests'
    transition_requests(requests, :cancel_before_started!, 'Requests cancelled')
  when 'hide_from_inbox'
    transition_requests(requests, :hold!, 'Requests hidden from inbox')
  else
    # This is the standard create action
    standard_create(requests)
  end
rescue ActiveRecord::RecordInvalid => e
  respond_to do |format|
    format.html do
      flash[:error] = e.record.errors.full_messages
      redirect_to(pipeline_path(@pipeline))
    end
    format.xml { render xml: @batch.errors.to_xml }
  end
end

#download_spreadsheetObject

Used in Cherrypicking pipeline to generate the template for CSV driven picks



361
362
363
364
# File 'app/controllers/batches_controller.rb', line 361

def download_spreadsheet
  csv_string = Tasks::PlateTemplateHandler.generate_spreadsheet(@batch)
  send_data csv_string, type: 'text/plain', filename: "#{@batch.id}_cherrypick_layout.csv", disposition: 'attachment'
end

#editObject

rubocop:enable Metrics/MethodLength



82
83
84
85
86
87
# File 'app/controllers/batches_controller.rb', line 82

def edit
  @rits = @batch.pipeline.request_information_types
  @requests = @batch.ordered_requests.includes(:batch_request, :asset, :target_asset, :comments)
  @users = User.all
  @controls = @batch.pipeline.controls
end

#failObject



194
195
196
# File 'app/controllers/batches_controller.rb', line 194

def fail
  @fail_reasons = @batch.workflow.source_is_internal? ? FAILURE_REASONS['internal'] : FAILURE_REASONS['external']
end

#fail_itemsObject

rubocop:todo Metrics/AbcSize, Metrics/MethodLength



198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'app/controllers/batches_controller.rb', line 198

def fail_items # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
  ActiveRecord::Base.transaction do
    fail_params =
      params.permit(:id, requested_fail: {}, requested_remove: {}, failure: %i[reason comment fail_but_charge])
    fail_and_remover = Batch::RequestFailAndRemover.new(fail_params)
    if fail_and_remover.save
      flash[:notice] = truncate_flash(fail_and_remover.notice)
    else
      flash[:error] = truncate_flash(fail_and_remover.errors.full_messages.join(';'))
    end
    redirect_to action: :fail, id: params[:id]
  end
end

#failedObject



187
188
189
190
191
192
# File 'app/controllers/batches_controller.rb', line 187

def failed
  # The params fall-back here reflects an older route where pipeline got passed in as :id. It should be removed
  # in the near future.
  @pipeline = Pipeline.find(params[:pipeline_id] || params[:id])
  @batches = @pipeline.batches.failed.order(id: :desc).includes(%i[user pipeline]).page(params[:page])
end

#filteredObject



337
338
# File 'app/controllers/batches_controller.rb', line 337

def filtered
end

#find_batch_by_barcodeObject



374
375
376
377
378
379
380
381
382
383
384
# File 'app/controllers/batches_controller.rb', line 374

def find_batch_by_barcode
  batch_id = LabEvent.find_batch_id_by_barcode(params[:id])
  if batch_id.nil?
    @batch_error = 'Batch id not found.'
    render action: 'batch_error', format: :xml
    nil
  else
    @batch = Batch.find(batch_id)
    render action: 'show', format: :xml
  end
end

#find_batch_by_batch_idObject



370
371
372
# File 'app/controllers/batches_controller.rb', line 370

def find_batch_by_batch_id
  @batch = Batch.find(params[:batch_id])
end

#find_batch_by_idObject



366
367
368
# File 'app/controllers/batches_controller.rb', line 366

def find_batch_by_id
  @batch = Batch.find(params[:id])
end

#generate_sample_sheetvoid

This method returns an undefined value.

Generates and sends the appropriate sample sheet(s) for the batch.



397
398
399
400
401
402
403
404
405
406
407
408
# File 'app/controllers/batches_controller.rb', line 397

def generate_sample_sheet
  return redirect_to() unless allow_sample_sheet_download?

  if @batch.pipeline.is_a?(ElementAvitiSequencingPipeline)
    generate_element_aviti_sample_sheet
  elsif @batch.pipeline.is_a?(UltimaSequencingPipeline)
    generate_ultima_sample_sheet
  else
    flash[:error] = 'Sample sheet generation is not supported for this pipeline.'
    redirect_to controller: 'batches', action: 'show', id: @batch.id
  end
end

#indexObject

rubocop:todo Metrics/AbcSize, Metrics/MethodLength



40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'app/controllers/batches_controller.rb', line 40

def index # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
  if logged_in?
    @user = params.fetch(:user, current_user)
    @batches = Batch.for_user(@user).order(id: :desc).includes(:user, :assignee, :pipeline).page(params[:page])
  else
    # Can end up here with XML. And it causes pain.
    @batches = Batch.order(id: :asc).page(params[:page]).limit(10)
  end
  respond_to do |format|
    format.html
    format.xml { render xml: @batches.to_xml }
    format.json { render json: @batches.to_json.gsub('null', '""') }
  end
end

#pendingObject



154
155
156
157
158
159
# File 'app/controllers/batches_controller.rb', line 154

def pending
  # The params fall-back here reflects an older route where pipeline got passed in as :id. It should be removed
  # in the near future.
  @pipeline = Pipeline.find(params[:pipeline_id] || params[:id])
  @batches = @pipeline.batches.pending.order(id: :desc).includes(%i[user pipeline]).page(params[:page])
end

#pipelineObject



144
145
146
147
148
149
150
151
152
# File 'app/controllers/batches_controller.rb', line 144

def pipeline
  # All pipeline batches routes should just direct to batches#index with pipeline and state as filter parameters
  @batches =
    Batch
      .where(pipeline_id: params[:pipeline_id] || params[:id])
      .order(id: :desc)
      .includes(:user, :pipeline)
      .page(params[:page])
end

#previous_qc_stateObject



330
331
332
333
334
335
# File 'app/controllers/batches_controller.rb', line 330

def previous_qc_state
  @batch.qc_previous_state!(current_user)
  @batch.save
  flash[:notice] = "Batch #{@batch.id} reset to state #{@batch.qc_state}"
  redirect_to batch_url(@batch)
end

Handles printing of the worksheet



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'app/controllers/batches_controller.rb', line 273

def print # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
  @task = Task.find_by(id: params[:task_id])
  @pipeline = @batch.pipeline
  @comments = @batch.comments
  template = @pipeline.batch_worksheet

  if template == 'cherrypick_worksheet'
    robot_id = params.fetch(:robot_id, @batch.robot_id)
    @robot = robot_id ? Robot.find(robot_id) : Robot.default_for_verification
    @plates = params[:barcode] ? Plate.with_barcode(params[:barcode]) : @batch.output_plates
  end

  if template
    render action: template, layout: false
  else
    redirect_back_or_to(batch_path(@batch), alert: "No worksheet for #{@pipeline.name}")
  end
end


263
264
265
266
267
268
269
270
# File 'app/controllers/batches_controller.rb', line 263

def print_amp_plate_barcodes
  if @batch.requests.empty?
    flash[:notice] = 'Your batch contains no requests.'
    redirect_to controller: 'batches', action: 'show', id: @batch.id
  else
    print_handler(LabelPrinter::Label::BatchPlateAmp)
  end
end


227
228
# File 'app/controllers/batches_controller.rb', line 227

def print_amp_plate_labels
end


254
255
256
257
258
259
260
261
# File 'app/controllers/batches_controller.rb', line 254

def print_barcodes
  if @batch.requests.empty?
    flash[:notice] = 'Your batch contains no requests.'
    redirect_to controller: 'batches', action: 'show', id: @batch.id
  else
    print_handler(LabelPrinter::Label::BatchTube)
  end
end


224
225
# File 'app/controllers/batches_controller.rb', line 224

def print_labels
end


250
251
252
# File 'app/controllers/batches_controller.rb', line 250

def print_plate_barcodes
  print_handler(LabelPrinter::Label::BatchRedirect)
end

rubocop:todo Metrics/MethodLength



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'app/controllers/batches_controller.rb', line 230

def print_plate_labels # rubocop:todo Metrics/MethodLength
  @pipeline = @batch.pipeline
  @output_barcodes = []

  @output_labware = @batch.plate_group_barcodes || []

  @output_labware.each_key do |parent|
    next if parent.nil?

    plate_barcode = parent.human_barcode
    @output_barcodes << plate_barcode if plate_barcode.present?
  end

  return if @output_barcodes.present?

  # We have no output barcodes, which means a problem
  flash[:error] = 'Output plates do not have barcodes to print'
  redirect_to controller: 'batches', action: 'show', id: @batch.id
end

#releasedObject



168
169
170
171
172
173
174
175
176
177
178
# File 'app/controllers/batches_controller.rb', line 168

def released
  # The params fall-back here reflects an older route where pipeline got passed in as :id. It should be removed
  # in the near future.
  @pipeline = Pipeline.find(params[:pipeline_id] || params[:id])

  @batches = @pipeline.batches.released.order(id: :desc).includes(%i[user pipeline]).page(params[:page])
  respond_to do |format|
    format.html
    format.xml { render layout: false }
  end
end

#reset_batchObject



323
324
325
326
327
328
# File 'app/controllers/batches_controller.rb', line 323

def reset_batch
  pipeline = @batch.pipeline
  @batch.reset!(current_user)
  flash[:notice] = "Batch #{@batch.id} has been reset"
  redirect_to controller: 'pipelines', action: :show, id: pipeline.id
end

#saveObject



220
221
222
# File 'app/controllers/batches_controller.rb', line 220

def save
  redirect_to action: :show, id: @batch.id
end

#showObject

rubocop:todo Metrics/MethodLength



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'app/controllers/batches_controller.rb', line 56

def show # rubocop:todo Metrics/AbcSize
  respond_to do |format|
    format.html do
      @submenu_presenter = Presenters::BatchSubmenuPresenter.new(current_user, @batch)

      @pipeline = @batch.pipeline
      @tasks = @batch.tasks.sort_by(&:sorted)
      @rits = @pipeline.request_information_types
      @input_labware = @batch.input_labware_report
      @output_labware = @batch.output_labware_report

      if @pipeline.pick_data
        @robot = @batch.robot_id ? Robot.find(@batch.robot_id) : Robot.with_verification_behaviour.first

        # In the event we have no robots with the correct behaviour, and none are specialised on the batch, fall-back
        # to the first robot.
        @robot ||= Robot.first
        @robots = Robot.with_verification_behaviour
      end
    end
    format.xml { render layout: false }
  end
end

#sortObject



212
213
214
215
216
217
218
# File 'app/controllers/batches_controller.rb', line 212

def sort
  @batch.assign_positions_to_requests!(params['requests_list'].map(&:to_i))

  # Touch the batch to update its timestamp and trigger re-broadcast
  @batch.touch # rubocop:disable Rails/SkipsModelValidations
  head :ok
end

#startedObject



161
162
163
164
165
166
# File 'app/controllers/batches_controller.rb', line 161

def started
  # The params fall-back here reflects an older route where pipeline got passed in as :id. It should be removed
  # in the near future.
  @pipeline = Pipeline.find(params[:pipeline_id] || params[:id])
  @batches = @pipeline.batches.started.order(id: :desc).includes(%i[user pipeline]).page(params[:page])
end

#swapObject

rubocop:todo Metrics/AbcSize



340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'app/controllers/batches_controller.rb', line 340

def swap # rubocop:todo Metrics/AbcSize
  if @batch.swap(
    current_user,
    'batch_1' => {
      'id' => params['batch']['1'],
      'lane' => params['batch']['position']['1']
    },
    'batch_2' => {
      'id' => params['batch']['2'],
      'lane' => params['batch']['position']['2']
    }
  )
    flash[:notice] = 'Successfully swapped lane positions'
    redirect_to batch_path(@batch)
  else
    flash[:error] = @batch.errors.full_messages.join('<br />')
    redirect_to action: :filtered, id: @batch.id
  end
end

#updateObject

rubocop:todo Metrics/MethodLength



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'app/controllers/batches_controller.rb', line 118

def update # rubocop:todo Metrics/AbcSize
  if batch_parameters[:assignee_id]
    user = User.find(batch_parameters[:assignee_id])
    assigned_message = "Assigned to #{user.name} (#{user.})."
  else
    assigned_message = ''
  end

  respond_to do |format|
    if @batch.update(batch_parameters)
      flash[:notice] = "Batch was successfully updated. #{assigned_message}"
      format.html { redirect_to batch_url(@batch) }
      format.xml { head :ok }
    else
      format.html { render action: 'edit' }
      format.xml { render xml: @batch.errors.to_xml }
    end
  end
end

#verifyObject



292
293
294
295
296
297
# File 'app/controllers/batches_controller.rb', line 292

def verify
  @requests = @batch.ordered_requests
  @pipeline = @batch.pipeline
  @count = @requests.length
  @verification_flavour = params[:verification_flavour]
end

#verify_layoutObject



299
300
301
302
303
304
305
306
307
308
309
310
# File 'app/controllers/batches_controller.rb', line 299

def verify_layout
  # scanned tube barcode params from page are called barcode_0, barcode_1, ... barcode_n
  scanned_barcodes = Array.new(@batch.requests.count) { |i| params["barcode_#{i}"] }
  verification_flavour = params[:verification_flavour].to_sym
  model_method = VERIFICATION_FLAVOUR_TO_MODEL_ACTION[verification_flavour]

  if @batch.send(model_method, scanned_barcodes, current_user)
    verify_layout_success(verification_flavour)
  else
    verify_layout_failure(verification_flavour)
  end
end

#verify_layout_failure(verification_flavour) ⇒ Object



318
319
320
321
# File 'app/controllers/batches_controller.rb', line 318

def verify_layout_failure(verification_flavour)
  flash[:error] = @batch.errors.full_messages.sort
  redirect_to action: :verify, id: @batch.id, verification_flavour: verification_flavour
end

#verify_layout_success(verification_flavour) ⇒ Object



312
313
314
315
316
# File 'app/controllers/batches_controller.rb', line 312

def verify_layout_success(verification_flavour)
  flash[:notice] =
    "All of the #{verification_flavour.to_s.humanize.downcase.pluralize} are in their correct positions."
  redirect_to batch_path(@batch)
end