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

x1238 Xenium analyser decoding consumables lot #451

Merged
merged 4 commits into from
Sep 13, 2024
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
15 changes: 15 additions & 0 deletions src/main/java/uk/ac/sanger/sccp/stan/config/FieldValidation.java
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,21 @@ public Validator<String> reagentPlateBarcodeValidator() {
return new StringValidator("Reagent plate barcode", 24, 24, charTypes);
}

@Bean
public Validator<String> samplePrepReagentLotValidator() {
return new StringValidator("Sample prep reagent lot", 6, 6, CharacterType.DIGIT);
}

@Bean
public Validator<String> reagentLotValidator() {
return new StringValidator("Reagent lot", 6, 6, CharacterType.DIGIT);
}

@Bean
public Validator<String> decodingConsumablesLotValidator() {
return new StringValidator("Decoding consumables lot number", 6, 6, CharacterType.DIGIT);
}

@Bean
public Validator<String> cytAssistBarcodeValidator() {
Set<CharacterType> charTypes = EnumSet.of(CharacterType.DIGIT, CharacterType.ALPHA, CharacterType.HYPHEN);
Expand Down
34 changes: 32 additions & 2 deletions src/main/java/uk/ac/sanger/sccp/stan/request/AnalyserRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,17 @@ public static class AnalyserLabware {
private String barcode;
private String workNumber;
private CassettePosition position;
private String decodingConsumablesLot;
private List<SampleROI> samples = List.of();

public AnalyserLabware() {}

public AnalyserLabware(String barcode, String workNumber, CassettePosition position, List<SampleROI> samples) {
public AnalyserLabware(String barcode, String workNumber, CassettePosition position,
String decodingConsumablesLot, List<SampleROI> samples) {
this.barcode = barcode;
this.workNumber = workNumber;
this.position = position;
this.decodingConsumablesLot = decodingConsumablesLot;
setSamples(samples);
}

Expand Down Expand Up @@ -140,6 +143,15 @@ public void setPosition(CassettePosition position) {
this.position = position;
}

/** The decoding consumables lot number. */
public String getDecodingConsumablesLot() {
return this.decodingConsumablesLot;
}

public void setDecodingConsumablesLot(String decodingConsumablesLot) {
this.decodingConsumablesLot = decodingConsumablesLot;
}

/**
* The sample regions of interest in this labware.
*/
Expand All @@ -153,7 +165,25 @@ public void setSamples(List<SampleROI> samples) {

@Override
public String toString() {
return String.format("{%s: %s, %s, %s}", position, repr(barcode), repr(workNumber), samples);
return String.format("{%s: %s, %s, %s, %s}", position, repr(barcode), repr(workNumber),
repr(decodingConsumablesLot), samples);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AnalyserLabware that = (AnalyserLabware) o;
return (Objects.equals(this.barcode, that.barcode)
&& Objects.equals(this.workNumber, that.workNumber)
&& this.position == that.position
&& Objects.equals(this.decodingConsumablesLot, that.decodingConsumablesLot)
&& Objects.equals(this.samples, that.samples));
}

@Override
public int hashCode() {
return barcode==null ? 0 : barcode.hashCode();
}
}
// endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ public static class ProbeOperationLabware {
private String barcode;
private String workNumber;
private SlideCosting kitCosting;
private String samplePrepReagentLot;
private List<ProbeLot> probes = List.of();

public ProbeOperationLabware() {}

public ProbeOperationLabware(String barcode, String workNumber, SlideCosting kitCosting, List<ProbeLot> probes) {
public ProbeOperationLabware(String barcode, String workNumber, SlideCosting kitCosting, String samplePrepReagentLot, List<ProbeLot> probes) {
setBarcode(barcode);
setWorkNumber(workNumber);
setKitCosting(kitCosting);
setSamplePrepReagentLot(samplePrepReagentLot);
setProbes(probes);
}

Expand Down Expand Up @@ -67,6 +69,15 @@ public void setKitCosting(SlideCosting kitCosting) {
this.kitCosting = kitCosting;
}

/** Sample prep reagent lot number. */
public String getSamplePrepReagentLot() {
return this.samplePrepReagentLot;
}

public void setSamplePrepReagentLot(String samplePrepReagentLot) {
this.samplePrepReagentLot = samplePrepReagentLot;
}

/**
* The probes used on this labware.
*/
Expand All @@ -86,6 +97,7 @@ public boolean equals(Object o) {
return (Objects.equals(this.barcode, that.barcode)
&& Objects.equals(this.workNumber, that.workNumber)
&& this.kitCosting==that.kitCosting
&& Objects.equals(this.samplePrepReagentLot, that.samplePrepReagentLot)
&& Objects.equals(this.probes, that.probes)
);
}
Expand All @@ -97,7 +109,8 @@ public int hashCode() {

@Override
public String toString() {
return String.format("(%s, %s, workNumber: %s, probes: %s)", repr(barcode), kitCosting, repr(workNumber), probes);
return String.format("(%s, %s, workNumber: %s, samplePrepReagentLot: %s, probes: %s)",
repr(barcode), kitCosting, repr(workNumber), repr(samplePrepReagentLot), probes);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public static class SegmentationLabware {
private List<Integer> commentIds = List.of();
private SlideCosting costing;
private LocalDateTime performed;
private String reagentLot;

/**
* The barcode of the labware.
Expand Down Expand Up @@ -134,6 +135,14 @@ public void setPerformed(LocalDateTime performed) {
this.performed = performed;
}

public String getReagentLot() {
return this.reagentLot;
}

public void setReagentLot(String reagentLot) {
this.reagentLot = reagentLot;
}

@Override
public String toString() {
return BasicUtils.describe(this)
Expand All @@ -142,6 +151,7 @@ public String toString() {
.add("commentIds", commentIds)
.add("costing", costing)
.add("performed", performed==null ? null : performed.toString())
.add("reagentLot", reagentLot)
.reprStringValues()
.toString();
}
Expand All @@ -155,7 +165,9 @@ public boolean equals(Object o) {
&& Objects.equals(this.workNumber, that.workNumber)
&& Objects.equals(this.commentIds, that.commentIds)
&& this.costing == that.costing
&& Objects.equals(this.performed, that.performed));
&& Objects.equals(this.performed, that.performed)
&& Objects.equals(this.reagentLot, that.reagentLot)
);
}

@Override
Expand Down
52 changes: 52 additions & 0 deletions src/main/java/uk/ac/sanger/sccp/stan/service/ProbeServiceImp.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import java.time.*;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;

import static uk.ac.sanger.sccp.utils.BasicUtils.*;
Expand All @@ -24,6 +25,7 @@
@Service
public class ProbeServiceImp implements ProbeService {
public static final String KIT_COSTING_NAME = "kit costing";
public static final String SAMPLE_PREP_REAGENT_LOT_NAME = "sample prep reagent lot";

private final LabwareValidatorFactory lwValFac;
private final LabwareRepo lwRepo;
Expand All @@ -35,6 +37,7 @@ public class ProbeServiceImp implements ProbeService {
private final OperationService opService;
private final WorkService workService;
private final Validator<String> probeLotValidator;
private final Validator<String> samplePrepReagentLotValidator;
private final Clock clock;

@Autowired
Expand All @@ -43,6 +46,7 @@ public ProbeServiceImp(LabwareValidatorFactory lwValFac,
ProbePanelRepo probePanelRepo, LabwareProbeRepo lwProbeRepo, LabwareNoteRepo noteRepo,
OperationService opService, WorkService workService,
@Qualifier("probeLotNumberValidator") Validator<String> probeLotValidator,
@Qualifier("samplePrepReagentLotValidator") Validator<String> samplePrepReagentLotValidator,
Clock clock) {
this.lwValFac = lwValFac;
this.lwRepo = lwRepo;
Expand All @@ -54,6 +58,7 @@ public ProbeServiceImp(LabwareValidatorFactory lwValFac,
this.opService = opService;
this.workService = workService;
this.probeLotValidator = probeLotValidator;
this.samplePrepReagentLotValidator = samplePrepReagentLotValidator;
this.clock = clock;
}

Expand All @@ -79,6 +84,7 @@ public OperationResult recordProbeOperation(User user, ProbeOperationRequest req
.toList();
UCMap<Work> work = workService.validateUsableWorks(problems, workNumbers);
checkKitCostings(problems, request.getLabware());
checkSamplePrepReagentLots(problems, request.getLabware());
UCMap<ProbePanel> probes = validateProbes(problems, request.getLabware());
if (request.getPerformed()!=null) {
validateTimestamp(problems, request.getPerformed(), labware);
Expand Down Expand Up @@ -160,6 +166,28 @@ public void checkKitCostings(Collection<String> problems, Collection<ProbeOperat
}
}

/**
* Checks sample prep reagent lots.
* Lots are trimmed; empty lots are nulled; missing lots are skipped.
* @param problems receptacle for problems
* @param pols details of the request
*/
public void checkSamplePrepReagentLots(Collection<String> problems, Collection<ProbeOperationLabware> pols) {
if (pols != null) {
final Consumer<String> addProblem = problems::add;
for (ProbeOperationLabware pol : pols) {
String lot = pol.getSamplePrepReagentLot();
if (lot != null) {
lot = emptyToNull(lot.trim());
pol.setSamplePrepReagentLot(lot);
if (lot != null) {
samplePrepReagentLotValidator.validate(lot, addProblem);
}
}
}
}
}

/**
* Loads and checks the probes indicated in the request, their lot numbers and plexes.
* @param problems receptacle for problems
Expand Down Expand Up @@ -240,6 +268,7 @@ public OperationResult perform(User user, Collection<ProbeOperationLabware> pols
UCMap<Work> workMap) {
UCMap<Operation> lwOps = makeOps(user, opType, pols, lwMap, time);
saveKitCostings(pols, lwMap, lwOps);
saveSamplePrepReagentLots(pols, lwMap, lwOps);
linkWork(pols, lwOps, workMap);
saveProbes(pols, lwOps, lwMap, probeMap);
return assembleResult(pols, lwMap, lwOps);
Expand Down Expand Up @@ -324,6 +353,29 @@ public void saveProbes(Collection<ProbeOperationLabware> pols, UCMap<Operation>
lwProbeRepo.saveAll(lwProbes);
}

/**
* Saves the indicated sample prep reagent lots against the indicated labware and ops
* @param pols request details
* @param lwMap map to look up labware by barcode
* @param lwOps map to look up operation by labware barcode
*/
public void saveSamplePrepReagentLots(Collection<ProbeOperationLabware> pols,
UCMap<Labware> lwMap, UCMap<Operation> lwOps) {
List<LabwareNote> notes = new ArrayList<>();
for (ProbeOperationLabware pol : pols) {
String lot = pol.getSamplePrepReagentLot();
if (!nullOrEmpty(lot)) {
String barcode = pol.getBarcode();
Labware lw = lwMap.get(barcode);
Operation op = lwOps.get(barcode);
notes.add(new LabwareNote(null, lw.getId(), op.getId(), SAMPLE_PREP_REAGENT_LOT_NAME, lot));
}
}
if (!notes.isEmpty()) {
noteRepo.saveAll(notes);
}
}

/**
* Assembles an operation result whose order matches the order given in the request.
* @param pols the request specifying labware barcodes
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package uk.ac.sanger.sccp.stan.service;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import uk.ac.sanger.sccp.stan.model.*;
import uk.ac.sanger.sccp.stan.repo.*;
Expand All @@ -14,11 +15,11 @@

import java.time.*;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toSet;
import static uk.ac.sanger.sccp.utils.BasicUtils.inMap;
import static uk.ac.sanger.sccp.utils.BasicUtils.nullOrEmpty;
import static uk.ac.sanger.sccp.utils.BasicUtils.*;

/**
* @author dr6
Expand All @@ -39,10 +40,13 @@ public class SegmentationServiceImp implements SegmentationService {
private final OperationCommentRepo opComRepo;
private final LabwareNoteRepo noteRepo;

private final Validator<String> reagentLotValidator;

public SegmentationServiceImp(Clock clock, ValidationHelperFactory valHelperFactory,
OperationService opService, WorkService workService,
OperationRepo opRepo, OperationTypeRepo opTypeRepo,
OperationCommentRepo opComRepo, LabwareNoteRepo noteRepo) {
OperationCommentRepo opComRepo, LabwareNoteRepo noteRepo,
@Qualifier("reagentLotValidator") Validator<String> reagentLotValidator) {
this.clock = clock;
this.valHelperFactory = valHelperFactory;
this.opService = opService;
Expand All @@ -51,6 +55,7 @@ public SegmentationServiceImp(Clock clock, ValidationHelperFactory valHelperFact
this.opTypeRepo = opTypeRepo;
this.opComRepo = opComRepo;
this.noteRepo = noteRepo;
this.reagentLotValidator = reagentLotValidator;
}

@Override
Expand Down Expand Up @@ -92,6 +97,7 @@ SegmentationData validate(User user, SegmentationRequest request) {
checkCostings(problems, data.opType, request.getLabware());
UCMap<LocalDateTime> priorOpTimes = checkPriorOps(problems, data.opType, data.labware.values());
checkTimestamps(val, clock, request.getLabware(), data.labware, priorOpTimes);
checkReagentLots(problems, request.getLabware());
return data;
}

Expand Down Expand Up @@ -221,6 +227,25 @@ void checkTimestamps(ValidationHelper val, Clock clock, List<SegmentationLabware
}
}

/**
* Checks the reagent lots
* @param problems receptacle for problems
* @param lwReqs details of the request
*/
void checkReagentLots(final Collection<String> problems, final Collection<SegmentationLabware> lwReqs) {
final Consumer<String> problemAdd = problems::add;
for (SegmentationLabware lwReq : lwReqs) {
String lot = lwReq.getReagentLot();
if (lot != null) {
lot = emptyToNull(lot.trim());
lwReq.setReagentLot(lot);
}
if (lot != null) {
reagentLotValidator.validate(lot, problemAdd);
}
}
}

/**
* Records the operations and all associated information for the request
* @param lwReqs details of the request
Expand Down Expand Up @@ -281,6 +306,9 @@ Operation recordOp(User user, SegmentationData data, SegmentationLabware lwReq,
newNotes.add(new LabwareNote(null, lw.getId(), op.getId(), "costing",
lwReq.getCosting().name()));
}
if (lwReq.getReagentLot()!=null) {
newNotes.add(new LabwareNote(null, lw.getId(), op.getId(), "reagent lot", lwReq.getReagentLot()));
}
if (!lwReq.getCommentIds().isEmpty()) {
lwReq.getCommentIds().stream()
.map(data.comments::get)
Expand Down
Loading
Loading