diff --git a/src/main/java/org/cbioportal/model/SampleTreatment.java b/src/main/java/org/cbioportal/model/SampleTreatment.java new file mode 100644 index 00000000000..edf9ef7541a --- /dev/null +++ b/src/main/java/org/cbioportal/model/SampleTreatment.java @@ -0,0 +1,4 @@ +package org.cbioportal.model; + +public record SampleTreatment(String treatment, int preSampleCount, int postSampleCount) { +} diff --git a/src/main/java/org/cbioportal/model/SampleTreatmentReport.java b/src/main/java/org/cbioportal/model/SampleTreatmentReport.java new file mode 100644 index 00000000000..bc4bbcc479f --- /dev/null +++ b/src/main/java/org/cbioportal/model/SampleTreatmentReport.java @@ -0,0 +1,6 @@ +package org.cbioportal.model; + +import java.util.Collection; + +public record SampleTreatmentReport(int totalSampleCount, Collection treatments) { +} diff --git a/src/main/java/org/cbioportal/persistence/StudyViewRepository.java b/src/main/java/org/cbioportal/persistence/StudyViewRepository.java index 59deedce6d4..95dc1b6936d 100644 --- a/src/main/java/org/cbioportal/persistence/StudyViewRepository.java +++ b/src/main/java/org/cbioportal/persistence/StudyViewRepository.java @@ -9,8 +9,8 @@ import org.cbioportal.model.CopyNumberCountByGene; import org.cbioportal.model.GenomicDataCount; import org.cbioportal.model.PatientTreatment; -import org.cbioportal.model.PatientTreatmentReport; import org.cbioportal.model.Sample; +import org.cbioportal.model.SampleTreatment; import org.cbioportal.web.parameter.ClinicalDataType; import org.cbioportal.web.parameter.StudyViewFilter; @@ -53,6 +53,10 @@ public interface StudyViewRepository { List getClinicalEventTypeCounts(StudyViewFilter studyViewFilter); List getPatientTreatments(StudyViewFilter studyViewFilter); + + int getTotalPatientTreatmentCount(StudyViewFilter studyViewFilter); - PatientTreatmentReport getPatientTreatmentReport(StudyViewFilter studyViewFilter); + List getSampleTreatments(StudyViewFilter studyViewFilter); + + int getTotalSampleTreatmentCount(StudyViewFilter studyViewFilter); } diff --git a/src/main/java/org/cbioportal/persistence/model/SampleAcquisitionEventRecord.java b/src/main/java/org/cbioportal/persistence/model/SampleAcquisitionEventRecord.java new file mode 100644 index 00000000000..b02c24c7100 --- /dev/null +++ b/src/main/java/org/cbioportal/persistence/model/SampleAcquisitionEventRecord.java @@ -0,0 +1,4 @@ +package org.cbioportal.persistence.model; + +public record SampleAcquisitionEventRecord(String sampleId, String patientUniqueId, String cancerStudyId, int timeTaken) { +} diff --git a/src/main/java/org/cbioportal/persistence/model/TreatmentRecord.java b/src/main/java/org/cbioportal/persistence/model/TreatmentRecord.java new file mode 100644 index 00000000000..aeeac35723b --- /dev/null +++ b/src/main/java/org/cbioportal/persistence/model/TreatmentRecord.java @@ -0,0 +1,4 @@ +package org.cbioportal.persistence.model; + +public record TreatmentRecord(String patientUniqueId, String treatment, int startTime, int stopTime) { +} diff --git a/src/main/java/org/cbioportal/persistence/mybatisclickhouse/StudyViewMapper.java b/src/main/java/org/cbioportal/persistence/mybatisclickhouse/StudyViewMapper.java index bd9d4b602c3..4721b93809e 100644 --- a/src/main/java/org/cbioportal/persistence/mybatisclickhouse/StudyViewMapper.java +++ b/src/main/java/org/cbioportal/persistence/mybatisclickhouse/StudyViewMapper.java @@ -1,6 +1,5 @@ package org.cbioportal.persistence.mybatisclickhouse; -import org.apache.ibatis.annotations.MapKey; import org.cbioportal.model.AlterationCountByGene; import org.cbioportal.model.CaseListDataCount; import org.cbioportal.model.ClinicalAttribute; @@ -11,14 +10,13 @@ import org.cbioportal.model.GenePanelToGene; import org.cbioportal.model.GenomicDataCount; import org.cbioportal.model.PatientTreatment; -import org.cbioportal.model.PatientTreatmentReport; import org.cbioportal.model.Sample; +import org.cbioportal.model.SampleTreatment; import org.cbioportal.persistence.helper.AlterationFilterHelper; import org.cbioportal.web.parameter.CategorizedClinicalDataCountFilter; import org.cbioportal.web.parameter.StudyViewFilter; import java.util.List; -import java.util.Map; public interface StudyViewMapper { @@ -59,5 +57,7 @@ List getClinicalDataCounts(StudyViewFilter studyViewFilter, C List getClinicalEventTypeCounts(StudyViewFilter studyViewFilter, CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter, boolean applyPatientIdFilters); List getPatientTreatments(StudyViewFilter studyViewFilter, CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter, boolean applyPatientIdFilters); - PatientTreatmentReport getPatientTreatmentCounts(StudyViewFilter studyViewFilter, CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter, boolean applyPatientIdFilters); + int getPatientTreatmentCounts(StudyViewFilter studyViewFilter, CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter, boolean applyPatientIdFilters); + List getSampleTreatmentCounts(StudyViewFilter studyViewFilter, CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter, boolean applyPatientIdFilters); + int getTotalSampleTreatmentCounts(StudyViewFilter studyViewFilter, CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter, boolean applyPatientIdFilters); } diff --git a/src/main/java/org/cbioportal/persistence/mybatisclickhouse/StudyViewMyBatisRepository.java b/src/main/java/org/cbioportal/persistence/mybatisclickhouse/StudyViewMyBatisRepository.java index 7da568dabf2..3e7bced4540 100644 --- a/src/main/java/org/cbioportal/persistence/mybatisclickhouse/StudyViewMyBatisRepository.java +++ b/src/main/java/org/cbioportal/persistence/mybatisclickhouse/StudyViewMyBatisRepository.java @@ -9,8 +9,8 @@ import org.cbioportal.model.GenomicDataCount; import org.cbioportal.model.CopyNumberCountByGene; import org.cbioportal.model.PatientTreatment; -import org.cbioportal.model.PatientTreatmentReport; import org.cbioportal.model.Sample; +import org.cbioportal.model.SampleTreatment; import org.cbioportal.persistence.StudyViewRepository; import org.cbioportal.persistence.enums.ClinicalAttributeDataSource; import org.cbioportal.persistence.helper.AlterationFilterHelper; @@ -190,13 +190,24 @@ public List getPatientTreatments(StudyViewFilter studyViewFilt } @Override - public PatientTreatmentReport getPatientTreatmentReport(StudyViewFilter studyViewFilter) { + public int getTotalPatientTreatmentCount(StudyViewFilter studyViewFilter) { CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter = extractClinicalDataCountFilters(studyViewFilter); - var patientTreatmentCounts = mapper.getPatientTreatmentCounts(studyViewFilter, categorizedClinicalDataCountFilter, + return mapper.getPatientTreatmentCounts(studyViewFilter, categorizedClinicalDataCountFilter, + shouldApplyPatientIdFilters(studyViewFilter, categorizedClinicalDataCountFilter)); + } + + @Override + public List getSampleTreatments(StudyViewFilter studyViewFilter) { + CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter = extractClinicalDataCountFilters(studyViewFilter); + return mapper.getSampleTreatmentCounts(studyViewFilter, categorizedClinicalDataCountFilter, + shouldApplyPatientIdFilters(studyViewFilter, categorizedClinicalDataCountFilter)); + } + + @Override + public int getTotalSampleTreatmentCount(StudyViewFilter studyViewFilter) { + CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter = extractClinicalDataCountFilters(studyViewFilter); + return mapper.getTotalSampleTreatmentCounts(studyViewFilter, categorizedClinicalDataCountFilter, shouldApplyPatientIdFilters(studyViewFilter, categorizedClinicalDataCountFilter)); - var patientTreatments = mapper.getPatientTreatments(studyViewFilter, categorizedClinicalDataCountFilter, - shouldApplyPatientIdFilters(studyViewFilter, categorizedClinicalDataCountFilter)); - return new PatientTreatmentReport(patientTreatmentCounts.totalPatients(), patientTreatmentCounts.totalSamples(), patientTreatments); } private void buildClinicalAttributeNameMap() { diff --git a/src/main/java/org/cbioportal/service/StudyViewColumnarService.java b/src/main/java/org/cbioportal/service/StudyViewColumnarService.java index f332b586361..7a28b7faabc 100644 --- a/src/main/java/org/cbioportal/service/StudyViewColumnarService.java +++ b/src/main/java/org/cbioportal/service/StudyViewColumnarService.java @@ -9,6 +9,7 @@ import org.cbioportal.model.CopyNumberCountByGene; import org.cbioportal.model.PatientTreatmentReport; import org.cbioportal.model.Sample; +import org.cbioportal.model.SampleTreatmentReport; import org.cbioportal.web.parameter.ClinicalDataType; import org.cbioportal.web.parameter.StudyViewFilter; @@ -37,4 +38,5 @@ public interface StudyViewColumnarService { List getClinicalEventTypeCounts(StudyViewFilter studyViewFilter); PatientTreatmentReport getPatientTreatmentReport(StudyViewFilter studyViewFilter); + SampleTreatmentReport getSampleTreatmentReport(StudyViewFilter studyViewFilter); } diff --git a/src/main/java/org/cbioportal/service/impl/StudyViewColumnarServiceImpl.java b/src/main/java/org/cbioportal/service/impl/StudyViewColumnarServiceImpl.java index 31dc8f01ac0..94142c39b4f 100644 --- a/src/main/java/org/cbioportal/service/impl/StudyViewColumnarServiceImpl.java +++ b/src/main/java/org/cbioportal/service/impl/StudyViewColumnarServiceImpl.java @@ -10,9 +10,11 @@ import org.cbioportal.model.GenomicDataCount; import org.cbioportal.model.PatientTreatmentReport; import org.cbioportal.model.Sample; +import org.cbioportal.model.SampleTreatmentReport; import org.cbioportal.persistence.StudyViewRepository; import org.cbioportal.service.AlterationCountService; import org.cbioportal.service.StudyViewColumnarService; +import org.cbioportal.service.treatment.TreatmentCountReportService; import org.cbioportal.web.parameter.ClinicalDataType; import org.cbioportal.web.parameter.StudyViewFilter; import org.springframework.beans.factory.annotation.Autowired; @@ -30,11 +32,15 @@ public class StudyViewColumnarServiceImpl implements StudyViewColumnarService { private final StudyViewRepository studyViewRepository; private final AlterationCountService alterationCountService; + private final TreatmentCountReportService treatmentCountReportService; @Autowired - public StudyViewColumnarServiceImpl(StudyViewRepository studyViewRepository, AlterationCountService alterationCountService) { + public StudyViewColumnarServiceImpl(StudyViewRepository studyViewRepository, + AlterationCountService alterationCountService, + TreatmentCountReportService treatmentCountReportService) { this.studyViewRepository = studyViewRepository; this.alterationCountService = alterationCountService; + this.treatmentCountReportService = treatmentCountReportService; } @Cacheable(cacheResolver = "generalRepositoryCacheResolver", condition = "@cacheEnabledConfig.getEnabled()") @@ -60,7 +66,12 @@ public List getClinicalEventTypeCounts(StudyViewFilter s @Override public PatientTreatmentReport getPatientTreatmentReport(StudyViewFilter studyViewFilter) { - return studyViewRepository.getPatientTreatmentReport(studyViewFilter); + return treatmentCountReportService.getPatientTreatmentReport(studyViewFilter); + } + + @Override + public SampleTreatmentReport getSampleTreatmentReport(StudyViewFilter studyViewFilter) { + return treatmentCountReportService.getSampleTreatmentReport(studyViewFilter); } public List getCnaGenes(StudyViewFilter studyViewFilter) { diff --git a/src/main/java/org/cbioportal/service/treatment/TreatmentCountReportService.java b/src/main/java/org/cbioportal/service/treatment/TreatmentCountReportService.java new file mode 100644 index 00000000000..5c275fefc26 --- /dev/null +++ b/src/main/java/org/cbioportal/service/treatment/TreatmentCountReportService.java @@ -0,0 +1,10 @@ +package org.cbioportal.service.treatment; + +import org.cbioportal.model.PatientTreatmentReport; +import org.cbioportal.model.SampleTreatmentReport; +import org.cbioportal.web.parameter.StudyViewFilter; + +public interface TreatmentCountReportService { + PatientTreatmentReport getPatientTreatmentReport(StudyViewFilter studyViewFilter); + SampleTreatmentReport getSampleTreatmentReport(StudyViewFilter studyViewFilter); +} diff --git a/src/main/java/org/cbioportal/service/treatment/TreatmentCountReportServiceImpl.java b/src/main/java/org/cbioportal/service/treatment/TreatmentCountReportServiceImpl.java new file mode 100644 index 00000000000..7906f49f694 --- /dev/null +++ b/src/main/java/org/cbioportal/service/treatment/TreatmentCountReportServiceImpl.java @@ -0,0 +1,34 @@ +package org.cbioportal.service.treatment; + +import org.cbioportal.model.PatientTreatmentReport; +import org.cbioportal.model.SampleTreatmentReport; +import org.cbioportal.persistence.StudyViewRepository; +import org.cbioportal.web.parameter.StudyViewFilter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class TreatmentCountReportServiceImpl implements TreatmentCountReportService { + + private final StudyViewRepository studyViewRepository; + + @Autowired + public TreatmentCountReportServiceImpl(StudyViewRepository studyViewRepository) { + this.studyViewRepository = studyViewRepository; + } + + @Override + public PatientTreatmentReport getPatientTreatmentReport(StudyViewFilter studyViewFilter) { + var patientTreatments = studyViewRepository.getPatientTreatments(studyViewFilter); + var totalPatientTreatmentCount = studyViewRepository.getTotalPatientTreatmentCount(studyViewFilter); + return new PatientTreatmentReport(totalPatientTreatmentCount, 0, patientTreatments); + } + + @Override + public SampleTreatmentReport getSampleTreatmentReport(StudyViewFilter studyViewFilter) { + var sampleTreatments = studyViewRepository.getSampleTreatments(studyViewFilter); + var totalSampleTreatmentCount = studyViewRepository.getTotalSampleTreatmentCount(studyViewFilter); + return new SampleTreatmentReport(totalSampleTreatmentCount, sampleTreatments); + } + +} diff --git a/src/main/java/org/cbioportal/web/columnar/StudyViewColumnStoreController.java b/src/main/java/org/cbioportal/web/columnar/StudyViewColumnStoreController.java index 03a877060bc..3fb6d372e7a 100644 --- a/src/main/java/org/cbioportal/web/columnar/StudyViewColumnStoreController.java +++ b/src/main/java/org/cbioportal/web/columnar/StudyViewColumnStoreController.java @@ -23,6 +23,7 @@ import org.cbioportal.model.PatientTreatmentReport; import org.cbioportal.model.PatientTreatmentRow; import org.cbioportal.model.Sample; +import org.cbioportal.model.SampleTreatmentReport; import org.cbioportal.service.ClinicalDataDensityPlotService; import org.cbioportal.service.StudyViewColumnarService; import org.cbioportal.service.ViolinPlotService; @@ -393,4 +394,31 @@ public ResponseEntity fetchPatientTreatmentCounts( HttpStatus.OK); } + @PreAuthorize("hasPermission(#involvedCancerStudies, 'Collection', T(org.cbioportal.utils.security.AccessLevel).READ)") + @PostMapping(value = "/column-store/treatments/sample-counts/fetch", produces = MediaType.APPLICATION_JSON_VALUE) + @ApiResponse(responseCode = "200", description = "OK", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = PatientTreatmentRow.class)))) + public ResponseEntity getSampleTreatmentCounts( + @Parameter(required = false ) + @RequestParam(name = "tier", required = false, defaultValue = "Agent") + ClinicalEventKeyCode tier, + + @Parameter(required = true, description = "Study view filter") + @Valid + @RequestBody(required = false) + StudyViewFilter studyViewFilter, + + @Parameter(hidden = true) // prevent reference to this attribute in the swagger-ui interface + @RequestAttribute(required = false, value = "involvedCancerStudies") + Collection 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 = "interceptedStudyViewFilter") + StudyViewFilter interceptedStudyViewFilter + ) { + return new ResponseEntity<>(studyViewColumnarService.getSampleTreatmentReport(interceptedStudyViewFilter), + HttpStatus.OK); + } + } diff --git a/src/main/java/org/cbioportal/web/util/InvolvedCancerStudyExtractorInterceptor.java b/src/main/java/org/cbioportal/web/util/InvolvedCancerStudyExtractorInterceptor.java index 82c95a09517..e94ee950cd8 100644 --- a/src/main/java/org/cbioportal/web/util/InvolvedCancerStudyExtractorInterceptor.java +++ b/src/main/java/org/cbioportal/web/util/InvolvedCancerStudyExtractorInterceptor.java @@ -133,6 +133,7 @@ public class InvolvedCancerStudyExtractorInterceptor implements HandlerIntercept public static final String GENERIC_ASSAY_BINARY_ENRICHMENT_FETCH_PATH = "/generic-assay-binary-enrichments/fetch"; public static final String CLINICAL_EVENT_TYPE_COUNT_FETCH_PATH = "/clinical-event-type-counts/fetch"; public static final String TREATMENTS_PATIENT_COUNT_FETCH_PATH = "/treatments/patient-counts/fetch"; + public static final String TREATMENTS_SAMPLE_COUNT_FETCH_PATH = "/treatments/sample-counts/fetch"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if (!request.getMethod().equals("POST")) { @@ -178,7 +179,7 @@ public class InvolvedCancerStudyExtractorInterceptor implements HandlerIntercept STUDY_VIEW_FILTERED_SAMPLES, STUDY_VIEW_MUTATED_GENES, STUDY_VIEW_STRUCTURAL_VARIANT_GENES, STUDY_VIEW_STRUCTURAL_VARIANT_COUNTS, STUDY_VIEW_SAMPLE_COUNTS, STUDY_VIEW_SAMPLE_LIST_COUNTS_PATH, STUDY_VIEW_CLINICAL_TABLE_DATA_FETCH_PATH, TREATMENTS_PATIENT_PATH, TREATMENTS_SAMPLE_PATH, STUDY_VIEW_PROFILE_SAMPLE_COUNTS_PATH, CLINICAL_EVENT_TYPE_COUNT_FETCH_PATH, - TREATMENTS_PATIENT_COUNT_FETCH_PATH + TREATMENTS_PATIENT_COUNT_FETCH_PATH, TREATMENTS_SAMPLE_COUNT_FETCH_PATH ).contains(requestPathInfo)) { return extractAttributesFromStudyViewFilter(request); } else if (requestPathInfo.equals(CLINICAL_DATA_ENRICHMENT_FETCH_PATH)) { diff --git a/src/main/resources/org/cbioportal/persistence/mybatisclickhouse/StudyViewFilterMapper.xml b/src/main/resources/org/cbioportal/persistence/mybatisclickhouse/StudyViewFilterMapper.xml index 2c2a7cd1dc4..872a6462b01 100644 --- a/src/main/resources/org/cbioportal/persistence/mybatisclickhouse/StudyViewFilterMapper.xml +++ b/src/main/resources/org/cbioportal/persistence/mybatisclickhouse/StudyViewFilterMapper.xml @@ -106,9 +106,57 @@ + + + + - + + + + SELECT concat(ced.cancer_study_identifier, '_', ced.sample_id) AS sample_unique_id + FROM ( + + SELECT + ced.value AS sample_id, + ced.patient_unique_id AS patient_unique_id, + min(ced.start_date) AS time_taken, + ced.cancer_study_identifier AS cancer_study_identifier + FROM clinical_event_derived ced + + key = 'SAMPLE_ID' + AND (event_type LIKE 'Sample Acquisition' OR event_type LIKE 'SPECIMEN') + + GROUP BY patient_unique_id, ced.value, cancer_study_identifier + ) ced + INNER JOIN ( + + SELECT + patient_unique_id, + value AS treatment, + argMin(start_date, start_date) AS treatment_time_taken + FROM clinical_event_derived + WHERE event_type = 'Treatment' + AND key = 'AGENT' + GROUP BY patient_unique_id, value + ) ced_inner ON ced_inner.patient_unique_id = ced.patient_unique_id + + + ced_inner.treatment = '${sampleTreatmentFilter.treatment}' + + + AND ced.time_taken <= ced_inner.treatment_time_taken + + + AND ced.time_taken > ced_inner.treatment_time_taken + + + + + GROUP BY patient_unique_id, ced.sample_id, ced.time_taken, ced.cancer_study_identifier, ced_inner.treatment, ced_inner.treatment_time_taken + + diff --git a/src/main/resources/org/cbioportal/persistence/mybatisclickhouse/StudyViewMapper.xml b/src/main/resources/org/cbioportal/persistence/mybatisclickhouse/StudyViewMapper.xml index fcfce0e9439..0c02d63fadc 100644 --- a/src/main/resources/org/cbioportal/persistence/mybatisclickhouse/StudyViewMapper.xml +++ b/src/main/resources/org/cbioportal/persistence/mybatisclickhouse/StudyViewMapper.xml @@ -331,16 +331,9 @@ GROUP BY event_type; - - - - - - - SELECT - count(DISTINCT patient_unique_id) AS totalPatients, - count(DISTINCT sample_unique_id) AS totalSamples + count(DISTINCT patient_unique_id) AS totalPatients FROM sample_derived patient_unique_id IN ( @@ -379,7 +372,89 @@ GROUP BY value; - + + + + + + + + + + + + +