Skip to content

Commit

Permalink
[GLT-4274] show omissions on the Run Details page
Browse files Browse the repository at this point in the history
  • Loading branch information
djcooke committed Nov 4, 2024
1 parent 1f6c190 commit d4294e7
Show file tree
Hide file tree
Showing 22 changed files with 442 additions and 46 deletions.
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

0 comments on commit d4294e7

Please sign in to comment.