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

[GLT-4060] require release for stopped cases #172

Merged
merged 2 commits into from
Jan 3, 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
1 change: 1 addition & 0 deletions changes/change_stopped_release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Release approval and release steps are now required even for stopped cases
1 change: 1 addition & 0 deletions changes/fix_stopped_overdue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Steps that were skipped due to the case being stopped could still be shown as overdue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ public enum CaseFilterKey {
|| kase.getDonor().getExternalName().toLowerCase().contains(string.toLowerCase())),
PENDING(string -> {
PendingState state = getState(string);
Predicate<Case> notStoppedOrPaused = kase -> !kase.getRequisition().isStopped() && !kase.getRequisition().isPaused();
Predicate<Case> notStoppedOrPaused = kase ->
(!state.isStoppable() || !kase.getRequisition().isStopped())
&& !kase.getRequisition().isPaused();
return notStoppedOrPaused.and(state.predicate());
}) {
@Override
Expand Down
38 changes: 21 additions & 17 deletions src/main/java/ca/on/oicr/gsi/dimsum/service/filtering/CaseSort.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public enum CaseSort {
@Override
public Comparator<Case> comparator(Map<Long, Assay> assaysById) {
return (a, b) -> {
// If one case is inactive (stopped/paused/complete), the other is more urgent
// If one case is inactive (paused/complete), the other is more urgent
// If both cases are inactive, consider them equal
if (isInactive(a)) {
if (isInactive(b)) {
Expand Down Expand Up @@ -58,8 +58,7 @@ public Comparator<Case> comparator(Map<Long, Assay> assaysById) {
}

private static boolean isInactive(Case kase) {
return kase.isStopped()
|| kase.getRequisition().isPaused()
return kase.getRequisition().isPaused()
|| CompletedGate.RELEASE.qualifyCase(kase);
}

Expand Down Expand Up @@ -93,23 +92,28 @@ private static int calculateStepDaysOverdue(Case kase, AssayTargets targets) {
}

private static boolean isStepBehind(Integer target, Case kase, CompletedGate completedGate) {
return target != null && kase.getCaseDaysSpent() > target && !completedGate.qualifyCase(kase);
return target != null && kase.getCaseDaysSpent() > target
&& (!kase.getRequisition().isStopped() || !completedGate.isStoppable())
&& !completedGate.qualifyCase(kase);
}

private static int calculateStepDaysRemaining(Case kase, AssayTargets targets) {
if (!CompletedGate.RECEIPT.qualifyCase(kase)) {
return calculateDaysRemaining(kase, targets.getReceiptDays());
} else if (!CompletedGate.EXTRACTION.qualifyCase(kase)) {
return calculateDaysRemaining(kase, targets.getExtractionDays());
} else if (!CompletedGate.LIBRARY_PREPARATION.qualifyCase(kase)) {
return calculateDaysRemaining(kase, targets.getLibraryPreparationDays());
} else if (!CompletedGate.LIBRARY_QUALIFICATION.qualifyCase(kase)) {
return calculateDaysRemaining(kase, targets.getLibraryQualificationDays());
} else if (!CompletedGate.FULL_DEPTH_SEQUENCING.qualifyCase(kase)) {
return calculateDaysRemaining(kase, targets.getFullDepthSequencingDays());
} else if (!CompletedGate.ANALYSIS_REVIEW.qualifyCase(kase)) {
return calculateDaysRemaining(kase, targets.getAnalysisReviewDays());
} else if (!CompletedGate.RELEASE_APPROVAL.qualifyCase(kase)) {
if (!kase.getRequisition().isStopped()) {
if (!CompletedGate.RECEIPT.qualifyCase(kase)) {
return calculateDaysRemaining(kase, targets.getReceiptDays());
} else if (!CompletedGate.EXTRACTION.qualifyCase(kase)) {
return calculateDaysRemaining(kase, targets.getExtractionDays());
} else if (!CompletedGate.LIBRARY_PREPARATION.qualifyCase(kase)) {
return calculateDaysRemaining(kase, targets.getLibraryPreparationDays());
} else if (!CompletedGate.LIBRARY_QUALIFICATION.qualifyCase(kase)) {
return calculateDaysRemaining(kase, targets.getLibraryQualificationDays());
} else if (!CompletedGate.FULL_DEPTH_SEQUENCING.qualifyCase(kase)) {
return calculateDaysRemaining(kase, targets.getFullDepthSequencingDays());
} else if (!CompletedGate.ANALYSIS_REVIEW.qualifyCase(kase)) {
return calculateDaysRemaining(kase, targets.getAnalysisReviewDays());
}
}
if (!CompletedGate.RELEASE_APPROVAL.qualifyCase(kase)) {
return calculateDaysRemaining(kase, targets.getReleaseApprovalDays());
} else if (!CompletedGate.RELEASE.qualifyCase(kase)) {
return calculateDaysRemaining(kase, targets.getReleaseDays());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

public enum CompletedGate {
// @formatter:off
RECEIPT("Receipt") {
RECEIPT("Receipt", true) {
@Override
public boolean qualifyCase(Case kase) {
return Helpers.isReceiptCompleted(kase);
Expand All @@ -37,7 +37,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}
}
},
EXTRACTION("Extraction") {
EXTRACTION("Extraction", true) {
@Override
public boolean qualifyTest(Test test) {
return test.isExtractionSkipped() || Helpers.isCompleted(test.getExtractions());
Expand All @@ -53,7 +53,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {

}
},
LIBRARY_PREPARATION("Library Preparation") {
LIBRARY_PREPARATION("Library Preparation", true) {
@Override
public boolean qualifyTest(Test test) {
return test.isLibraryPreparationSkipped()
Expand All @@ -70,7 +70,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}

},
LIBRARY_QUALIFICATION("Library Qualification") {
LIBRARY_QUALIFICATION("Library Qualification", true) {
@Override
public boolean qualifyTest(Test test) {
return Helpers.isCompleted(test.getLibraryQualifications());
Expand All @@ -85,7 +85,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}
}
},
FULL_DEPTH_SEQUENCING("Full-Depth Sequencing") {
FULL_DEPTH_SEQUENCING("Full-Depth Sequencing", true) {
@Override
public boolean qualifyTest(Test test) {
return Helpers.isCompleted(test.getFullDepthSequencings());
Expand All @@ -100,7 +100,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}
}
},
ANALYSIS_REVIEW("Analysis Review") {
ANALYSIS_REVIEW("Analysis Review", true) {
@Override
public boolean qualifyCase(Case kase) {
return Helpers.isCompletedRequisitionQc(kase.getRequisition(), Requisition::getAnalysisReviews);
Expand All @@ -113,7 +113,7 @@ public boolean qualifyRequisition(Requisition requisition) {
}

},
RELEASE_APPROVAL("Release Approval") {
RELEASE_APPROVAL("Release Approval", false) {
@Override
public boolean qualifyCase(Case kase) {
return Helpers.isCompletedRequisitionQc(kase.getRequisition(), Requisition::getReleaseApprovals);
Expand All @@ -124,7 +124,7 @@ public boolean qualifyRequisition(Requisition requisition) {
return Helpers.isCompletedRequisitionQc(requisition, Requisition::getReleaseApprovals);
}
},
RELEASE("Release") {
RELEASE("Release", false) {
@Override
public boolean qualifyCase(Case kase) {
return Helpers.isCompletedRequisitionQc(kase.getRequisition(), Requisition::getReleases);
Expand All @@ -145,18 +145,24 @@ public static CompletedGate getByLabel(String label) {
}

private final String label;
private final boolean stoppable;
private final Predicate<Case> casePredicate = this::qualifyCase;
private final Predicate<Test> testPredicate = this::qualifyTest;
private final Predicate<Requisition> requisitionPredicate = this::qualifyRequisition;

private CompletedGate(String label) {
private CompletedGate(String label, boolean stoppable) {
this.label = label;
this.stoppable = stoppable;
}

public String getLabel() {
return label;
}

public boolean isStoppable() {
return stoppable;
}

public Predicate<Case> predicate() {
return casePredicate;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
public enum PendingState {

// @formatter:off
RECEIPT_QC("Receipt QC") {
RECEIPT_QC("Receipt QC", true) {
@Override
public boolean qualifyCase(Case kase) {
return Helpers.isPendingReceiptQc(kase);
Expand All @@ -64,7 +64,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}
}
},
EXTRACTION("Extraction") {
EXTRACTION("Extraction", true) {
@Override
public boolean qualifyCase(Case kase) {
return Helpers.isReceiptPassed
Expand All @@ -86,7 +86,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}
}
},
EXTRACTION_QC("Extraction QC Sign-Off") {
EXTRACTION_QC("Extraction QC Sign-Off", true) {
@Override
public boolean qualifyTest(Test test) {
return Helpers.hasPendingQc(test.getExtractions());
Expand All @@ -101,7 +101,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}
}
},
LIBRARY_PREPARATION("Library Preparation") {
LIBRARY_PREPARATION("Library Preparation", true) {
@Override
public boolean qualifyTest(Test test) {
return !test.isLibraryPreparationSkipped()
Expand All @@ -119,7 +119,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}
}
},
LIBRARY_QC("Library QC Sign-Off") {
LIBRARY_QC("Library QC Sign-Off", true) {
@Override
public boolean qualifyTest(Test test) {
return Helpers.hasPendingQc(test.getLibraryPreparations());
Expand All @@ -134,7 +134,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}
}
},
LIBRARY_QUALIFICATION("Library Qualification") {
LIBRARY_QUALIFICATION("Library Qualification", true) {
@Override
public boolean qualifyTest(Test test) {
return Helpers.hasPendingWork(test.getLibraryQualifications(), test.getLibraryPreparations());
Expand All @@ -152,7 +152,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}
}
},
LIBRARY_QUALIFICATION_QC("Library Qualification QC Sign-Off") {
LIBRARY_QUALIFICATION_QC("Library Qualification QC Sign-Off", true) {
@Override
public boolean qualifyTest(Test test) {
return Helpers.hasPendingQc(test.getLibraryQualifications());
Expand All @@ -167,7 +167,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}
}
},
LIBRARY_QUALIFICATION_DATA_REVIEW("Library Qualification Data Review") {
LIBRARY_QUALIFICATION_DATA_REVIEW("Library Qualification Data Review", true) {
@Override
public boolean qualifyTest(Test test) {
return Helpers.hasPendingDataReview(test.getLibraryQualifications());
Expand All @@ -182,7 +182,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}
}
},
FULL_DEPTH_SEQUENCING("Full-Depth Sequencing") {
FULL_DEPTH_SEQUENCING("Full-Depth Sequencing", true) {
@Override
public boolean qualifyTest(Test test) {
return Helpers.hasPendingWork(test.getFullDepthSequencings(), test.getLibraryQualifications());
Expand All @@ -201,7 +201,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}
}
},
FULL_DEPTH_QC("Full-Depth Sequencing QC Sign-Off") {
FULL_DEPTH_QC("Full-Depth Sequencing QC Sign-Off", true) {
@Override
public boolean qualifyTest(Test test) {
return Helpers.hasPendingQc(test.getFullDepthSequencings());
Expand All @@ -216,7 +216,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}
}
},
FULL_DEPTH_DATA_REVIEW("Full-Depth Sequencing Data Review") {
FULL_DEPTH_DATA_REVIEW("Full-Depth Sequencing Data Review", true) {
@Override
public boolean qualifyTest(Test test) {
return Helpers.hasPendingDataReview(test.getFullDepthSequencings());
Expand All @@ -231,7 +231,7 @@ public boolean qualifySample(Sample sample, MetricCategory requestCategory) {
}
}
},
ANALYSIS_REVIEW("Analysis Review") {
ANALYSIS_REVIEW("Analysis Review", true) {
@Override
public boolean qualifyCase(Case kase) {
return kase.getTests().stream().allMatch(Helpers.isCompleted(Test::getFullDepthSequencings))
Expand All @@ -243,11 +243,11 @@ public boolean qualifyRequisition(Requisition requisition) {
return requisition.getAnalysisReviews().isEmpty();
}
},
RELEASE_APPROVAL("Release Approval") {
RELEASE_APPROVAL("Release Approval", false) {

@Override
public boolean qualifyCase(Case kase) {
return Helpers.isCompletedRequisitionQc(kase, Requisition::getAnalysisReviews)
return (kase.isStopped() || Helpers.isCompletedRequisitionQc(kase, Requisition::getAnalysisReviews))
&& this.qualifyRequisition(kase.getRequisition());
}

Expand All @@ -256,7 +256,7 @@ public boolean qualifyRequisition(Requisition requisition) {
return requisition.getReleaseApprovals().isEmpty();
}
},
RELEASE("Release") {
RELEASE("Release", false) {
@Override
public boolean qualifyCase(Case kase) {
return Helpers.isCompletedRequisitionQc(kase, Requisition::getReleaseApprovals)
Expand All @@ -278,18 +278,24 @@ public static PendingState getByLabel(String label) {
}

private final String label;
private final boolean stoppable;
private final Predicate<Case> casePredicate = this::qualifyCase;
private final Predicate<Test> testPredicate = this::qualifyTest;
private final Predicate<Requisition> requisitionPredicate = this::qualifyRequisition;

private PendingState(String label) {
private PendingState(String label, boolean stoppable) {
this.label = label;
this.stoppable = stoppable;
}

public String getLabel() {
return label;
}

public boolean isStoppable() {
return stoppable;
}

public Predicate<Case> predicate() {
return casePredicate;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,30 +99,6 @@ public void testSortByUrgencyBothCompleted() {
testUrgencyComparator(a, b, true);
}

@org.junit.jupiter.api.Test
public void testSortByUrgencyBothStopped() {
Case a = mockEmptyCase(1L);
when(a.isStopped()).thenReturn(true);

Case b = mockEmptyCase(1L);
when(b.isStopped()).thenReturn(true);

// Both stopped, should be considered equivalent
testUrgencyComparator(a, b, true);
}

@org.junit.jupiter.api.Test
public void testSortByUrgencyOneCompletedOneStopped() {
Case a = mockEmptyCase(1L);
a.getRequisition().getReleases().add(mockRequisitionQc(true, 2023, 1, 1));

Case b = mockEmptyCase(1L);
when(b.isStopped()).thenReturn(true);

// One completed, one stopped. Should be considered equivalent
testUrgencyComparator(a, b, true);
}

@org.junit.jupiter.api.Test
public void testSortByUrgencyOneCompleted() {
Case a = mockEmptyCase(1L);
Expand All @@ -134,17 +110,6 @@ public void testSortByUrgencyOneCompleted() {
testUrgencyComparator(a, b);
}

@org.junit.jupiter.api.Test
public void testSortByUrgencyOneStopped() {
Case a = mockEmptyCase(1L);

Case b = mockEmptyCase(1L);
when(b.isStopped()).thenReturn(true);

// B stopped. A is more urgent
testUrgencyComparator(a, b);
}

@org.junit.jupiter.api.Test
public void testSortByUrgencyBothPaused() {
Case a = mockEmptyCase(1L);
Expand All @@ -168,18 +133,6 @@ public void testSortByUrgencyOnePaused() {
testUrgencyComparator(a, b);
}

@org.junit.jupiter.api.Test
public void testSortByUrgencyOnePausedOneStopped() {
Case a = mockEmptyCase(1L);
when(a.getRequisition().isPaused()).thenReturn(true);

Case b = mockEmptyCase(1L);
when(b.isStopped()).thenReturn(true);

// A paused, B stopped. Should be considered equivalent
testUrgencyComparator(a, b, true);
}

@org.junit.jupiter.api.Test
public void testSortByUrgencyBothOverdue() {
Case a = mockEmptyCase(1L);
Expand Down
Loading