Skip to content

Commit

Permalink
Wrap minimum amount of services to see study view working
Browse files Browse the repository at this point in the history
  • Loading branch information
forus committed Jan 22, 2025
1 parent 31c3842 commit a90c41c
Show file tree
Hide file tree
Showing 15 changed files with 1,044 additions and 296 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import java.util.function.Function;
import java.util.stream.Collectors;
import org.cbioportal.model.CancerStudy;
import org.cbioportal.model.EntityType;
import org.cbioportal.model.MolecularProfile;
import org.cbioportal.model.SampleList;
import org.cbioportal.persistence.GenericAssayRepository;
Expand All @@ -15,9 +14,6 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
Expand Down Expand Up @@ -65,14 +61,15 @@ public Map<String, SampleList> buildSampleListMap() {
}

public Map<String, CancerStudy> buildCancerStudyMap() {
Map<String, CancerStudy> cancerStudyMap = studyRepository.getAllStudies(
null,
"SUMMARY",
REPOSITORY_RESULT_LIMIT,
REPOSITORY_RESULT_OFFSET,
null,
"ASC").stream()
.collect(Collectors.toMap(CancerStudy::getCancerStudyIdentifier, Function.identity()));
Map<String, CancerStudy> cancerStudyMap =
studyRepository.getAllStudies(
null,
"SUMMARY",
REPOSITORY_RESULT_LIMIT,
REPOSITORY_RESULT_OFFSET,
null,
"ASC").stream()
.collect(Collectors.toMap(CancerStudy::getCancerStudyIdentifier, Function.identity()));
LOG.debug(" cancer study map size: " + cancerStudyMap.size());
return cancerStudyMap;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package org.cbioportal.service.impl.vs;

import org.cbioportal.model.CancerStudy;
import org.cbioportal.model.MolecularProfile;
import org.cbioportal.model.TypeOfCancer;
import org.cbioportal.service.CancerTypeService;
import org.cbioportal.service.exception.CancerTypeNotFoundException;
import org.cbioportal.service.util.SessionServiceRequestHandler;
import org.cbioportal.web.parameter.VirtualStudy;
import org.cbioportal.web.parameter.VirtualStudyData;
import org.cbioportal.web.parameter.VirtualStudySamples;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

//TODO implement caching here?
@Service
public class PublishedVirtualStudyService {

public static final String ALL_USERS = "*";
private static final Logger LOG = LoggerFactory.getLogger(PublishedVirtualStudyService.class);
private final CancerTypeService cancerTypeService;
private final SessionServiceRequestHandler sessionServiceRequestHandler;
private final TypeOfCancer defaultCancerType;

public PublishedVirtualStudyService(SessionServiceRequestHandler sessionServiceRequestHandler,
CancerTypeService cancerTypeService,
@Value("${default_type_of_cancer_id:mixed}") String defaultTypeOfCancerId) throws CancerTypeNotFoundException {
this.sessionServiceRequestHandler = sessionServiceRequestHandler;
this.cancerTypeService = cancerTypeService;
this.defaultCancerType = cancerTypeService.getCancerType(defaultTypeOfCancerId);
}

public List<VirtualStudy> getAllPublishedVirtualStudies() {
return sessionServiceRequestHandler.getVirtualStudiesAccessibleToUser(ALL_USERS);
}

public List<VirtualStudy> getPublishedVirtualStudiesByKeyword(String keyword) {
return getAllPublishedVirtualStudies().stream()
.filter(cs -> shouldSelect(cs, keyword)).toList();
}

public Optional<VirtualStudy> getPublishedVirtualStudyById(String id) {
VirtualStudy virtualStudyById = sessionServiceRequestHandler.getVirtualStudyById(id);
boolean notPublishedVs = virtualStudyById != null && !virtualStudyById.getData().getUsers().contains(ALL_USERS);
if (notPublishedVs) {
return Optional.empty();
}
return Optional.ofNullable(virtualStudyById);
}

public List<MolecularProfile> getAllVirtualMolecularProfiles(List<MolecularProfile> molecularProfiles) {
Map<String, List<MolecularProfile>> molecularProfilesByStudyId = molecularProfiles.stream().collect(Collectors.groupingBy(MolecularProfile::getCancerStudyIdentifier));
return getAllPublishedVirtualStudies().stream().flatMap(virtualStudy -> {
List<String> studyIds = virtualStudy.getData().getStudies().stream().map(VirtualStudySamples::getId).toList();
//TODO can we check if any of samples in the virtual study is in the molecular profile?
return studyIds.stream().flatMap(studyId ->
molecularProfilesByStudyId.get(studyId).stream().map(molecularProfile -> {
MolecularProfile virtualMolecularProfile = new MolecularProfile();
virtualMolecularProfile.setMolecularAlterationType(molecularProfile.getMolecularAlterationType());
virtualMolecularProfile.setCancerStudyIdentifier(virtualStudy.getId());
virtualMolecularProfile.setDatatype(molecularProfile.getDatatype());
virtualMolecularProfile.setStableId(molecularProfile.getStableId().replace(molecularProfile.getCancerStudyIdentifier(), virtualStudy.getId()));
virtualMolecularProfile.setName(molecularProfile.getName());
virtualMolecularProfile.setDescription(molecularProfile.getDescription());
virtualMolecularProfile.setCancerStudy(toCancerStudy(virtualStudy));
virtualMolecularProfile.setPatientLevel(molecularProfile.getPatientLevel());
virtualMolecularProfile.setGenericAssayType(molecularProfile.getGenericAssayType());
virtualMolecularProfile.setPivotThreshold(molecularProfile.getPivotThreshold());
virtualMolecularProfile.setShowProfileInAnalysisTab(molecularProfile.getShowProfileInAnalysisTab());
virtualMolecularProfile.setSortOrder(molecularProfile.getSortOrder());
return virtualMolecularProfile;
//TODO check if molecular profile can be merged
}).collect(Collectors.groupingBy(MolecularProfile::getStableId)).values().stream().map(List::getFirst)
);
}).toList();
}

public CancerStudy toCancerStudy(VirtualStudy vs) {
VirtualStudyData vsd = vs.getData();
CancerStudy cs = new CancerStudy();
cs.setCancerStudyIdentifier(vs.getId());
cs.setName(vsd.getName());
try {
cs.setTypeOfCancer(vsd.getTypeOfCancerId() == null ? this.defaultCancerType : cancerTypeService.getCancerType(vsd.getTypeOfCancerId()));
cs.setTypeOfCancerId(vsd.getTypeOfCancerId() == null ? this.defaultCancerType.getTypeOfCancerId() : vsd.getTypeOfCancerId());
} catch (CancerTypeNotFoundException e) {
throw new RuntimeException(e);
}
cs.setDescription(vsd.getDescription());
cs.setPmid(vsd.getPmid());
//TODO run filters on the dynamic virtual study
cs.setAllSampleCount(vsd.getStudies().stream().map(s -> s.getSamples().size()).reduce(0, Integer::sum));
//TODO we can implement this field for published virtual studies to predefine rights on groups even before the study is created
cs.setGroups("PUBLIC");
return cs;
}

private static boolean shouldSelect(VirtualStudy vs, String keyword) {
//TODO improve the search. The keyword can be also sent to mongo to search for virtual studies
if (keyword == null) {
return true;
}
return vs.getData().getName().toLowerCase().contains(keyword.toLowerCase());
}

/**
* Publishes virtual study optionally updating metadata fields
* @param id - id of public virtual study to publish
* @param typeOfCancerId - if specified (not null) update type of cancer of published virtual study
* @param pmid - if specified (not null) update PubMed ID of published virtual study
*/
public void publishVirtualStudy(String id, String typeOfCancerId, String pmid) {
VirtualStudy virtualStudyDataToPublish = sessionServiceRequestHandler.getVirtualStudyById(id);
VirtualStudyData virtualStudyData = virtualStudyDataToPublish.getData();
updateStudyMetadataFieldsIfSpecified(virtualStudyData, typeOfCancerId, pmid);
virtualStudyData.setUsers(Set.of(ALL_USERS));
sessionServiceRequestHandler.updateVirtualStudy(virtualStudyDataToPublish);
}

/**
* Un-publish virtual study
* @param id - id of public virtual study to un-publish
*/
public void unPublishVirtualStudy(String id) {
VirtualStudy virtualStudyToUnPublish = sessionServiceRequestHandler.getVirtualStudyById(id);
if (virtualStudyToUnPublish == null) {
throw new NoSuchElementException("The virtual study with id=" + id + " has not been found in the public list.");
}
VirtualStudyData virtualStudyData = virtualStudyToUnPublish.getData();
Set<String> users = virtualStudyData.getUsers();
if (users == null || users.isEmpty() || !users.contains(ALL_USERS)) {
throw new NoSuchElementException("The virtual study with id=" + id + " has not been found in the public list.");
}
virtualStudyData.setUsers(Set.of(virtualStudyData.getOwner()));
sessionServiceRequestHandler.updateVirtualStudy(virtualStudyToUnPublish);
}

private void updateStudyMetadataFieldsIfSpecified(VirtualStudyData virtualStudyData, String typeOfCancerId, String pmid) {
if (typeOfCancerId != null) {
try {
cancerTypeService.getCancerType(typeOfCancerId);
virtualStudyData.setTypeOfCancerId(typeOfCancerId);
} catch (CancerTypeNotFoundException e) {
LOG.error("No cancer type with id={} were found.", typeOfCancerId);
throw new IllegalArgumentException( "The cancer type is not valid: " + typeOfCancerId);
}
}
if (pmid != null) {
virtualStudyData.setPmid(pmid);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.cbioportal.service.impl.vs;

import org.apache.commons.math3.util.Pair;
import org.cbioportal.model.*;
import org.cbioportal.service.StudyViewService;
import org.cbioportal.service.exception.StudyNotFoundException;
import org.cbioportal.web.parameter.VirtualStudy;
import org.cbioportal.web.parameter.VirtualStudySamples;

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class VMAwareStudyViewService implements StudyViewService {

private final StudyViewService studyViewService;
private final PublishedVirtualStudyService publishedVirtualStudyService;

public VMAwareStudyViewService(StudyViewService studyViewService, PublishedVirtualStudyService publishedVirtualStudyService) {
this.studyViewService = studyViewService;
this.publishedVirtualStudyService = publishedVirtualStudyService;
}

@Override
public List<GenomicDataCount> getGenomicDataCounts(List<String> studyIds, List<String> sampleIds) {
List<String> harmonisedStudyIds = getMaterialisedStudyIds(studyIds, sampleIds);
return studyViewService.getGenomicDataCounts(harmonisedStudyIds, sampleIds);
}

private List<String> getMaterialisedStudyIds(List<String> studyIds, List<String> sampleIds) {
Map<String, VirtualStudy> virtualStudiesById = publishedVirtualStudyService.getAllPublishedVirtualStudies().stream().collect(Collectors.toMap(VirtualStudy::getId, Function.identity()));
return IntStream.range(0, sampleIds.size())
.mapToObj(pos -> {
String studyId = studyIds.get(pos);
if (virtualStudiesById.containsKey(studyId)) {
String sampleId = sampleIds.get(pos);
VirtualStudy virtualStudy = virtualStudiesById.get(studyId);
return virtualStudy.getData().getStudies().stream()
.filter(virtualStudySamples -> virtualStudySamples.getSamples().contains(sampleId))
.findFirst()
.map(VirtualStudySamples::getId).orElse(studyId);
}
return studyId;
}).toList();
}

@Override
public List<AlterationCountByGene> getMutationAlterationCountByGenes(List<String> studyIds, List<String> sampleIds, AlterationFilter annotationFilter) throws StudyNotFoundException {
return studyViewService.getMutationAlterationCountByGenes(getMaterialisedStudyIds(studyIds, sampleIds), sampleIds, annotationFilter);
}

@Override
public List<GenomicDataCountItem> getMutationCountsByGeneSpecific(List<String> studyIds, List<String> sampleIds, List<Pair<String, String>> genomicDataFilters, AlterationFilter annotationFilter) {
return studyViewService.getMutationTypeCountsByGeneSpecific(getMaterialisedStudyIds(studyIds, sampleIds), sampleIds, genomicDataFilters);
}

@Override
public List<GenomicDataCountItem> getMutationTypeCountsByGeneSpecific(List<String> studyIds, List<String> sampleIds, List<Pair<String, String>> genomicDataFilters) {
return studyViewService.getMutationTypeCountsByGeneSpecific(getMaterialisedStudyIds(studyIds, sampleIds), sampleIds, genomicDataFilters);
}

@Override
public List<AlterationCountByGene> getStructuralVariantAlterationCountByGenes(List<String> studyIds, List<String> sampleIds, AlterationFilter annotationFilter) throws StudyNotFoundException {
return studyViewService.getStructuralVariantAlterationCountByGenes(getMaterialisedStudyIds(studyIds, sampleIds), sampleIds, annotationFilter);
}

@Override
public List<AlterationCountByStructuralVariant> getStructuralVariantAlterationCounts(List<String> studyIds, List<String> sampleIds, AlterationFilter annotationFilters) {
return studyViewService.getStructuralVariantAlterationCounts(getMaterialisedStudyIds(studyIds, sampleIds), sampleIds, annotationFilters);
}

@Override
public List<CopyNumberCountByGene> getCNAAlterationCountByGenes(List<String> studyIds, List<String> sampleIds, AlterationFilter annotationFilter) throws StudyNotFoundException {
return studyViewService.getCNAAlterationCountByGenes(getMaterialisedStudyIds(studyIds, sampleIds), sampleIds, annotationFilter);
}

@Override
public List<GenomicDataCountItem> getCNAAlterationCountsByGeneSpecific(List<String> studyIds, List<String> sampleIds, List<Pair<String, String>> genomicDataFilters) {
return studyViewService.getCNAAlterationCountsByGeneSpecific(getMaterialisedStudyIds(studyIds, sampleIds), sampleIds, genomicDataFilters);
}

@Override
public List<GenericAssayDataCountItem> fetchGenericAssayDataCounts(List<String> sampleIds, List<String> studyIds, List<String> stableIds, List<String> profileTypes) {
return studyViewService.fetchGenericAssayDataCounts(sampleIds, getMaterialisedStudyIds(studyIds, sampleIds), stableIds, profileTypes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.cbioportal.service.impl.vs;

import org.cbioportal.model.CancerStudy;
import org.cbioportal.model.MolecularProfile;
import org.cbioportal.model.SampleList;
import org.cbioportal.persistence.cachemaputil.CacheMapUtil;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

//TODO enable cache for this class. Think on how to evict it when a new virtual study is created
public class VSAwareCacheMapUtil implements CacheMapUtil {
private final CacheMapUtil cacheMapUtil;
private final PublishedVirtualStudyService publishedVirtualStudyService;

public VSAwareCacheMapUtil(CacheMapUtil cacheMapUtil, PublishedVirtualStudyService publishedVirtualStudyService) {
this.cacheMapUtil = cacheMapUtil;
this.publishedVirtualStudyService = publishedVirtualStudyService;
}

@Override
public Map<String, MolecularProfile> getMolecularProfileMap() {
List<MolecularProfile> molecularProfiles = cacheMapUtil.getMolecularProfileMap().values().stream().toList();
return Stream.concat(
publishedVirtualStudyService.getAllVirtualMolecularProfiles(molecularProfiles).stream(),
molecularProfiles.stream())
.collect(Collectors.toMap(MolecularProfile::getStableId, Function.identity()));
}

@Override
public Map<String, SampleList> getSampleListMap() {
return cacheMapUtil.getSampleListMap();
}

@Override
public Map<String, CancerStudy> getCancerStudyMap() {
return Stream.concat(
publishedVirtualStudyService.getAllPublishedVirtualStudies().stream().map(publishedVirtualStudyService::toCancerStudy),
cacheMapUtil.getCancerStudyMap().values().stream())
.collect(Collectors.toMap(CancerStudy::getCancerStudyIdentifier, Function.identity()));
}

@Override
public boolean hasCacheEnabled() {
return true;
}
}
Loading

0 comments on commit a90c41c

Please sign in to comment.