Skip to content
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
14 changes: 13 additions & 1 deletion src/main/java/uk/ac/sanger/sccp/stan/model/PlanAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class PlanAction {
private String sampleThickness;
@ManyToOne
private BioState newBioState;
private Integer sectioningOrder;

public PlanAction() {}

Expand Down Expand Up @@ -113,6 +114,14 @@ public void setNewBioState(BioState newBioState) {
this.newBioState = newBioState;
}

public Integer getSectioningOrder() {
return this.sectioningOrder;
}

public void setSectioningOrder(Integer sectioningOrder) {
this.sectioningOrder = sectioningOrder;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand All @@ -125,7 +134,9 @@ public boolean equals(Object o) {
&& Objects.equals(this.sample, that.sample)
&& Objects.equals(this.newSection, that.newSection)
&& Objects.equals(this.sampleThickness, that.sampleThickness)
&& Objects.equals(this.newBioState, that.newBioState));
&& Objects.equals(this.newBioState, that.newBioState)
&& Objects.equals(this.sectioningOrder, that.sectioningOrder)
);
}

@Override
Expand All @@ -144,6 +155,7 @@ public String toString() {
.addRepr("newSection", newSection)
.add("sampleThickness", sampleThickness)
.add("newBioState", newBioState)
.add("sectioningOrder", sectioningOrder)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class PlanRequestAction {
private int sampleId;
private PlanRequestSource source;
private String sampleThickness;
private Integer sectioningOrder;

public PlanRequestAction() {}

Expand Down Expand Up @@ -59,6 +60,14 @@ public void setSampleThickness(String sampleThickness) {
this.sampleThickness = sampleThickness;
}

public Integer getSectioningOrder() {
return this.sectioningOrder;
}

public void setSectioningOrder(Integer sectioningOrder) {
this.sectioningOrder = sectioningOrder;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand All @@ -67,7 +76,9 @@ public boolean equals(Object o) {
return (this.sampleId==that.sampleId
&& Objects.equals(this.addresses, that.addresses)
&& Objects.equals(this.source, that.source)
&& Objects.equals(this.sampleThickness, that.sampleThickness));
&& Objects.equals(this.sampleThickness, that.sampleThickness)
&& Objects.equals(this.sectioningOrder, that.sectioningOrder)
);
}

@Override
Expand All @@ -82,6 +93,7 @@ public String toString() {
.add("sampleId", sampleId)
.add("source", source)
.add("sampleThickness", sampleThickness)
.add("sectioningOrder", sectioningOrder)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,12 @@ public List<PlanAction> createActions(PlanRequestLabware requestLabware, int pla
.findAny()
.orElseThrow(() -> new EntityNotFoundException("Sample " + prac.getSampleId()
+ " not found in " + prac.getSource()));
Integer sectioningOrder = prac.getSectioningOrder();
for (Address ad : prac.getAddresses()) {
Slot slot1 = destination.getSlot(ad);
PlanAction action = new PlanAction(null, planId, slot0, slot1, originalSample,
null, prac.getSampleThickness(), newBioState);
action.setSectioningOrder(sectioningOrder);
actions.add(action);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public UCMap<Labware> validateSources(OperationType opType) {
Set<String> releasedBarcodes = new LinkedHashSet<>();
Set<String> discardedBarcodes = new LinkedHashSet<>();
Set<String> usedBarcodes = new LinkedHashSet<>();
Map<Integer, Set<Integer>> sampleIdSectioningOrders = new HashMap<>();
for (PlanRequestAction action : (Iterable<PlanRequestAction>) (actions()::iterator)) {
PlanRequestSource source = action.getSource();
if (source==null || source.getBarcode()==null || source.getBarcode().isEmpty()) {
Expand Down Expand Up @@ -111,11 +112,19 @@ public UCMap<Labware> validateSources(OperationType opType) {
addProblem("Slot %s of labware %s does not contain a sample with ID %s.",
address, barcode, action.getSampleId());
}
if (action.getSectioningOrder()==null) {
addProblem("Missing sectioning order.");
}
if (opType!=null && opType.sourceMustBeBlock() && !slot.isBlock()) {
addProblem("Source %s,%s is not a block for operation %s.", action.getSource().getBarcode(), address,
opType.getName());
} else if (opType!=null && !opType.canCreateSection() && sample!=null && sample.getSection()==null) {
addProblem("Operation %s cannot create a section of sample %s.", opType.getName(), sample.getId());
} else if (sample != null && action.getSectioningOrder() != null) {
Set<Integer> secOrders = sampleIdSectioningOrders.computeIfAbsent(sample.getId(), k -> new HashSet<>());
if (!secOrders.add(action.getSectioningOrder())) {
addProblem("Repeated sectioning order: %s from sample %s.", action.getSectioningOrder(), sample.getId());
}
}
}
if (!unfoundBarcodes.isEmpty()) {
Expand Down
14 changes: 14 additions & 0 deletions src/main/resources/db/changelog/changelog-4.03.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">

<changeSet id="4.3.0" author="dr6">
<addColumn tableName="plan_action">
<column name="sectioning_order" type="INT" afterColumn="sample_thickness">
<constraints nullable="true"/>
</column>
</addColumn>
</changeSet>

</databaseChangeLog>
1 change: 1 addition & 0 deletions src/main/resources/db/changelog/changelog-master.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@
<include relativeToChangelogFile="true" file="changelog-4.00.xml"/>
<include relativeToChangelogFile="true" file="changelog-4.01.xml"/>
<include relativeToChangelogFile="true" file="changelog-4.02.xml"/>
<include relativeToChangelogFile="true" file="changelog-4.03.xml"/>
</databaseChangeLog>
4 changes: 4 additions & 0 deletions src/main/resources/schema.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,8 @@ type PlanAction {
newSection: String
"""The planned thickness of the sample, if any."""
sampleThickness: String
"""An indicator for the order sections are taken from their block."""
sectioningOrder: Int
}

"""A planned operation."""
Expand All @@ -554,6 +556,8 @@ input PlanRequestAction {
sampleThickness: String
"""The source of the sample (describing a slot in some existing labware)."""
source: PlanRequestSource!
"""An indicator for the order sections are taken from their block."""
sectioningOrder: Int!
}

"""A specification of new labware to be created for a plan request."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public void testPlanAndRecordSection() throws Exception {
String[] expectedPlanDestAddresses = { "A1", "A2", "B2" };
int[] expectedPlanSampleId = {blockSamples[0].getId(), blockSamples[0].getId(), blockSamples[1].getId()};
assertEquals(expectedPlanDestAddresses.length, resultActions.size());

int[] expectedSectioningOrder = {1,1,2};
for (int i = 0; i < expectedPlanDestAddresses.length; ++i) {
Map<String, ?> resultAction = resultActions.get(i);
assertEquals("A1", chainGet(resultAction, "source", "address"));
Expand All @@ -99,6 +99,7 @@ public void testPlanAndRecordSection() throws Exception {
if (i == expectedPlanDestAddresses.length - 1) {
assertEquals("2.5", resultAction.get("sampleThickness"));
}
assertEquals(expectedSectioningOrder[i], resultAction.get("sectioningOrder"));
}

testRetrievePlanData(barcodes);
Expand All @@ -120,7 +121,7 @@ private void testRetrievePlanData(String[] barcodes) throws Exception {
List<Map<String,?>> planActionsData = chainGet(planData, "plan", "planActions");
assertThat(planActionsData).hasSize(3);
assertEquals(List.of(List.of("A1", "A2"), List.of("B2")), planData.get("groups"));

int[] expectedSectioningOrder = {3,4};
for (int i = 1; i < barcodes.length; ++i) {
String barcode = barcodes[i]; // fetal waste barcode
result = tester.post(planQuery.replace("$BARCODE", barcode));
Expand All @@ -133,6 +134,7 @@ private void testRetrievePlanData(String[] barcodes) throws Exception {
.collect(toList())).containsExactlyInAnyOrder(sources[i-1]);
planActionsData = chainGet(planData, "plan", "planActions");
assertThat(planActionsData).hasSize(1);
assertEquals(expectedSectioningOrder[i-1], planActionsData.getFirst().get("sectioningOrder"));
}
assertEquals(List.of(List.of("A1")), planData.get("groups"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,11 +323,13 @@ public void testCreateActions(boolean useNewBioState) {
new PlanRequestAction(A2, samples.get(0).getId(),
new PlanRequestSource(sourceBarcodes.get(0), null), "1")
));
Zip.enumerate(prl.getActions().stream()).forEach((i, a) -> a.setSectioningOrder(i+1));

List<PlanAction> expectedActions = List.of(
new PlanAction(21, planId, sources.get(0).getFirstSlot(), destination.getFirstSlot(), samples.get(0), null, null, bioState),
new PlanAction(22, planId, sources.get(0).getFirstSlot(), destination.getSlot(A2), samples.get(0), null, "1", bioState)
);
Zip.enumerate(expectedActions.stream()).forEach((i, a) -> a.setSectioningOrder(i+1));

final int[] planActionIdCounter = {20};
when(mockPlanActionRepo.saveAll(any())).then(invocation -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public void testValidatePrebarcode(String barcode, boolean xenium, boolean preba

@ParameterizedTest
@MethodSource("sourcesData")
public void testValidateSources(Object sourceBarcodes, Object sourceAddresses, Integer sampleId,
public void testValidateSources(Object sourceBarcodes, Object sourceAddresses, Integer sampleId, Integer sectioningOrder,
Labware existingLabware, OperationType opType, Object expectedProblems) {
when(mockLabwareRepo.findByBarcode(any())).thenReturn(Optional.empty());
if (existingLabware!=null) {
Expand All @@ -173,6 +173,9 @@ public void testValidateSources(Object sourceBarcodes, Object sourceAddresses, I
new PlanRequestSource(bc, sourceAddressIter.hasNext() ? sourceAddressIter.next() : null), null)
)
.collect(toList());
if (sectioningOrder != null) {
prActions.forEach(a -> a.setSectioningOrder(sectioningOrder));
}

PlanRequest request = new PlanRequest(
opType.getName(),
Expand Down Expand Up @@ -418,27 +421,29 @@ static Stream<Arguments> sourcesData() {
OperationType otherOpType = EntityFactory.makeOperationType("Other", null);

return Stream.of(
Arguments.of(List.of(), List.of(), null, null, sectionOpType, null),
Arguments.of(block.getBarcode(), A1, blockSampleId, block, sectionOpType, null),
Arguments.of(block.getBarcode(), null, blockSampleId, block, sectionOpType, null),
Arguments.of(nonBlock.getBarcode(), null, sectionSampleId, nonBlock, otherOpType, null),

Arguments.of(destroyedLw.getBarcode(), A1, sectionSampleId, destroyedLw, otherOpType, "Labware already destroyed: ["+destroyedLw.getBarcode()+"]"),
Arguments.of(releasedLw.getBarcode(), A1, sectionSampleId, releasedLw, otherOpType, "Labware already released: ["+releasedLw.getBarcode()+"]"),
Arguments.of(discardedLw.getBarcode(), A1, sectionSampleId, discardedLw, otherOpType, "Labware already discarded: ["+discardedLw.getBarcode()+"]"),
Arguments.of(null, A1, blockSampleId, null, sectionOpType, "Missing source barcode."),
Arguments.of("", A1, blockSampleId, null, sectionOpType, "Missing source barcode."),
Arguments.of("404", A1, blockSampleId, null, sectionOpType, "Unknown labware barcode: [404]"),
Arguments.of(block.getBarcode(), new Address(2,3), blockSampleId, block, sectionOpType,
Arguments.of(List.of(), List.of(), null, 1, null, sectionOpType, null),
Arguments.of(block.getBarcode(), A1, blockSampleId, 1, block, sectionOpType, null),
Arguments.of(block.getBarcode(), null, blockSampleId, 1, block, sectionOpType, null),
Arguments.of(nonBlock.getBarcode(), null, sectionSampleId, 1, nonBlock, otherOpType, null),

Arguments.of(List.of(block.getBarcode(), block.getBarcode()), List.of(A1, A1), blockSampleId, 1, block, sectionOpType, "Repeated sectioning order: 1 from sample "+ blockSampleId+"."),
Arguments.of(block.getBarcode(), A1, blockSampleId, null, block, sectionOpType, "Missing sectioning order."),
Arguments.of(destroyedLw.getBarcode(), A1, sectionSampleId, 1, destroyedLw, otherOpType, "Labware already destroyed: ["+destroyedLw.getBarcode()+"]"),
Arguments.of(releasedLw.getBarcode(), A1, sectionSampleId, 1, releasedLw, otherOpType, "Labware already released: ["+releasedLw.getBarcode()+"]"),
Arguments.of(discardedLw.getBarcode(), A1, sectionSampleId, 1, discardedLw, otherOpType, "Labware already discarded: ["+discardedLw.getBarcode()+"]"),
Arguments.of(null, A1, blockSampleId, 1, null, sectionOpType, "Missing source barcode."),
Arguments.of("", A1, blockSampleId, 1, null, sectionOpType, "Missing source barcode."),
Arguments.of("404", A1, blockSampleId, 1, null, sectionOpType, "Unknown labware barcode: [404]"),
Arguments.of(block.getBarcode(), new Address(2,3), blockSampleId, 1, block, sectionOpType,
"Labware "+block.getBarcode()+" ("+lt.getName()+") has no slot at address B3."),
Arguments.of(block.getBarcode(), null, blockSampleId+1, block, sectionOpType,
Arguments.of(block.getBarcode(), null, blockSampleId+1, 1, block, sectionOpType,
"Slot A1 of labware "+block.getBarcode()+" does not contain a sample with ID "+(blockSampleId+1)+"."),
Arguments.of(block.getBarcode(), null, blockSampleId, block, nonSectionOpType,
Arguments.of(block.getBarcode(), null, blockSampleId, 1, block, nonSectionOpType,
"Operation Nonsection cannot create a section of sample "+blockSampleId+"."),
Arguments.of(nonBlock.getBarcode(), null, sectionSampleId, nonBlock, sectionOpType,
Arguments.of(nonBlock.getBarcode(), null, sectionSampleId, 1, nonBlock, sectionOpType,
"Source "+nonBlock.getBarcode()+",A1 is not a block for operation "+sectionOpType.getName()+"."),
Arguments.of(List.of("404", "404"), List.of(), blockSampleId, null, sectionOpType, "Unknown labware barcode: [404]"),
Arguments.of(List.of("404", "405"), List.of(), blockSampleId, null, sectionOpType, "Unknown labware barcodes: [404, 405]")
Arguments.of(List.of("404", "404"), List.of(), blockSampleId, 1, null, sectionOpType, "Unknown labware barcode: [404]"),
Arguments.of(List.of("404", "405"), List.of(), blockSampleId, 1, null, sectionOpType, "Unknown labware barcodes: [404, 405]")
);
}

Expand Down
5 changes: 5 additions & 0 deletions src/test/resources/graphql/plan.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ mutation {
source:{ barcode:"STAN-B70C" }
addresses: ["A1", "A2"]
sampleId: 55555
sectioningOrder: 1
}
{
source: { barcode: "STAN-B70D" }
addresses: ["B2"]
sampleId: 55556
sampleThickness: "2.5"
sectioningOrder: 2
}
]
}, {
Expand All @@ -25,6 +27,7 @@ mutation {
source:{ barcode:"STAN-B70C" }
addresses: ["A1"]
sampleId: 55555
sectioningOrder: 3
}
]
}, {
Expand All @@ -34,6 +37,7 @@ mutation {
source:{ barcode:"STAN-B70D" }
addresses: ["A1"]
sampleId: 55556
sectioningOrder: 4
}
]
}]
Expand All @@ -58,6 +62,7 @@ mutation {
sample { id }
newSection
sampleThickness
sectioningOrder
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/test/resources/graphql/plan_simple.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mutation {
source:{ barcode:"BARCODE0" }
addresses: ["A1"]
sampleId: 55555
sectioningOrder: 1
}
]
}]
Expand Down
1 change: 1 addition & 0 deletions src/test/resources/graphql/plandata.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ query {
labwareId
address
}
sectioningOrder
}
}
destination {
Expand Down
Loading