Skip to content

Commit

Permalink
add clickhouse custom data endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
Bryan Lai committed Aug 28, 2024
1 parent b36bffb commit ef2ef4f
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,16 @@
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import jakarta.validation.Valid;
import org.cbioportal.model.AlterationCountByGene;
import org.cbioportal.model.AlterationFilter;
import org.cbioportal.model.CaseListDataCount;
import org.cbioportal.model.ClinicalData;
import org.cbioportal.model.ClinicalDataBin;
import org.cbioportal.model.ClinicalDataCountItem;
import org.cbioportal.model.ClinicalEventKeyCode;
import org.cbioportal.model.ClinicalEventTypeCount;
import org.cbioportal.model.ClinicalViolinPlotData;
import org.cbioportal.model.CopyNumberCountByGene;
import org.cbioportal.model.DensityPlotData;
import org.cbioportal.model.GenomicDataCount;
import org.cbioportal.model.PatientTreatmentReport;
import org.cbioportal.model.Sample;
import org.cbioportal.model.SampleTreatmentReport;
import org.cbioportal.service.ClinicalDataDensityPlotService;
import org.cbioportal.model.GenomicDataCountItem;
import org.cbioportal.service.StudyViewColumnarService;
import org.cbioportal.service.ViolinPlotService;
import org.apache.commons.collections4.CollectionUtils;
import org.cbioportal.model.*;
import org.cbioportal.service.*;
import org.cbioportal.service.exception.StudyNotFoundException;
import org.cbioportal.service.util.CustomDataSession;
import org.cbioportal.web.columnar.util.CustomDataFilterUtil;
import org.cbioportal.web.columnar.util.NewStudyViewFilterUtil;
import org.cbioportal.web.config.annotation.InternalApi;
import org.cbioportal.web.parameter.ClinicalDataBinCountFilter;
import org.cbioportal.web.parameter.ClinicalDataCountFilter;
import org.cbioportal.web.parameter.ClinicalDataFilter;
import org.cbioportal.web.parameter.DataBinMethod;
import org.cbioportal.web.parameter.GenomicDataCountFilter;
import org.cbioportal.web.parameter.GenomicDataFilter;
import org.cbioportal.web.parameter.MutationOption;
import org.cbioportal.web.parameter.Projection;
import org.cbioportal.web.parameter.StudyViewFilter;
import org.cbioportal.web.parameter.*;
import org.cbioportal.web.util.ClinicalDataBinUtil;
import org.cbioportal.web.util.DensityPlotParameters;
import org.cbioportal.web.util.StudyViewFilterUtil;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -56,10 +35,8 @@
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@InternalApi
Expand All @@ -72,20 +49,28 @@ public class StudyViewColumnStoreController {
private final ClinicalDataBinner clinicalDataBinner;
private final ClinicalDataDensityPlotService clinicalDataDensityPlotService;
private final ViolinPlotService violinPlotService;
private final CustomDataService customDataService;
private final PatientService patientService;

@Autowired
private StudyViewFilterUtil studyViewFilterUtil;
@Autowired
private ClinicalDataBinUtil clinicalDataBinUtil;

@Autowired
public StudyViewColumnStoreController(StudyViewColumnarService studyViewColumnarService,
ClinicalDataBinner clinicalDataBinner,
ClinicalDataDensityPlotService clinicalDataDensityPlotService,
ViolinPlotService violinPlotService
ViolinPlotService violinPlotService,
CustomDataService customDataService,
PatientService patientService
) {
this.studyViewColumnarService = studyViewColumnarService;
this.clinicalDataBinner = clinicalDataBinner;
this.clinicalDataDensityPlotService = clinicalDataDensityPlotService;
this.violinPlotService = violinPlotService;
this.customDataService = customDataService;
this.patientService = patientService;
}


Expand Down Expand Up @@ -491,37 +476,90 @@ public ResponseEntity<SampleTreatmentReport> fetchSampleTreatmentCounts(
return new ResponseEntity<>(studyViewColumnarService.getSampleTreatmentReport(interceptedStudyViewFilter),
HttpStatus.OK);
}

@PreAuthorize("hasPermission(#involvedCancerStudies, 'Collection<CancerStudyId>', T(org.cbioportal.utils.security.AccessLevel).READ)")
@RequestMapping(value = "/column-store/custom-data-counts/fetch", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(description = "Fetch custom data counts by study view filter")
@ApiResponse(responseCode = "200", description = "OK",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ClinicalDataCountItem.class))))
public ResponseEntity<List<ClinicalDataCountItem>> fetchCustomDataCounts(
@Parameter(required = true, description = "Custom data count filter") @Valid @RequestBody(required = false) ClinicalDataCountFilter clinicalDataCountFilter,
@Parameter(hidden = true) // prevent reference to this attribute in the swagger-ui
// interface
@RequestAttribute(required = false, value = "involvedCancerStudies") Collection<String> involvedCancerStudies,
@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 = "interceptedClinicalDataCountFilter") ClinicalDataCountFilter interceptedClinicalDataCountFilter) {

List<ClinicalDataFilter> attributes = interceptedClinicalDataCountFilter.getAttributes();
StudyViewFilter studyViewFilter = interceptedClinicalDataCountFilter.getStudyViewFilter();
if (attributes.size() == 1) {
NewStudyViewFilterUtil.removeSelfCustomDataFromFilter(attributes.get(0).getAttributeId(), studyViewFilter);
}

List <SampleIdentifier> filteredSampleIdentifiers = studyViewColumnarService.getFilteredSamples(studyViewFilter).stream().map(sample -> studyViewFilterUtil.buildSampleIdentifier(sample.getCancerStudyIdentifier(), sample.getStableId())).collect(Collectors.toList());

if (filteredSampleIdentifiers.isEmpty()) {
return new ResponseEntity<>(new ArrayList<>(), HttpStatus.OK);
}

final List<String> attributeIds = attributes.stream().map(ClinicalDataFilter::getAttributeId).collect(Collectors.toList());
Map<String, CustomDataSession> customDataSessionsMap = customDataService.getCustomDataSessions(attributeIds);

Map<String, SampleIdentifier> customSamplesMap = filteredSampleIdentifiers.stream()
.collect(Collectors.toMap(sampleIdentifier -> studyViewFilterUtil.getCaseUniqueKey(
sampleIdentifier.getStudyId(),
sampleIdentifier.getSampleId()
), Function.identity()));

List<String> studyIds = new ArrayList<>();
List<String> sampleIds = new ArrayList<>();
studyViewFilterUtil.extractStudyAndSampleIds(filteredSampleIdentifiers, studyIds, sampleIds);

long patientCustomDataSessionsCount = customDataSessionsMap.values().stream()
.filter(customDataSession -> customDataSession.getData().getPatientAttribute()).count();
List<Patient> patients = new ArrayList<>();
if (patientCustomDataSessionsCount > 0) {
patients.addAll(patientService.getPatientsOfSamples(studyIds, sampleIds));
}

List<ClinicalDataCountItem> result = studyViewFilterUtil.getClinicalDataCountsFromCustomData(customDataSessionsMap.values(),
customSamplesMap, patients);

return new ResponseEntity<>(result, HttpStatus.OK);
}

// @PreAuthorize("hasPermission(#involvedCancerStudies, 'Collection<CancerStudyId>', T(org.cbioportal.utils.security.AccessLevel).READ)")
// @RequestMapping(value = "/column-store/custom-data-bin-counts/fetch", method = RequestMethod.POST,
// consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
// @Operation(description = "Fetch custom data bin counts by study view filter")
// @ApiResponse(responseCode = "200", description = "OK",
// content = @Content(array = @ArraySchema(schema = @Schema(implementation = ClinicalDataBin.class))))
// public ResponseEntity<List<ClinicalDataBin>> fetchCustomDataBinCounts(
// @Parameter(description = "Method for data binning")
// @RequestParam(defaultValue = "DYNAMIC") DataBinMethod dataBinMethod,
// @Parameter(required = true, description = "Clinical data bin count filter")
// @Valid @RequestBody(required = false) ClinicalDataBinCountFilter clinicalDataBinCountFilter,
// @Parameter(hidden = true) // prevent reference to this attribute in the swagger-ui interface
// @RequestAttribute(required = false, value = "involvedCancerStudies") Collection<String> involvedCancerStudies,
// @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 = "interceptedClinicalDataBinCountFilter") ClinicalDataBinCountFilter interceptedClinicalDataBinCountFilter
// ) {
// // TODO code shared with ClinicalDataController.fetchCustomDataCounts
// List<ClinicalDataBinFilter> attributes = interceptedClinicalDataBinCountFilter.getAttributes();
// StudyViewFilter studyViewFilter = interceptedClinicalDataBinCountFilter.getStudyViewFilter();
// if (attributes.size() == 1) {
// NewStudyViewFilterUtil.removeSelfCustomDataFromFilter(attributes.get(0).getAttributeId(), studyViewFilter);
// }
// List<SampleIdentifier> filteredSampleIdentifiers = studyViewFilterApplier.apply(studyViewFilter);
//
// if (filteredSampleIdentifiers.isEmpty()) {
// return new ResponseEntity<>(new ArrayList<>(), HttpStatus.OK);
// }
//
// final List<ClinicalDataBin> clinicalDataBins = clinicalDataBinUtil.fetchCustomDataBinCounts(dataBinMethod, interceptedClinicalDataBinCountFilter, false);
//
// return new ResponseEntity<>(clinicalDataBins, HttpStatus.OK);
// }
@PreAuthorize("hasPermission(#involvedCancerStudies, 'Collection<CancerStudyId>', T(org.cbioportal.utils.security.AccessLevel).READ)")
@RequestMapping(value = "/column-store/custom-data-bin-counts/fetch", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(description = "Fetch custom data bin counts by study view filter")
@ApiResponse(responseCode = "200", description = "OK",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ClinicalDataBin.class))))
public ResponseEntity<List<ClinicalDataBin>> fetchCustomDataBinCounts(
@Parameter(description = "Method for data binning")
@RequestParam(defaultValue = "DYNAMIC") DataBinMethod dataBinMethod,
@Parameter(required = true, description = "Clinical data bin count filter")
@Valid @RequestBody(required = false) ClinicalDataBinCountFilter clinicalDataBinCountFilter,
@Parameter(hidden = true) // prevent reference to this attribute in the swagger-ui interface
@RequestAttribute(required = false, value = "involvedCancerStudies") Collection<String> involvedCancerStudies,
@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 = "interceptedClinicalDataBinCountFilter") ClinicalDataBinCountFilter interceptedClinicalDataBinCountFilter
) {
// TODO code shared with ClinicalDataController.fetchCustomDataCounts
List<ClinicalDataBinFilter> attributes = interceptedClinicalDataBinCountFilter.getAttributes();
StudyViewFilter studyViewFilter = interceptedClinicalDataBinCountFilter.getStudyViewFilter();
if (attributes.size() == 1) {
NewStudyViewFilterUtil.removeSelfCustomDataFromFilter(attributes.get(0).getAttributeId(), studyViewFilter);
}
List<SampleIdentifier> filteredSampleIdentifiers = studyViewColumnarService.getFilteredSamples(studyViewFilter).stream().map(sample -> studyViewFilterUtil.buildSampleIdentifier(sample.getCancerStudyIdentifier(), sample.getStableId())).collect(Collectors.toList());

if (filteredSampleIdentifiers.isEmpty()) {
return new ResponseEntity<>(new ArrayList<>(), HttpStatus.OK);
}

final List<ClinicalDataBin> clinicalDataBins = clinicalDataBinUtil.fetchCustomDataBinCounts(dataBinMethod, interceptedClinicalDataBinCountFilter, false);

return new ResponseEntity<>(clinicalDataBins, HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public List<SampleIdentifier> apply(StudyViewFilter studyViewFilter, boolean neg
if (!CollectionUtils.isEmpty(clinicalDataIntervalFilters)) {
sampleIdentifiers = intervalFilterClinicalData(sampleIdentifiers, clinicalDataIntervalFilters, negateFilters);
}

if (!CollectionUtils.isEmpty(studyViewFilter.getCustomDataFilters())) {
sampleIdentifiers = customDataFilterApplier.apply(sampleIdentifiers, studyViewFilter.getCustomDataFilters(),
negateFilters);
Expand Down
23 changes: 5 additions & 18 deletions src/main/java/org/cbioportal/web/util/StudyViewFilterUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,7 @@
import org.cbioportal.service.GeneService;
import org.cbioportal.service.util.CustomDataSession;
import org.cbioportal.service.util.CustomDataValue;
import org.cbioportal.web.parameter.ClinicalDataFilter;
import org.cbioportal.web.parameter.DataBinFilter;
import org.cbioportal.web.parameter.DataFilter;
import org.cbioportal.web.parameter.DataFilterValue;
import org.cbioportal.web.parameter.GeneIdType;
import org.cbioportal.web.parameter.GenericAssayDataBinFilter;
import org.cbioportal.web.parameter.GenericAssayDataFilter;
import org.cbioportal.web.parameter.GenomicDataBinFilter;
import org.cbioportal.web.parameter.GenomicDataFilter;
import org.cbioportal.web.parameter.MutationDataFilter;
import org.cbioportal.web.parameter.MutationOption;
import org.cbioportal.web.parameter.Projection;
import org.cbioportal.web.parameter.SampleIdentifier;
import org.cbioportal.web.parameter.StudyViewFilter;
import org.cbioportal.web.parameter.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

Expand All @@ -53,12 +40,12 @@ public StudyViewFilterUtil() {
geneService = null;
}

public void extractStudyAndSampleIds(
List<SampleIdentifier> sampleIdentifiers,
public <T extends SampleIdentifier> void extractStudyAndSampleIds(
List<T> sampleIdentifiers,
List<String> studyIds,
List<String> sampleIds
) {
for (SampleIdentifier sampleIdentifier : sampleIdentifiers) {
for (T sampleIdentifier : sampleIdentifiers) {
studyIds.add(sampleIdentifier.getStudyId());
sampleIds.add(sampleIdentifier.getSampleId());
}
Expand Down Expand Up @@ -210,7 +197,7 @@ public List<ClinicalDataCountItem> getClinicalDataCountsFromCustomData(Collectio
return clinicalDataCountItem;
}).collect(Collectors.toList());
}

public boolean isSingleStudyUnfiltered(StudyViewFilter filter) {
return filter.getStudyIds() != null &&
filter.getStudyIds().size() == 1 &&
Expand Down

0 comments on commit ef2ef4f

Please sign in to comment.