Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[OSU-123] Sanger submission batch specify reserved cells #4982

Merged
merged 8 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions spec/support/select_from_chosen.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,11 @@ def select_from_chosen(item_text, options)
find("##{field[:id]}_chosen ul.chosen-results li", text: item_text).click
end

def unselect_from_chosen(item_text, options)
page.scroll_to(options[:scroll_to]) if options[:scroll_to]

field = find_field(options[:from], visible: false)
find("##{field[:id]}_chosen li.search-choice", text: item_text).find(".search-choice-close").click
end

end
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,23 @@
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
window.vue_sanger_sequencing_well_plate_editor_app = {
props: ["submissions", "builder_config"],
props: ["submissions", "defaultReservedCells"],

data() {
return {
builder: new SangerSequencing.WellPlateBuilder,
columnOrder: null
columnOrder: null,
reservedCells: [],
};
},

beforeCompile() {
this.colorBuilder = new SangerSequencing.WellPlateColors(this.builder);
return this.builder.setReservedCells(this.builder_config.reserved_cells);
},

compiled() {
this.updateReservedCells(this.defaultReservedCells);
this.initReservedCellsInput();
},

ready() {
Expand All @@ -27,6 +32,10 @@ window.vue_sanger_sequencing_well_plate_editor_app = {
this.builder.changeOrderStrategy(this.columnOrder);
},

updateReservedCells(values) {
this.builder.setReservedCells(values || []);
},

addSubmission(submissionId) {
return this.builder.addSubmission(this.findSubmission(submissionId));
},
Expand Down Expand Up @@ -55,6 +64,19 @@ window.vue_sanger_sequencing_well_plate_editor_app = {

isNotInPlate(submissionId) {
return !this.isInPlate(submissionId);
},
initReservedCellsInput() {
let self = this;

setTimeout(function() {
let comp = $(self.$els.reservedCells);

if (comp.length) {
self.updateReservedCells(comp.val());

comp.chosen().on("change", () => self.updateReservedCells(comp.val()));
}
}, 0);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ exports.SangerSequencing.WellPlateBuilder = class WellPlateBuilder {
// This array maintains all of the submissions that have ever been added
// in order to keep consistent colors when removing and adding samples.
this.allSubmissions = [];
this._reservedCells = ["A01", "A02"];
this._reservedCells = [];
this._orderingStrategy = new SangerSequencing.OddFirstOrderingStrategy;
this._render();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def new
.includes(:samples, order_detail: :product)
.for_facility(current_facility)
.for_product_group(product_group)
@builder_config = WellPlateConfiguration.find(product_group)
end

def create
Expand Down Expand Up @@ -86,6 +85,12 @@ def order_options

helper_method :order_options

def reserved_cells_options
["A01", "A02"]
end

helper_method :reserved_cells_options

private

def product_group
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class BatchForm
attribute :batch
attribute :column_order, default: "odd_first"

attr_writer :reserved_cells

delegate :submission_ids, :group, to: :batch

validates :submission_ids, presence: true
Expand Down Expand Up @@ -49,6 +51,12 @@ def save
batch.save if valid?
end

def reserved_cells
return @reserved_cells if @reserved_cells.present?

WellPlateConfiguration.find(group).reserved_cells
end

private

def submissions_in_correct_facility
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ module SangerSequencing

class WellPlateConfiguration

attr_reader :reserved_cells

def initialize(reserved_cells: [])
@reserved_cells = reserved_cells
end
Expand All @@ -16,13 +18,6 @@ def initialize(reserved_cells: [])
def self.find(key)
CONFIGS[key] || CONFIGS[:default]
end

def to_json
{
reserved_cells: Array(@reserved_cells),
}.to_json
end

end

end
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
= javascript_include_tag "sanger_sequencing/well_plate"
= stylesheet_link_tag "sanger_sequencing/application"

%vue-sanger-sequencing-well-plate-editor-app(inline-template){ ":submissions" => @submissions.to_json(include: :samples), ":builder_config" => @builder_config.to_json }
%vue-sanger-sequencing-well-plate-editor-app(inline-template){ ":submissions" => @submissions.to_json(include: :samples), ":default-reserved-cells" => @batch.reserved_cells }

= simple_form_for :batch, url: facility_sanger_sequencing_admin_batches_path do |f|
= f.input :group, as: :hidden
Expand All @@ -18,7 +18,12 @@
collection: order_options,
include_blank: false,
label: SangerSequencing::BatchForm.human_attribute_name(:column_order),
input_html: { "v-on:change": "changeOrder()", "v-model" => "columnOrder" }
input_html: { "v-on:change": "changeOrder()", "v-model": "columnOrder" }

= f.input :reserved_cells,
collection: reserved_cells_options,
label: SangerSequencing::BatchForm.human_attribute_name(:reserved_cells),
input_html: { multiple: true, "v-el:reserved-cells": true }

= f.submit text("submit"), class: "btn btn-primary"

Expand Down
1 change: 1 addition & 0 deletions vendor/engines/sanger_sequencing/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ en:
attributes:
sanger_sequencing/batch_form:
column_order: Column Order
reserved_cells: Reserved Cells
errors:
models:
sanger_sequencing/batch_form:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS202: Simplify dynamic range loops
* DS205: Consider reworking code to avoid use of IIFEs
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
//= require sanger_sequencing/well_plate

describe("SangerSequencing.WellPlateBuilder", function() {
const sampleList = function(count) {
if (this.lastSampleId === undefined) { this.lastSampleId = 0; }
return (() => {
const result = [];
for (let x = 0, end = count, asc = 0 <= end; asc ? x < end : x > end; asc ? x++ : x--) {
this.lastSampleId++;
result.push(new SangerSequencing.Sample({id: this.lastSampleId, customer_sample_id: `Testing ${x}`}));
}
return result;
})();
};

describe("addSubmission()", function() {
beforeEach(function() {
this.submission = { id: 542, samples: sampleList(2) };
this.wellPlate = new SangerSequencing.WellPlateBuilder;
this.wellPlate.setReservedCells(["A01", "A02"]);
});

it("can add a submission", function() {
this.wellPlate.addSubmission(this.submission);
return expect(this.wellPlate.submissions).toEqual([this.submission]);
});

return it("cannot add a submission twice", function() {
this.wellPlate.addSubmission(this.submission);
this.wellPlate.addSubmission(this.submission);
return expect(this.wellPlate.submissions).toEqual([this.submission]);
});
});

describe("removeSubmission()", function() {
beforeEach(function() {
this.wellPlate = new SangerSequencing.WellPlateBuilder;
this.wellPlate.setReservedCells(["A01", "A02"]);
this.submission1 = { id: 542, samples: sampleList(2) };
this.submission2 = { id: 543, samples: sampleList(3) };
this.wellPlate.addSubmission(this.submission1);
return this.wellPlate.addSubmission(this.submission2);
});

return it("can remove a submission", function() {
this.wellPlate.removeSubmission(this.submission1);
return expect(this.wellPlate.submissions).toEqual([this.submission2]);
});
});

describe("samples()", function() {
beforeEach(function() {
this.wellPlate = new SangerSequencing.WellPlateBuilder;
this.wellPlate.setReservedCells(["A01", "A02"]);
this.submission1 = { id: 542, samples: sampleList(2) };
this.submission2 = { id: 543, samples: sampleList(3) };
this.wellPlate.addSubmission(this.submission1);
return this.wellPlate.addSubmission(this.submission2);
});

return it("returns the samples in order", function() {
return expect(this.wellPlate.samples()).toEqual(this.submission1.samples.concat(this.submission2.samples));
});
});

describe("sampleAtCell()", function() {
beforeEach(function() {
this.wellPlate = new SangerSequencing.WellPlateBuilder;
this.wellPlate.setReservedCells(["A01", "A02"]);
this.submission1 = { id: 542, samples: sampleList(2) };
this.submission2 = { id: 543, samples: sampleList(3) };
this.wellPlate.addSubmission(this.submission1);
return this.wellPlate.addSubmission(this.submission2);
});

it("finds the first sample at B01 when A01 is reserved", function() {
return expect(this.wellPlate.sampleAtCell("B01")).toEqual(this.submission1.samples[0]);
});

it("finds the first sample at B01 when A01 is not reserved", function() {
this.wellPlate.setReservedCells([]);
return expect(this.wellPlate.sampleAtCell("A01")).toEqual(this.submission1.samples[0]);
});

return describe("when it rolls over into a second plate", function() {
beforeEach(function() {
this.submission3 = { id: 544, samples: sampleList(92) };
return this.wellPlate.addSubmission(this.submission3);
});

it("finds the first sample at B01", function() {
return expect(this.wellPlate.sampleAtCell("B01", 0)).toEqual(this.submission1.samples[0]);
});

return it("finds the sample in the second plate at B01", function() {
// 89 because 96 - 5(already added) - 2 (reserved) = 89
return expect(this.wellPlate.sampleAtCell("B01", 1)).toEqual(this.submission3.samples[89]);
});
});
});

describe("plateCount()", function() {
beforeEach(function() {
this.wellPlate = new SangerSequencing.WellPlateBuilder;
this.wellPlate.setReservedCells(["A01", "A02"]);
});

it("has one plate when empty", function() {
return expect(this.wellPlate.plateCount()).toEqual(1);
});

it("has one plate when less than 96 cells", function() {
this.submission = { id: 542, samples: sampleList(40) };
this.wellPlate.addSubmission(this.submission);
return expect(this.wellPlate.plateCount()).toEqual(1);
});

it("has one plates when it is completely full", function() {
this.submission = { id: 542, samples: sampleList(94) };
this.wellPlate.addSubmission(this.submission);
return expect(this.wellPlate.plateCount()).toEqual(1);
});

it("has two plates when it is just over full", function() {
this.submission = { id: 542, samples: sampleList(95) };
this.wellPlate.addSubmission(this.submission);
return expect(this.wellPlate.plateCount()).toEqual(2);
});

return it("has three plates when it gets really big", function() {
this.submission = { id: 542, samples: sampleList(280) };
this.wellPlate.addSubmission(this.submission);
return expect(this.wellPlate.plateCount()).toEqual(3);
});
});

return describe("plates", function() {
beforeEach(function() {
this.wellPlate = new SangerSequencing.WellPlateBuilder;
this.wellPlate.setReservedCells(["A01", "A02"]);
this.submission = { id: 542, samples: sampleList(8) };
return this.wellPlate.addSubmission(this.submission);
});

it("has 96 cells", function() {
const results = this.wellPlate.plates[0];
return expect(Object.keys(results).length).toEqual(96);
});

return it("renders odd rows first", function() {
const results = this.wellPlate.plates[0];
return (() => {
const result = [];
for (var expected of [
["A01", "reserved" ],
["B01", "Testing 0" ],
["C01", "Testing 1" ],
["D01", "Testing 2" ],
["E01", "Testing 3" ],
["F01", "Testing 4" ],
["G01", "Testing 5" ],
["H01", "Testing 6" ],
["A02", "reserved" ],
["B02", "" ],
["C02", "" ],
["D02", "" ],
["E02", "" ],
["F02", "" ],
["G02", "" ],
["H02", "" ],
["A03", "Testing 7" ],
["B03", "" ],
["C03", "" ],
["D03", "" ],
["E03", "" ],
["F03", "" ],
["G03", "" ],
["H03", "" ],
]) {
var well = expected[0];
var value = expected[1];
result.push(expect(results[well].customerSampleId()).toEqual(value));
}
return result;
})();
});
});
});
Loading