diff --git a/app/controllers/process_plates_controller.rb b/app/controllers/process_plates_controller.rb index d090ea3d..811faa30 100644 --- a/app/controllers/process_plates_controller.rb +++ b/app/controllers/process_plates_controller.rb @@ -15,56 +15,61 @@ def new end def create - respond_to do |format| - bed_verification_model = InstrumentProcessesInstrument.get_bed_verification_type(params[:instrument_barcode], params[:instrument_process]) - - if bed_verification_model.nil? - flash[:error] = 'Invalid instrument or process' - else - bed_layout_verification = bed_verification_model.new( - instrument_barcode: params[:instrument_barcode], - scanned_values: params[:robot], - api: api - ) - if bed_layout_verification.validate_and_create_audits?(params) - # the param is called 'source_plates' but we could be working with tube racks or plates etc. - barcodes = sanitize_barcodes(params[:source_plates]) - - # find out if the 'receive_plates' process was executed - receive_plates_process = InstrumentProcess.find_by(id: params[:instrument_process]).key.eql?('slf_receive_plates') - - call_external_services(barcodes) if barcodes && receive_plates_process - - # add a flash on the page for the number of unique barcodes scanned in - num_unique_barcodes = bed_layout_verification.process_plate&.num_unique_barcodes - flash[:notice] = if num_unique_barcodes - "Success - #{num_unique_barcodes} unique plate(s) scanned" - else - 'Success' - end - else - flash[:error] = bed_layout_verification.errors.values.flatten.join("\n") - end - end - format.html { redirect_to(new_process_plate_path) } - end + bed_verification_model = InstrumentProcessesInstrument.get_bed_verification_type(params[:instrument_barcode], params[:instrument_process]) + raise 'Invalid instrument or process' if bed_verification_model.nil? + + bed_layout_verification = bed_verification_model.new( + instrument_barcode: params[:instrument_barcode], + scanned_values: params[:robot], + api: api + ) + raise format_errors(bed_layout_verification) unless bed_layout_verification.validate_and_create_audits?(params) + + back_to_new_with_message('Success') && return unless receive_plates_process?(params) + + # here on is relevant to 'receiving plates' only + # the param is called 'source_plates' but we could be working with tube racks or plates etc. + barcodes = sanitize_barcodes(params[:source_plates]) + raise 'No barcodes were provided' if barcodes.empty? + + responses = call_external_services(barcodes) + @results = generate_results(barcodes, responses) + + flash[:notice] = "Scanned #{bed_layout_verification.process_plate&.num_unique_barcodes} barcodes." + render :results + rescue StandardError => e + flash[:error] = e.message + redirect_to(new_process_plate_path) + end + + def format_errors(obj) + obj.errors.values.flatten.join("\n") + end + + def back_to_new_with_message(message, flash_type = :notice) + flash[flash_type] = message + redirect_to(new_process_plate_path) + end + + # find out if the 'receive_plates' process was executed + def receive_plates_process?(params) + @receive_plates_process ||= InstrumentProcess.find_by(id: params[:instrument_process]).key.eql?('slf_receive_plates') end - # rubocop:todo Lint/UselessAssignment - # Disabling Lint/UselessAssignment here as I know KT and AS are both actively working on - # displaying some of this information to the user. So don't want to create merge issues by removing it. # Call any external services - currently lighthouse service for plates from Lighthouse Labs and # wrangler for tube racks. If no samples are found in the lighthouse service, try the wrangler def call_external_services(barcodes) + output = { lighthouse: [], wrangler: [] } # call the lighthouse service first as we are assuming that most labware scanned will be # plates from Lighthouse Labs - lighthouse_responses = Lighthouse.call_api(barcodes) + output[:lighthouse] = Lighthouse.call_api(barcodes) # keeping it simple for now, if all the responses are not CREATED, send ALL the barcodes # to the wrangler - wrangler_responses = Wrangler.call_api(barcodes) unless all_created?(lighthouse_responses) + output[:wrangler] = Wrangler.call_api(barcodes) unless all_created?(output[:lighthouse]) + + output end - # rubocop:enable Lint/UselessAssignment # Returns a list of unique barcodes by removing blanks and duplicates def sanitize_barcodes(barcodes) @@ -77,4 +82,36 @@ def all_created?(responses) responses.all? { |response| response[:code] == '201' } end + + def generate_results(barcodes, responses) + output = {} + # default 'success' for each barcode to 'No' + barcodes.each { |b| output[b] = { success: 'No' } } + + # loop through service responses to update 'output' with successes + # puts "DEBUG: responses: #{JSON.pretty_generate(responses)}" + responses[:lighthouse]&.select { |r| r[:code] == '201' }&.each do |r| + output[r[:barcode]] = parse_response(r, :Lighthouse) + end + responses[:wrangler]&.select { |r| r[:code] == '201' }&.each do |r| + output[r[:barcode]] = parse_response(r, :CGaP) + end + + output + end + + def parse_response(response, service) + { + success: 'Yes', + source: service, + purpose: response.dig(:body, 'data', 'attributes', 'purpose_name'), + study: response.dig(:body, 'data', 'attributes', 'study_names')&.join(', ') + } + end + + def all_labware_created?(results) + return false if results.any? { |_barcode, details| details[:success] == 'No' } + + true + end end diff --git a/app/views/admin/instruments/index.html.haml b/app/views/admin/instruments/index.html.haml index 2871583f..4936ae14 100644 --- a/app/views/admin/instruments/index.html.haml +++ b/app/views/admin/instruments/index.html.haml @@ -1,4 +1,4 @@ -%h1 "Manage Instruments" +%h1 Manage Instruments %table#instruments %thead diff --git a/app/views/admin/processes/index.html.haml b/app/views/admin/processes/index.html.haml index 9f6e3812..39560d41 100644 --- a/app/views/admin/processes/index.html.haml +++ b/app/views/admin/processes/index.html.haml @@ -1,4 +1,4 @@ -%h1 "Manage Processes" +%h1 Manage Processes %table#processes %thead diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 62a5c437..e27974e7 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -2,9 +2,9 @@ <%= ProcessTracking::Application.config.name %> - - <%= stylesheet_link_tag(:flash,:form,:formtastic_static,:formtastic_changes, skip_pipeline: true ) %> - <%= javascript_include_tag('jquery.min.js','rails.js','application.js', skip_pipeline: true ) %> + + <%= stylesheet_link_tag(:flash, :form_custom, :formtastic_static, :formtastic_changes, skip_pipeline: true) %> + <%= javascript_include_tag('jquery.min.js','rails.js','application.js', skip_pipeline: true) %> <%= csrf_meta_tag %> diff --git a/app/views/process_plates/new.html.haml b/app/views/process_plates/new.html.haml index e438fa74..5c110530 100644 --- a/app/views/process_plates/new.html.haml +++ b/app/views/process_plates/new.html.haml @@ -1,42 +1,43 @@ %h2 Activity Logging %div{:id => "info_box"} + = form_for :process_plate, :url => process_plates_path , :html => {:class => "submit-once"} do |form| - %fieldset - %ul - %li - %label{ :for => "user_barcode" } User barcode - = text_field_tag "user_barcode" - .live_results#user_barcode_results + %fieldset + %ul + %li + %label{ :for => "user_barcode" } User barcode + = text_field_tag "user_barcode" + .live_results#user_barcode_results - %li - %label{ :for => "instrument_barcode" } Instrument barcode - = text_field_tag "instrument_barcode" - .live_results#instrument_barcode_results + %li + %label{ :for => "instrument_barcode" } Instrument barcode + = text_field_tag "instrument_barcode" + .live_results#instrument_barcode_results - %li - %label{ :for => "instrument_process" } Instrument process - %select#instrument_process{ :name => "instrument_process" } - %option{ :value => -1 } Select a process... - - for process_name, process_id in InstrumentProcess.sorted_by_name.map { |x| [x.name, x.id]} - %option{ :value => process_id } #{process_name} + %li + %label{ :for => "instrument_process" } Instrument process + %select#instrument_process{ :name => "instrument_process" } + %option{ :value => -1 } Select a process... + - for process_name, process_id in InstrumentProcess.sorted_by_name.map { |x| [x.name, x.id]} + %option{ :value => process_id } #{process_name} - %li - #source_plates_results + %li + #source_plates_results - %li.hidden#visual_check_input - %label{ :for => "visual_check"} Visual check performed - = check_box_tag "visual_check" + %li.hidden#visual_check_input + %label{ :for => "visual_check"} Visual check performed + = check_box_tag "visual_check" - %li.hidden#witness_barcode_input - %label{ :for => "witness_barcode"} Witness barcode - = text_field_tag "witness_barcode" + %li.hidden#witness_barcode_input + %label{ :for => "witness_barcode"} Witness barcode + = text_field_tag "witness_barcode" - %li - %input{ :id => "btnSubmit", :type => "submit", :value => 'Submit' } + %li + %input{ :id => "btnSubmit", :type => "submit", :value => 'Submit' } - content_for :page_javascript do - :javascript + :javascript (function($) { diff --git a/app/views/process_plates/results.html.haml b/app/views/process_plates/results.html.haml new file mode 100644 index 00000000..089b313b --- /dev/null +++ b/app/views/process_plates/results.html.haml @@ -0,0 +1,27 @@ +%h2 Activity Logging +%h3 Labware import results + +%table + %thead + %tr + %th Barcode + %th Imported? + %th Source + %th Purpose + %th Study + %tbody + - @results.each do |barcode, details| + %tr + %td + = barcode + %td + = details[:success] + %td + = details[:source] + %td + = details[:purpose] + %td + = details[:study] + +%br += link_to 'Scan more labware', new_process_plate_path \ No newline at end of file diff --git a/config.ru b/config.ru index 93277a0e..3476455e 100644 --- a/config.ru +++ b/config.ru @@ -2,4 +2,4 @@ # This file is used by Rack-based servers to start the application. require ::File.expand_path('../config/environment', __FILE__) -run ProcessTracking::Application +run Rails.application diff --git a/config/environments/development.rb b/config/environments/development.rb index 2b4720e7..f2946219 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -47,7 +47,7 @@ config.admin_email = 'example@example.com' # https://github.com/sanger/wrangler - config.wrangler_url = 'http://127.0.0.1:5001/tube_rack' + config.wrangler_url = 'http://127.0.0.1:5000/wrangle' # https://github.com/sanger/lighthouse config.lighthouse_host = 'http://127.0.0.1:5000' diff --git a/config/environments/test.rb b/config/environments/test.rb index 0cee720f..a4243fe2 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -45,7 +45,7 @@ config.admin_email = 'example@example.com' # https://github.com/sanger/wrangler - config.wrangler_url = 'http://example.com/tube_rack' + config.wrangler_url = 'http://example.com/wrangle' # https://github.com/sanger/lighthouse config.lighthouse_host = 'http://127.0.0.1:5000' diff --git a/lib/lighthouse.rb b/lib/lighthouse.rb index 69842740..cda71078 100644 --- a/lib/lighthouse.rb +++ b/lib/lighthouse.rb @@ -20,7 +20,7 @@ def self.call_api(barcodes) http.request(req) end - responses << { code: res.code, body: res.body } + responses << { barcode: barcode, code: res.code, body: Lighthouse.parse_body(res.body) } end rescue StandardError => e Rails.logger.error(e) @@ -30,4 +30,11 @@ def self.call_api(barcodes) end responses end + + def self.parse_body(body) + JSON.parse(body) + rescue StandardError + # return the body as is if not valid JSON + body + end end diff --git a/lib/wrangler.rb b/lib/wrangler.rb index d813060e..62359aa2 100644 --- a/lib/wrangler.rb +++ b/lib/wrangler.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'json' + class Wrangler require 'net/http' @@ -18,7 +20,7 @@ def self.call_api(barcodes) http.request(req) end - responses << { barcode: barcode, code: res.code } + responses << { barcode: barcode, code: res.code, body: Wrangler.parse_body(res.body) } end rescue StandardError => e Rails.logger.error(e) @@ -29,4 +31,11 @@ def self.call_api(barcodes) end responses end + + def self.parse_body(body) + JSON.parse(body) + rescue StandardError + # return the body as is if not valid JSON + body + end end diff --git a/public/stylesheets/form.css b/public/stylesheets/form_custom.css similarity index 85% rename from public/stylesheets/form.css rename to public/stylesheets/form_custom.css index d2a54b8f..c126266b 100644 --- a/public/stylesheets/form.css +++ b/public/stylesheets/form_custom.css @@ -62,5 +62,15 @@ li#beds_and_plates ul{ padding: 0px; } +table, td, th { + border: 1px solid #ddd; + text-align: left; +} +table { + border-collapse: collapse; +} +th, td { + padding: 15px; +} diff --git a/test/controllers/process_plates_controller.rb b/test/controllers/process_plates_controller.rb index 26e81fbf..fcbfbd1d 100644 --- a/test/controllers/process_plates_controller.rb +++ b/test/controllers/process_plates_controller.rb @@ -37,4 +37,57 @@ class ProcessPlatesControllerTest < ActiveSupport::TestCase ProcessPlatesController.new.call_external_services(%w[123 456]) end end + + context '#generate_results' do + should 'give failed results if no response' do + barcodes = ['AB1'] + + responses = { lighthouse: [], wrangler: [] } + + expected_results = { 'AB1' => { success: 'No' } } + + results = ProcessPlatesController.new.generate_results(barcodes, responses) + assert_equal(expected_results, results) + end + + should 'give detailed results if success' do + barcodes = %w[AB1 AB2 AB3] + + responses = { + lighthouse: [ + generate_success_response('AB1', 'Purpose 1', ['Study 1', 'Study 2']), + generate_success_response('AB2', 'Purpose 1', ['Study 1']), + generate_fail_response('AB3') + ], + wrangler: [ + generate_fail_response('AB1'), + generate_fail_response('AB2'), + generate_success_response('AB3', 'Purpose 1', ['Study 1', 'Study 2']) + ] + } + + expected_results = { + 'AB1' => { + success: 'Yes', + source: :Lighthouse, + purpose: 'Purpose 1', + study: 'Study 1, Study 2' + }, + 'AB2' => { + success: 'Yes', + source: :Lighthouse, + purpose: 'Purpose 1', + study: 'Study 1' + }, + 'AB3' => { + success: 'Yes', + source: :CGaP, + purpose: 'Purpose 1', + study: 'Study 1, Study 2' + } + } + results = ProcessPlatesController.new.generate_results(barcodes, responses) + assert_equal(expected_results, results) + end + end end diff --git a/test/test_helper.rb b/test/test_helper.rb index e832f6f0..3324bdf7 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -15,4 +15,31 @@ class ActiveSupport::TestCase # Add more helper methods to be used by all tests here... include RSpec::Matchers + + def generate_success_response(barcode, purpose_name, study_names) + { + barcode: barcode, + code: '201', + body: { + 'data' => { + 'attributes' => { + 'purpose_name' => purpose_name, + 'study_names' => study_names + } + } + } + } + end + + def generate_fail_response(barcode) + { + barcode: barcode, + code: '400', + body: { + 'errors': [ + 'No samples for this barcode' + ] + } + } + end end diff --git a/test/unit/lighthouse_test.rb b/test/unit/lighthouse_test.rb index d4fc638e..12247fab 100644 --- a/test/unit/lighthouse_test.rb +++ b/test/unit/lighthouse_test.rb @@ -30,8 +30,8 @@ class LighthouseTest < ActiveSupport::TestCase stub_request(:any, uri_template).to_return(status: 201) assert_equal( - Lighthouse.call_api([@input_params[:source_plates]]), - [{ code: '201', body: '' }] + [{ barcode: 'barcode_one', code: '201', body: '' }], + Lighthouse.call_api([@input_params[:source_plates]]) ) end @@ -45,12 +45,12 @@ class LighthouseTest < ActiveSupport::TestCase stub_request(:any, uri_template).to_return(status: 201) assert_equal( - Lighthouse.call_api(barcodes), [ - { code: '201', body: '' }, - { code: '201', body: '' }, - { code: '201', body: '' } - ] + { barcode: 'barcode_one', code: '201', body: '' }, + { barcode: 'barcode_two', code: '201', body: '' }, + { barcode: 'barcode_three', code: '201', body: '' } + ], + Lighthouse.call_api(barcodes) ) end end diff --git a/test/unit/wrangler_test.rb b/test/unit/wrangler_test.rb index 24a377ba..0a5f487f 100644 --- a/test/unit/wrangler_test.rb +++ b/test/unit/wrangler_test.rb @@ -31,8 +31,8 @@ class WranglerTest < ActiveSupport::TestCase stub_request(:any, uri_template).to_return(status: 200) assert_equal( - Wrangler.call_api([@input_params[:source_plates]]), - [{ barcode: @barcode, code: '200' }] + [{ barcode: @barcode, code: '200', body: '' }], + Wrangler.call_api([@input_params[:source_plates]]) ) end @@ -46,12 +46,12 @@ class WranglerTest < ActiveSupport::TestCase stub_request(:any, uri_template).to_return(status: 200) assert_equal( - Wrangler.call_api(barcodes), [ - { barcode: 'barcode_one', code: '200' }, - { barcode: 'barcode_two', code: '200' }, - { barcode: 'barcode_three', code: '200' } - ] + { barcode: 'barcode_one', code: '200', body: '' }, + { barcode: 'barcode_two', code: '200', body: '' }, + { barcode: 'barcode_three', code: '200', body: '' } + ], + Wrangler.call_api(barcodes) ) end end