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-4274] show omissions on the Run Details page #235

Merged
merged 2 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions changes/add_run_omissions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Omissions table on the Run Details page, showing libraries that are included in the run but not a
part of any case
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@
<dependency>
<groupId>ca.on.oicr.gsi.cardea</groupId>
<artifactId>cardea-data</artifactId>
<version>1.14.0</version>
<version>1.15.1-SNAPSHOT</version>
</dependency>
</dependencies>

Expand Down Expand Up @@ -184,4 +184,4 @@
</plugins>
</build>

</project>
</project>
30 changes: 20 additions & 10 deletions src/main/java/ca/on/oicr/gsi/dimsum/CaseLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@
import org.springframework.web.reactive.function.client.WebClient;
import ca.on.oicr.gsi.cardea.data.Assay;
import ca.on.oicr.gsi.cardea.data.Case;
import ca.on.oicr.gsi.cardea.data.OmittedRunSample;
import ca.on.oicr.gsi.cardea.data.OmittedSample;
import ca.on.oicr.gsi.cardea.data.Project;
import ca.on.oicr.gsi.cardea.data.RunAndLibraries;
import ca.on.oicr.gsi.cardea.data.Sample;
import ca.on.oicr.gsi.cardea.data.Test;
import ca.on.oicr.gsi.dimsum.data.CaseData;
import ca.on.oicr.gsi.dimsum.data.ProjectSummary;
import ca.on.oicr.gsi.dimsum.data.RunAndLibraries;
import ca.on.oicr.gsi.dimsum.service.filtering.CompletedGate;
import ca.on.oicr.gsi.dimsum.service.filtering.PendingState;
import io.micrometer.core.instrument.MeterRegistry;
Expand Down Expand Up @@ -72,7 +73,8 @@ public CaseData load(ZonedDateTime previousTimestamp) throws IOException {
ca.on.oicr.gsi.cardea.data.CaseData cardeaCaseData = loadCardeaData(builder);

Map<Long, Assay> assaysById = cardeaCaseData.getAssaysById();
Map<String, RunAndLibraries> runsByName = sortRuns(cardeaCaseData.getCases());
Map<String, RunAndLibraries> runsByName =
sortRuns(cardeaCaseData.getCases(), cardeaCaseData.getOmittedRunSamples());
List<OmittedSample> omittedSamples = cardeaCaseData.getOmittedSamples();
Set<String> requisitionNames = loadRequisitionNames(cardeaCaseData.getCases());
Set<String> projectsNames = loadProjectsNames(cardeaCaseData.getCases());
Expand All @@ -97,7 +99,7 @@ requisitionNames, projectsNames, donorNames, getRunNames(runsByName), testNames,
*
* @param builder WebClient builder state used to fetch data from Cardea API `/dimsum` endpoint
*/
public ca.on.oicr.gsi.cardea.data.CaseData loadCardeaData(WebClient.Builder builder)
private ca.on.oicr.gsi.cardea.data.CaseData loadCardeaData(WebClient.Builder builder)
throws IOException {
ca.on.oicr.gsi.cardea.data.CaseData data = builder
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(LIMIT_FOR_DATA_LOAD))
Expand Down Expand Up @@ -145,8 +147,9 @@ private static Set<String> getRunNames(Map<String, RunAndLibraries> runsByName)
return runsByName.keySet();
}

private Map<String, RunAndLibraries> sortRuns(List<Case> cases) {
Map<String, RunAndLibraries.Builder> map = new HashMap<>();
private Map<String, RunAndLibraries> sortRuns(List<Case> cases,
List<OmittedRunSample> omittedRunSamples) {
Map<Long, RunAndLibraries.Builder> map = new HashMap<>();
for (Case kase : cases) {
for (Test test : kase.getTests()) {
for (Sample sample : test.getLibraryQualifications()) {
Expand All @@ -159,18 +162,25 @@ private Map<String, RunAndLibraries> sortRuns(List<Case> cases) {
}
}
}
for (OmittedRunSample sample : omittedRunSamples) {
if (map.containsKey(sample.getRunId())) {
map.get(sample.getRunId()).addOmittedSample(sample);
} else {
log.warn("OmittedRunSample found for missing run ID: {}", sample.getRunId());
}
}
return map.values().stream()
.map(RunAndLibraries.Builder::build)
.collect(Collectors.toMap(x -> x.getRun().getName(), Function.identity()));
}

private void addRunLibrary(Map<String, RunAndLibraries.Builder> map, Sample sample,
private void addRunLibrary(Map<Long, RunAndLibraries.Builder> map, Sample sample,
BiConsumer<RunAndLibraries.Builder, Sample> addSample) {
String runName = sample.getRun().getName();
if (!map.containsKey(runName)) {
map.put(runName, new RunAndLibraries.Builder().run(sample.getRun()));
long runId = sample.getRun().getId();
if (!map.containsKey(runId)) {
map.put(runId, new RunAndLibraries.Builder().run(sample.getRun()));
}
addSample.accept(map.get(runName), sample);
addSample.accept(map.get(runId), sample);
}

@FunctionalInterface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import ca.on.oicr.gsi.cardea.data.Run;
import ca.on.oicr.gsi.cardea.data.RunAndLibraries;
import ca.on.oicr.gsi.cardea.data.Sample;
import ca.on.oicr.gsi.dimsum.controller.NotFoundException;
import ca.on.oicr.gsi.dimsum.data.RunAndLibraries;
import ca.on.oicr.gsi.dimsum.service.CaseService;

@Controller
Expand Down Expand Up @@ -40,7 +40,8 @@ public String getRunPage(@PathVariable String runName, ModelMap model) {
.collect(Collectors.joining(",")));
model.put("showLibraryQualifications", !runAndLibraries.getLibraryQualifications().isEmpty());
model.put("showFullDepthSequencings", !runAndLibraries.getFullDepthSequencings().isEmpty());
return "run";
model.put("showOmitted", !runAndLibraries.getOmittedSamples().isEmpty());
return "run-detail";
}

private static String getRunStatus(Run run) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ca.on.oicr.gsi.cardea.data.OmittedRunSample;
import ca.on.oicr.gsi.cardea.data.Run;
import ca.on.oicr.gsi.cardea.data.RunAndLibraries;
import ca.on.oicr.gsi.cardea.data.Sample;
import ca.on.oicr.gsi.dimsum.controller.NotFoundException;
import ca.on.oicr.gsi.dimsum.controller.rest.request.DataQuery;
import ca.on.oicr.gsi.dimsum.data.RunAndLibraries;
import ca.on.oicr.gsi.dimsum.service.CaseService;
import ca.on.oicr.gsi.dimsum.service.filtering.CaseFilter;
import ca.on.oicr.gsi.dimsum.service.filtering.OmittedRunSampleSort;
import ca.on.oicr.gsi.dimsum.service.filtering.RunFilter;
import ca.on.oicr.gsi.dimsum.service.filtering.RunSort;
import ca.on.oicr.gsi.dimsum.service.filtering.SampleSort;
Expand Down Expand Up @@ -59,6 +61,16 @@ public TableData<Sample> getFullDepthSequencings(@PathVariable String runName,
query.getPageNumber(), sort, descending, filters);
}

@PostMapping("/{runName}/omissions")
public TableData<OmittedRunSample> getOmitted(@PathVariable String runName,
@RequestBody DataQuery query) {
checkRunExists(runName);
OmittedRunSampleSort sort = parseSort(query, OmittedRunSampleSort::getByLabel);
boolean descending = parseDescending(query);
return caseService.getOmittedSamplesForRun(runName, query.getPageSize(),
query.getPageNumber(), sort, descending);
}

private void checkRunExists(String runName) {
RunAndLibraries runAndLibraries = caseService.getRunAndLibraries(runName);
if (runAndLibraries == null) {
Expand Down
1 change: 0 additions & 1 deletion src/main/java/ca/on/oicr/gsi/dimsum/data/CaseData.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import ca.on.oicr.gsi.cardea.data.Assay;
import ca.on.oicr.gsi.cardea.data.Case;
import ca.on.oicr.gsi.cardea.data.OmittedSample;
import ca.on.oicr.gsi.cardea.data.RunAndLibraries;

@Immutable
public class CaseData {
Expand Down
77 changes: 77 additions & 0 deletions src/main/java/ca/on/oicr/gsi/dimsum/data/RunAndLibraries.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package ca.on.oicr.gsi.dimsum.data;

import static java.util.Objects.requireNonNull;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import ca.on.oicr.gsi.cardea.data.OmittedRunSample;
import ca.on.oicr.gsi.cardea.data.Run;
import ca.on.oicr.gsi.cardea.data.Sample;

/**
* Immutable RunAndLibraries
*/
public class RunAndLibraries {

private final Set<Sample> fullDepthSequencings;
private final Set<Sample> libraryQualifications;
private final Set<OmittedRunSample> omittedSamples;
private final Run run;

private RunAndLibraries(Builder builder) {
this.run = requireNonNull(builder.run);
this.libraryQualifications = Collections.unmodifiableSet(builder.libraryQualifications);
this.fullDepthSequencings = Collections.unmodifiableSet(builder.fullDepthSequencings);
this.omittedSamples = Collections.unmodifiableSet(builder.omittedSamples);
}

public Set<Sample> getFullDepthSequencings() {
return fullDepthSequencings;
}

public Set<Sample> getLibraryQualifications() {
return libraryQualifications;
}

public Set<OmittedRunSample> getOmittedSamples() {
return omittedSamples;
}

public Run getRun() {
return run;
}

public static class Builder {

private Set<Sample> fullDepthSequencings = new HashSet<>();
private Set<Sample> libraryQualifications = new HashSet<>();
private Set<OmittedRunSample> omittedSamples = new HashSet<>();
private Run run;

public Builder addFullDepthSequencing(Sample sample) {
fullDepthSequencings.add(sample);
return this;
}

public Builder addLibraryQualification(Sample sample) {
libraryQualifications.add(sample);
return this;
}

public Builder addOmittedSample(OmittedRunSample sample) {
omittedSamples.add(sample);
return this;
}

public RunAndLibraries build() {
return new RunAndLibraries(this);
}

public Builder run(Run run) {
this.run = run;
return this;
}

}

}
21 changes: 20 additions & 1 deletion src/main/java/ca/on/oicr/gsi/dimsum/service/CaseService.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
import ca.on.oicr.gsi.cardea.data.Case;
import ca.on.oicr.gsi.cardea.data.CaseRelease;
import ca.on.oicr.gsi.cardea.data.MetricCategory;
import ca.on.oicr.gsi.cardea.data.OmittedRunSample;
import ca.on.oicr.gsi.cardea.data.OmittedSample;
import ca.on.oicr.gsi.cardea.data.Project;
import ca.on.oicr.gsi.cardea.data.Run;
import ca.on.oicr.gsi.cardea.data.RunAndLibraries;
import ca.on.oicr.gsi.cardea.data.Sample;
import ca.on.oicr.gsi.cardea.data.Test;
import ca.on.oicr.gsi.dimsum.CaseLoader;
Expand All @@ -42,11 +42,13 @@
import ca.on.oicr.gsi.dimsum.data.ProjectSummary;
import ca.on.oicr.gsi.dimsum.data.ProjectSummaryField;
import ca.on.oicr.gsi.dimsum.data.ProjectSummaryRow;
import ca.on.oicr.gsi.dimsum.data.RunAndLibraries;
import ca.on.oicr.gsi.dimsum.data.TestTableView;
import ca.on.oicr.gsi.dimsum.service.filtering.CaseFilter;
import ca.on.oicr.gsi.dimsum.service.filtering.CaseFilterKey;
import ca.on.oicr.gsi.dimsum.service.filtering.CaseSort;
import ca.on.oicr.gsi.dimsum.service.filtering.CompletedGate;
import ca.on.oicr.gsi.dimsum.service.filtering.OmittedRunSampleSort;
import ca.on.oicr.gsi.dimsum.service.filtering.OmittedSampleFilter;
import ca.on.oicr.gsi.dimsum.service.filtering.OmittedSampleFilterKey;
import ca.on.oicr.gsi.dimsum.service.filtering.OmittedSampleSort;
Expand Down Expand Up @@ -558,6 +560,23 @@ public TableData<Sample> getFullDepthSequencingsForRun(String runName, int pageS
RunAndLibraries::getFullDepthSequencings, MetricCategory.FULL_DEPTH_SEQUENCING);
}

public TableData<OmittedRunSample> getOmittedSamplesForRun(String runName, int pageSize,
int pageNumber, OmittedRunSampleSort sort, boolean descending) {
RunAndLibraries runAndLibraries = caseData.getRunAndLibraries(runName);
Set<OmittedRunSample> samples =
runAndLibraries == null ? Collections.emptySet() : runAndLibraries.getOmittedSamples();

TableData<OmittedRunSample> data = new TableData<>();
data.setTotalCount(samples.size());
data.setFilteredCount(samples.size());
data.setItems(samples.stream()
.sorted(descending ? sort.comparator().reversed() : sort.comparator())
.skip(pageSize * (pageNumber - 1))
.limit(pageSize)
.toList());
return data;
}

private TableData<Sample> getRunLibraries(String runName, int pageSize,
int pageNumber, SampleSort sort, boolean descending, Collection<CaseFilter> filters,
Function<RunAndLibraries, Set<Sample>> getSamples, MetricCategory requestCategory) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
import ca.on.oicr.gsi.cardea.data.MetricCategory;
import ca.on.oicr.gsi.cardea.data.MetricSubcategory;
import ca.on.oicr.gsi.cardea.data.Run;
import ca.on.oicr.gsi.cardea.data.RunAndLibraries;
import ca.on.oicr.gsi.cardea.data.Sample;
import ca.on.oicr.gsi.dimsum.data.IssueState;
import ca.on.oicr.gsi.dimsum.data.Notification;
import ca.on.oicr.gsi.dimsum.data.RunAndLibraries;
import ca.on.oicr.gsi.dimsum.data.RunQcCommentSummary;
import ca.on.oicr.gsi.dimsum.service.filtering.NotificationSort;
import ca.on.oicr.gsi.dimsum.service.filtering.TableData;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package ca.on.oicr.gsi.dimsum.service.filtering;

import java.util.Comparator;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import ca.on.oicr.gsi.cardea.data.OmittedRunSample;
import ca.on.oicr.gsi.dimsum.util.DataUtils;

public enum OmittedRunSampleSort {

// @formatter:off
NAME("Name", Comparator.comparing(OmittedRunSample::getName)),
QC_STATUS("QC Status", Comparator.comparing(OmittedRunSampleSort::getQcStatusSortPriority));
// @formatter:on

private static final Map<String, OmittedRunSampleSort> map =
Stream.of(OmittedRunSampleSort.values())
.collect(Collectors.toMap(OmittedRunSampleSort::getLabel, Function.identity()));

public static OmittedRunSampleSort getByLabel(String label) {
return map.get(label);
}

private final String label;
private final Comparator<OmittedRunSample> comparator;

private OmittedRunSampleSort(String label, Comparator<OmittedRunSample> comparator) {
this.label = label;
this.comparator = comparator;
}

public String getLabel() {
return label;
}

public Comparator<OmittedRunSample> comparator() {
return comparator;
}

protected static int getQcStatusSortPriority(OmittedRunSample sample) {
if (sample.getQcDate() == null) {
return 1;
} else if (sample.getDataReviewDate() == null) {
return 2;
} else if (DataUtils.isTopUpRequired(sample)) {
return 3;
} else if (Boolean.TRUE.equals(sample.getQcPassed())) {
return 4;
} else {
return 5;
}
}
}
5 changes: 5 additions & 0 deletions src/main/java/ca/on/oicr/gsi/dimsum/util/DataUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import ca.on.oicr.gsi.cardea.data.Case;
import ca.on.oicr.gsi.cardea.data.CaseDeliverable;
import ca.on.oicr.gsi.cardea.data.CaseRelease;
import ca.on.oicr.gsi.cardea.data.OmittedRunSample;
import ca.on.oicr.gsi.cardea.data.Sample;

public class DataUtils {
Expand Down Expand Up @@ -64,6 +65,10 @@ public static boolean isTopUpRequired(Sample sample) {
return TOP_UP_REASON.equals(sample.getQcReason());
}

public static boolean isTopUpRequired(OmittedRunSample sample) {
return TOP_UP_REASON.equals(sample.getQcReason());
}

public static boolean isAnalysisReviewSkipped(Case kase) {
return kase.getProjects().stream().allMatch(project -> project.isAnalysisReviewSkipped());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ <h1 class="text-green-200 font-sarabun font-light font text-32" th:text="${title
text-24 mt-8">Full Depth Sequencings</h2>
<div th:if="${showFullDepthSequencings}" id="fullDepthSequencingsTableContainer"></div>

<script src="/js/run.js"></script>
<h2 th:if="${showOmitted}" class="text-green-200 font-sarabun font-light font text-24 mt-8">
Omissions <i id="omissionsInfo" class="fa-solid fa-circle-question text-black"></i>
</h2>
<div th:if="${showOmitted}" id="omissionsTableContainer"></div>

<script src="/js/runDetails.js"></script>

</div>
</body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
import ca.on.oicr.gsi.cardea.data.MetricCategory;
import ca.on.oicr.gsi.cardea.data.MetricSubcategory;
import ca.on.oicr.gsi.cardea.data.Run;
import ca.on.oicr.gsi.cardea.data.RunAndLibraries;
import ca.on.oicr.gsi.cardea.data.Sample;
import ca.on.oicr.gsi.dimsum.data.IssueState;
import ca.on.oicr.gsi.dimsum.data.RunAndLibraries;

public class NotificationManagerTest {

Expand Down
Loading
Loading