Skip to content

Commit

Permalink
Rfc80/sample treatment endpoint (#10927)
Browse files Browse the repository at this point in the history
* Add Sample Count Report Endpoint

* Push counting logic to sql

* Remove unused methods

* Update PatientTreatments to pass int from db for totalPatientTreatmentCount

* Fix tests

* Add unit test for sample Treatments

* Remove unused import

* Fix indentation
  • Loading branch information
haynescd authored Aug 5, 2024
1 parent e935c41 commit bf71f1b
Show file tree
Hide file tree
Showing 17 changed files with 322 additions and 37 deletions.
4 changes: 4 additions & 0 deletions src/main/java/org/cbioportal/model/SampleTreatment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.cbioportal.model;

public record SampleTreatment(String treatment, int preSampleCount, int postSampleCount) {
}
6 changes: 6 additions & 0 deletions src/main/java/org/cbioportal/model/SampleTreatmentReport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.cbioportal.model;

import java.util.Collection;

public record SampleTreatmentReport(int totalSampleCount, Collection<SampleTreatment> treatments) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -53,6 +53,10 @@ public interface StudyViewRepository {
List<ClinicalEventTypeCount> getClinicalEventTypeCounts(StudyViewFilter studyViewFilter);

List<PatientTreatment> getPatientTreatments(StudyViewFilter studyViewFilter);

int getTotalPatientTreatmentCount(StudyViewFilter studyViewFilter);

PatientTreatmentReport getPatientTreatmentReport(StudyViewFilter studyViewFilter);
List<SampleTreatment> getSampleTreatments(StudyViewFilter studyViewFilter);

int getTotalSampleTreatmentCount(StudyViewFilter studyViewFilter);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.cbioportal.persistence.model;

public record SampleAcquisitionEventRecord(String sampleId, String patientUniqueId, String cancerStudyId, int timeTaken) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.cbioportal.persistence.model;

public record TreatmentRecord(String patientUniqueId, String treatment, int startTime, int stopTime) {
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -59,5 +57,7 @@ List<ClinicalDataCount> getClinicalDataCounts(StudyViewFilter studyViewFilter, C
List<ClinicalEventTypeCount> getClinicalEventTypeCounts(StudyViewFilter studyViewFilter, CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter, boolean applyPatientIdFilters);

List<PatientTreatment> getPatientTreatments(StudyViewFilter studyViewFilter, CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter, boolean applyPatientIdFilters);
PatientTreatmentReport getPatientTreatmentCounts(StudyViewFilter studyViewFilter, CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter, boolean applyPatientIdFilters);
int getPatientTreatmentCounts(StudyViewFilter studyViewFilter, CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter, boolean applyPatientIdFilters);
List<SampleTreatment> getSampleTreatmentCounts(StudyViewFilter studyViewFilter, CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter, boolean applyPatientIdFilters);
int getTotalSampleTreatmentCounts(StudyViewFilter studyViewFilter, CategorizedClinicalDataCountFilter categorizedClinicalDataCountFilter, boolean applyPatientIdFilters);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -190,13 +190,24 @@ public List<PatientTreatment> 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<SampleTreatment> 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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -37,4 +38,5 @@ public interface StudyViewColumnarService {

List<ClinicalEventTypeCount> getClinicalEventTypeCounts(StudyViewFilter studyViewFilter);
PatientTreatmentReport getPatientTreatmentReport(StudyViewFilter studyViewFilter);
SampleTreatmentReport getSampleTreatmentReport(StudyViewFilter studyViewFilter);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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()")
Expand All @@ -60,7 +66,12 @@ public List<ClinicalEventTypeCount> 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<CopyNumberCountByGene> getCnaGenes(StudyViewFilter studyViewFilter) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -393,4 +394,31 @@ public ResponseEntity<PatientTreatmentReport> fetchPatientTreatmentCounts(
HttpStatus.OK);
}

@PreAuthorize("hasPermission(#involvedCancerStudies, 'Collection<CancerStudyId>', 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<SampleTreatmentReport> 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<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 = "interceptedStudyViewFilter")
StudyViewFilter interceptedStudyViewFilter
) {
return new ResponseEntity<>(studyViewColumnarService.getSampleTreatmentReport(interceptedStudyViewFilter),
HttpStatus.OK);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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")) {
Expand Down Expand Up @@ -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)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,57 @@
</foreach>
</if>
<!-- ... extend for other elements of the StudyViewFilter object -->
<!-- Apply Sample Treatment Filter -->
<if test="studyViewFilter.sampleTreatmentFilters != null and !studyViewFilter.sampleTreatmentFilters.getFilters().isEmpty()">
<include refid="applySampleTreatmentFilter"/>
</if>
</trim>
</sql>


<sql id="applySampleTreatmentFilter">
<foreach item="andedSampleTreatmentFilters" collection="studyViewFilter.sampleTreatmentFilters.getFilters()" open="INTERSECT" separator="INTERSECT">
SELECT concat(ced.cancer_study_identifier, '_', ced.sample_id) AS sample_unique_id
FROM (
<!-- Nested sub query to grab minimum sample acquisition event -->
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
<where>
key = 'SAMPLE_ID'
AND (event_type LIKE 'Sample Acquisition' OR event_type LIKE 'SPECIMEN')
</where>
GROUP BY patient_unique_id, ced.value, cancer_study_identifier
) ced
INNER JOIN (
<!-- Nested sub query to grab all treatments group by patients-->
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
<where>
<foreach item="sampleTreatmentFilter" collection="andedSampleTreatmentFilters.getFilters()" open="AND ((" separator=") OR (" close="))">
ced_inner.treatment = '${sampleTreatmentFilter.treatment}'
<choose>
<when test="sampleTreatmentFilter.time.name() == 'Pre'">
AND ced.time_taken &lt;= ced_inner.treatment_time_taken
</when>
<otherwise>
AND ced.time_taken &gt; ced_inner.treatment_time_taken
</otherwise>
</choose>
</foreach>
</where>
GROUP BY patient_unique_id, ced.sample_id, ced.time_taken, ced.cancer_study_identifier, ced_inner.treatment, ced_inner.treatment_time_taken
</foreach>
</sql>
<sql id="applyClinicalEventTypeFilter">
<foreach item="clinicalEventFilter" collection="studyViewFilter.getClinicalEventFilters()" open="INTERSECT"
separator="INTERSECT">
Expand Down
Loading

0 comments on commit bf71f1b

Please sign in to comment.