From d89cabd2774b0705baf6a2106359084aa58132d5 Mon Sep 17 00:00:00 2001 From: Onur Sumer Date: Mon, 28 Oct 2024 14:33:52 -0400 Subject: [PATCH] fetch missing patient clinical data for the density plot --- .../StudyViewColumnStoreController.java | 60 ++++++++----------- ...tUtil.java => ClinicalDataXyPlotUtil.java} | 36 ++++++++++- 2 files changed, 60 insertions(+), 36 deletions(-) rename src/main/java/org/cbioportal/web/columnar/util/{ClinicalDataViolinPlotUtil.java => ClinicalDataXyPlotUtil.java} (57%) diff --git a/src/main/java/org/cbioportal/web/columnar/StudyViewColumnStoreController.java b/src/main/java/org/cbioportal/web/columnar/StudyViewColumnStoreController.java index c2aa05fa076..32d32ce8a7b 100644 --- a/src/main/java/org/cbioportal/web/columnar/StudyViewColumnStoreController.java +++ b/src/main/java/org/cbioportal/web/columnar/StudyViewColumnStoreController.java @@ -68,15 +68,12 @@ import java.math.BigDecimal; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import java.util.stream.Stream; -import static org.cbioportal.web.columnar.util.ClinicalDataViolinPlotUtil.convertPatientClinicalDataToSampleClinicalData; -import static org.cbioportal.web.columnar.util.ClinicalDataViolinPlotUtil.filterNonEmptyClinicalData; +import static org.cbioportal.web.columnar.util.ClinicalDataXyPlotUtil.fetchClinicalDataForXyPlot; @InternalApi @RestController() @@ -291,9 +288,8 @@ public ResponseEntity fetchClinicalDataDensityPlot( @Parameter(hidden = true) // prevent reference to this attribute in the swagger-ui interface. this attribute is needed for the @PreAuthorize tag above. @Valid @RequestAttribute(required = false, value = "interceptedStudyViewFilter") StudyViewFilter interceptedStudyViewFilter, @Parameter(required = true, description = "Study view filter") - @RequestBody(required = false) StudyViewFilter studyViewFilter) { - - List xyAttributeId = new ArrayList<>(Arrays.asList(xAxisAttributeId, yAxisAttributeId)); + @RequestBody(required = false) StudyViewFilter studyViewFilter + ) { DensityPlotParameters densityPlotParameters = new DensityPlotParameters.Builder() .xAxisAttributeId(xAxisAttributeId) @@ -308,8 +304,18 @@ public ResponseEntity fetchClinicalDataDensityPlot( .yAxisLogScale(yAxisLogScale) .build(); - List sampleClinicalDataList = studyViewColumnarService.getSampleClinicalData(interceptedStudyViewFilter, xyAttributeId); - DensityPlotData result = clinicalDataDensityPlotService.getDensityPlotData(sampleClinicalDataList, densityPlotParameters, interceptedStudyViewFilter); + List combinedClinicalDataList = fetchClinicalDataForXyPlot( + studyViewColumnarService, + interceptedStudyViewFilter, + List.of(xAxisAttributeId, yAxisAttributeId), + false + ); + + DensityPlotData result = clinicalDataDensityPlotService.getDensityPlotData( + combinedClinicalDataList, + densityPlotParameters, + interceptedStudyViewFilter + ); return new ResponseEntity<>(result, HttpStatus.OK); } @@ -341,14 +347,17 @@ public ResponseEntity fetchClinicalDataViolinPlots( @Parameter(hidden = true) // prevent reference to this attribute in the swagger-ui interface. this attribute is needed for the @PreAuthorize tag above. @Valid @RequestAttribute(required = false, value = "interceptedStudyViewFilter") StudyViewFilter interceptedStudyViewFilter, @Parameter(required = true, description = "Study view filter") - @Valid @RequestBody(required = false) StudyViewFilter studyViewFilter) { - + @Valid @RequestBody(required = false) StudyViewFilter studyViewFilter + ) { // fetch the samples by using the provided study view filter List filteredSamples = studyViewColumnarService.getFilteredSamples(interceptedStudyViewFilter); // remove the numerical clinical data filter from the study view filter. // this new modified filter is used to fetch sample and patient clinical data. // this is required to get the complete violin plot data. + // filteredSamples reflects only the original unmodified study view filter. + // we will need to fetch samples again to get the samples corresponding to this modified filter, + // otherwise patient to sample mapping may be incomplete. if (interceptedStudyViewFilter.getClinicalDataFilters() != null) { interceptedStudyViewFilter.getClinicalDataFilters().stream() .filter(f->f.getAttributeId().equals(numericalAttributeId)) @@ -356,31 +365,12 @@ public ResponseEntity fetchClinicalDataViolinPlots( .ifPresent(f->interceptedStudyViewFilter.getClinicalDataFilters().remove(f)); } - List attributeIds = List.of(numericalAttributeId, categoricalAttributeId); - - // Filter out clinical data with empty attribute values due to Clickhouse migration - List sampleClinicalDataList = filterNonEmptyClinicalData( - studyViewColumnarService.getSampleClinicalData(interceptedStudyViewFilter, attributeIds) + List combinedClinicalDataList = fetchClinicalDataForXyPlot( + studyViewColumnarService, + interceptedStudyViewFilter, + List.of(numericalAttributeId, categoricalAttributeId), + true // filter out clinical data with empty attribute values due to Clickhouse migration ); - List patientClinicalDataList = filterNonEmptyClinicalData( - studyViewColumnarService.getPatientClinicalData(interceptedStudyViewFilter, attributeIds) - ); - - List combinedClinicalDataList; - if (patientClinicalDataList.isEmpty()) { - combinedClinicalDataList = sampleClinicalDataList; - } else { - // we previously fetched sample and patient clinical data with the modified study view filter, - // however filteredSamples reflects only the original unmodified study view filter. - // we need to fetch samples again to get the samples corresponding to this modified filter, - // otherwise patient to sample mapping may be incomplete. - List samplesWithoutNumericalFilter = studyViewColumnarService.getFilteredSamples(interceptedStudyViewFilter); - - combinedClinicalDataList = Stream.concat( - sampleClinicalDataList.stream(), - convertPatientClinicalDataToSampleClinicalData(patientClinicalDataList, samplesWithoutNumericalFilter).stream() - ).toList(); - } // Only mutation count can use log scale boolean useLogScale = logScale && numericalAttributeId.equals("MUTATION_COUNT"); diff --git a/src/main/java/org/cbioportal/web/columnar/util/ClinicalDataViolinPlotUtil.java b/src/main/java/org/cbioportal/web/columnar/util/ClinicalDataXyPlotUtil.java similarity index 57% rename from src/main/java/org/cbioportal/web/columnar/util/ClinicalDataViolinPlotUtil.java rename to src/main/java/org/cbioportal/web/columnar/util/ClinicalDataXyPlotUtil.java index 597845db2ef..aee3a04a485 100644 --- a/src/main/java/org/cbioportal/web/columnar/util/ClinicalDataViolinPlotUtil.java +++ b/src/main/java/org/cbioportal/web/columnar/util/ClinicalDataXyPlotUtil.java @@ -2,13 +2,47 @@ import org.cbioportal.model.ClinicalData; import org.cbioportal.model.Sample; +import org.cbioportal.service.StudyViewColumnarService; +import org.cbioportal.web.parameter.StudyViewFilter; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import java.util.stream.Stream; -public class ClinicalDataViolinPlotUtil { +public class ClinicalDataXyPlotUtil { + public static List fetchClinicalDataForXyPlot( + StudyViewColumnarService studyViewColumnarService, + StudyViewFilter studyViewFilter, + List attributeIds, + boolean shouldFilterNonEmptyClinicalData + ) { + List combinedClinicalDataList; + List sampleClinicalDataList = studyViewColumnarService.getSampleClinicalData(studyViewFilter, attributeIds); + List patientClinicalDataList = studyViewColumnarService.getPatientClinicalData(studyViewFilter, attributeIds); + + if (shouldFilterNonEmptyClinicalData) { + sampleClinicalDataList = filterNonEmptyClinicalData(sampleClinicalDataList); + patientClinicalDataList = filterNonEmptyClinicalData(patientClinicalDataList); + } + + if (patientClinicalDataList.isEmpty()) { + combinedClinicalDataList = sampleClinicalDataList; + } else { + // fetch samples for the given study view filter. + // we need this to construct the complete patient to sample map. + List samples = studyViewColumnarService.getFilteredSamples(studyViewFilter); + + combinedClinicalDataList = Stream.concat( + sampleClinicalDataList.stream(), + convertPatientClinicalDataToSampleClinicalData(patientClinicalDataList, samples).stream() + ).toList(); + } + + return combinedClinicalDataList; + } + public static List filterNonEmptyClinicalData(List clinicalData) { return clinicalData .stream()