From 68da1cf64366516abc9c3551b92b32a0f6da65bf Mon Sep 17 00:00:00 2001 From: Calvin Lu <59149377+calvinlu3@users.noreply.github.com> Date: Wed, 20 Dec 2023 10:50:45 -0500 Subject: [PATCH] Add annotation search endpoint --- .../model/AnnotationSearchQueryType.java | 7 + .../oncokb/model/AnnotationSearchResult.java | 24 ++ .../cbio/oncokb/util/AlterationUtils.java | 15 +- .../mskcc/cbio/oncokb/util/EvidenceUtils.java | 5 +- .../cbio/oncokb/util/IndicatorUtils.java | 85 +++-- .../mskcc/cbio/oncokb/util/LevelUtils.java | 12 + .../org/mskcc/cbio/oncokb/util/MainUtils.java | 37 ++ .../cbio/oncokb/api/pub/v1/SearchApi.java | 2 - .../api/pub/v1/VariantsApiController.java | 5 +- .../api/pvt/PrivateAnnotationController.java | 353 ++++++++++++++++++ .../api/pvt/PrivateSearchApiController.java | 27 +- 11 files changed, 501 insertions(+), 71 deletions(-) create mode 100644 core/src/main/java/org/mskcc/cbio/oncokb/model/AnnotationSearchQueryType.java create mode 100644 core/src/main/java/org/mskcc/cbio/oncokb/model/AnnotationSearchResult.java create mode 100644 web/src/main/java/org/mskcc/cbio/oncokb/api/pvt/PrivateAnnotationController.java diff --git a/core/src/main/java/org/mskcc/cbio/oncokb/model/AnnotationSearchQueryType.java b/core/src/main/java/org/mskcc/cbio/oncokb/model/AnnotationSearchQueryType.java new file mode 100644 index 000000000..55920cfb3 --- /dev/null +++ b/core/src/main/java/org/mskcc/cbio/oncokb/model/AnnotationSearchQueryType.java @@ -0,0 +1,7 @@ +package org.mskcc.cbio.oncokb.model; + + +public enum AnnotationSearchQueryType { + GENE, VARIANT, DRUG, CANCER_TYPE +} + diff --git a/core/src/main/java/org/mskcc/cbio/oncokb/model/AnnotationSearchResult.java b/core/src/main/java/org/mskcc/cbio/oncokb/model/AnnotationSearchResult.java new file mode 100644 index 000000000..8ce5ca449 --- /dev/null +++ b/core/src/main/java/org/mskcc/cbio/oncokb/model/AnnotationSearchResult.java @@ -0,0 +1,24 @@ +package org.mskcc.cbio.oncokb.model; + +public class AnnotationSearchResult { + AnnotationSearchQueryType queryType; + IndicatorQueryResp indicatorQueryResp; + + + public AnnotationSearchQueryType getQueryType() { + return this.queryType; + } + + public void setQueryType(AnnotationSearchQueryType queryType) { + this.queryType = queryType; + } + + public IndicatorQueryResp getIndicatorQueryResp() { + return this.indicatorQueryResp; + } + + public void setIndicatorQueryResp(IndicatorQueryResp indicatorQueryResp) { + this.indicatorQueryResp = indicatorQueryResp; + } + +} diff --git a/core/src/main/java/org/mskcc/cbio/oncokb/util/AlterationUtils.java b/core/src/main/java/org/mskcc/cbio/oncokb/util/AlterationUtils.java index 485920a44..687015cfa 100644 --- a/core/src/main/java/org/mskcc/cbio/oncokb/util/AlterationUtils.java +++ b/core/src/main/java/org/mskcc/cbio/oncokb/util/AlterationUtils.java @@ -108,7 +108,7 @@ public static Set findOverlapAlteration(List alterations } private static Matcher getExclusionCriteriaMatcher(String proteinChange) { - Pattern exclusionPatter = Pattern.compile("(.*)\\{\\s*(exclude|excluding)(.*)\\}", Pattern.CASE_INSENSITIVE); + Pattern exclusionPatter = Pattern.compile("(.*)[\\{\\()]\\s*(exclude|excluding)(.*)[\\}\\)]", Pattern.CASE_INSENSITIVE); Matcher exclusionMatch = exclusionPatter.matcher(proteinChange); return exclusionMatch; } @@ -1142,7 +1142,7 @@ private static String getMissenseVariantAllele(Alteration alteration, int positi return null; } - public static List lookupVariant(String query, Boolean exactMatch, List alterations) { + public static List lookupVariant(String query, Boolean exactMatch, Boolean omitExclusion, List alterations) { List alterationList = new ArrayList<>(); // Only support columns(alteration/name) blur search. query = query.toLowerCase().trim(); @@ -1152,12 +1152,12 @@ public static List lookupVariant(String query, Boolean exactMatch, L return alterationList; query = query.trim().toLowerCase(); for (Alteration alteration : alterations) { - if (isMatch(exactMatch, query, alteration.getAlteration())) { + if (isMatch(exactMatch, omitExclusion, query, alteration.getAlteration())) { alterationList.add(alteration); continue; } - if (isMatch(exactMatch, query, alteration.getName())) { + if (isMatch(exactMatch, omitExclusion, query, alteration.getName())) { alterationList.add(alteration); continue; } @@ -1168,7 +1168,7 @@ public static List lookupVariant(String query, Boolean exactMatch, L String fullName = NamingUtils.getFullName(query); if (fullName != null) { for (Alteration alteration : alterations) { - if (isMatch(exactMatch, fullName, alteration.getName())) { + if (isMatch(exactMatch, omitExclusion, fullName, alteration.getName())) { alterationList.add(alteration); } } @@ -1177,8 +1177,11 @@ public static List lookupVariant(String query, Boolean exactMatch, L return alterationList; } - private static Boolean isMatch(Boolean exactMatch, String query, String string) { + private static Boolean isMatch(Boolean exactMatch, Boolean omitExclusion, String query, String string) { if (string != null) { + if (omitExclusion) { + string = AlterationUtils.removeExclusionCriteria(string); + } if (exactMatch) { if (StringUtils.containsIgnoreCase(string, query)) { return true; diff --git a/core/src/main/java/org/mskcc/cbio/oncokb/util/EvidenceUtils.java b/core/src/main/java/org/mskcc/cbio/oncokb/util/EvidenceUtils.java index 83f531c56..fd830619f 100644 --- a/core/src/main/java/org/mskcc/cbio/oncokb/util/EvidenceUtils.java +++ b/core/src/main/java/org/mskcc/cbio/oncokb/util/EvidenceUtils.java @@ -8,7 +8,6 @@ import org.mskcc.cbio.oncokb.bo.ArticleBo; import org.mskcc.cbio.oncokb.bo.EvidenceBo; import org.mskcc.cbio.oncokb.model.*; -import org.mskcc.cbio.oncokb.model.TumorType; import javax.xml.parsers.ParserConfigurationException; import java.util.*; @@ -181,7 +180,7 @@ private static Set getEvidence(ReferenceGenome referenceGenome, Eviden if (query.getGene() != null) { genes.add(query.getGene()); - if (query.getExactMatchedAlteration() == null && query.getAlterations().isEmpty() && query.getAlleles().isEmpty()) { + if ((query.getExactMatchedAlteration() != null && query.getExactMatchedAlteration().getAlteration().length() == 0) && query.getAlterations().isEmpty() && query.getAlleles().isEmpty()) { alterations.addAll(AlterationUtils.getAllAlterations(referenceGenome, query.getGene())); } else { if (query.getAlterations() != null) { @@ -366,6 +365,8 @@ private static Set filterEvidence(Set evidences, EvidenceQue //Add all gene specific evidences if (evidence.getAlterations().isEmpty()) { filtered.add(evidence); + } else if (evidenceQuery.getExactMatchedAlteration() != null && StringUtils.isEmpty(evidenceQuery.getExactMatchedAlteration().getAlteration())) { + filtered.add(evidence); } else { boolean hasjointed = !Collections.disjoint(evidence.getAlterations(), evidenceQuery.getAlterations()); if (!hasjointed) { diff --git a/core/src/main/java/org/mskcc/cbio/oncokb/util/IndicatorUtils.java b/core/src/main/java/org/mskcc/cbio/oncokb/util/IndicatorUtils.java index 78199f8f9..7bae27ed1 100644 --- a/core/src/main/java/org/mskcc/cbio/oncokb/util/IndicatorUtils.java +++ b/core/src/main/java/org/mskcc/cbio/oncokb/util/IndicatorUtils.java @@ -13,6 +13,7 @@ import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.mskcc.cbio.oncokb.util.LevelUtils.getTherapeuticLevelsWithPriorityLIstIterator; import static org.mskcc.cbio.oncokb.util.SummaryUtils.allelesToStr; @@ -276,38 +277,37 @@ public static IndicatorQueryResp processQuery(Query query, indicatorQuery.setMutationEffect(mutationEffectResp); } } + } - if (hasTreatmentEvidence) { - if (StringUtils.isEmpty(query.getTumorType())) { - treatmentEvidences = EvidenceUtils.getRelevantEvidences(query, matchedAlt, - selectedTreatmentEvidence, levels, relevantAlterationsWithoutAlternativeAlleles, alleles); - } else { - treatmentEvidences = EvidenceUtils.keepHighestLevelForSameTreatments( - EvidenceUtils.getRelevantEvidences(query, matchedAlt, - selectedTreatmentEvidence, levels, relevantAlterationsWithoutAlternativeAlleles, alleles), query.getReferenceGenome(), matchedAlt); - } + // Set implications + if (hasTreatmentEvidence) { + if (StringUtils.isEmpty(query.getTumorType())) { + treatmentEvidences = EvidenceUtils.getRelevantEvidences(query, matchedAlt, + selectedTreatmentEvidence, levels, relevantAlterationsWithoutAlternativeAlleles, alleles); + } else { + treatmentEvidences = EvidenceUtils.keepHighestLevelForSameTreatments( + EvidenceUtils.getRelevantEvidences(query, matchedAlt, + selectedTreatmentEvidence, levels, relevantAlterationsWithoutAlternativeAlleles, alleles), query.getReferenceGenome(), matchedAlt); } + } - if (hasDiagnosticImplicationEvidence) { - List implications = new ArrayList<>(); - implications.addAll(getImplications(matchedAlt, alleles, relevantAlterationsWithoutAlternativeAlleles, EvidenceType.DIAGNOSTIC_IMPLICATION, matchedTumorType, StringUtils.isEmpty(query.getTumorType()) ? null : relevantDownwardTumorTypes, query.getHugoSymbol(), Collections.singleton(LevelOfEvidence.LEVEL_Dx1))); - - // For Dx2 and Dx3, the logic is the same as Tx/Px - Set levelOfEvidences = new HashSet<>(); - levelOfEvidences.add(LevelOfEvidence.LEVEL_Dx2); - levelOfEvidences.add(LevelOfEvidence.LEVEL_Dx3); - implications.addAll(getImplications(matchedAlt, alleles, relevantAlterationsWithoutAlternativeAlleles, EvidenceType.DIAGNOSTIC_IMPLICATION, matchedTumorType, StringUtils.isEmpty(query.getTumorType()) ? null : relevantUpwardTumorTypes, query.getHugoSymbol(), levelOfEvidences)); - indicatorQuery.setDiagnosticImplications(implications); - if (indicatorQuery.getDiagnosticImplications().size() > 0) { - indicatorQuery.setHighestDiagnosticImplicationLevel(LevelUtils.getHighestDiagnosticImplicationLevel(indicatorQuery.getDiagnosticImplications().stream().map(implication -> implication.getLevelOfEvidence()).collect(Collectors.toSet()))); - } + if (gene != null && hasDiagnosticImplicationEvidence) { + List implications = new ArrayList<>(); + Set levelOfEvidences = new HashSet<>(); + levelOfEvidences.add(LevelOfEvidence.LEVEL_Dx1); + levelOfEvidences.add(LevelOfEvidence.LEVEL_Dx2); + levelOfEvidences.add(LevelOfEvidence.LEVEL_Dx3); + implications.addAll(getImplications(gene, matchedAlt, alleles, relevantAlterationsWithoutAlternativeAlleles, EvidenceType.DIAGNOSTIC_IMPLICATION, matchedTumorType, StringUtils.isEmpty(query.getTumorType()) ? null : relevantUpwardTumorTypes, query.getHugoSymbol(), levelOfEvidences)); + indicatorQuery.setDiagnosticImplications(implications); + if (indicatorQuery.getDiagnosticImplications().size() > 0) { + indicatorQuery.setHighestDiagnosticImplicationLevel(LevelUtils.getHighestDiagnosticImplicationLevel(indicatorQuery.getDiagnosticImplications().stream().map(implication -> implication.getLevelOfEvidence()).collect(Collectors.toSet()))); } + } - if (hasPrognosticImplicationEvidence) { - indicatorQuery.setPrognosticImplications(getImplications(matchedAlt, alleles, relevantAlterationsWithoutAlternativeAlleles, EvidenceType.PROGNOSTIC_IMPLICATION, matchedTumorType, StringUtils.isEmpty(query.getTumorType()) ? null : relevantUpwardTumorTypes, query.getHugoSymbol(), null)); - if (indicatorQuery.getPrognosticImplications().size() > 0) { - indicatorQuery.setHighestPrognosticImplicationLevel(LevelUtils.getHighestPrognosticImplicationLevel(indicatorQuery.getPrognosticImplications().stream().map(implication -> implication.getLevelOfEvidence()).collect(Collectors.toSet()))); - } + if (gene != null && hasPrognosticImplicationEvidence) { + indicatorQuery.setPrognosticImplications(getImplications(gene, matchedAlt, alleles, relevantAlterationsWithoutAlternativeAlleles, EvidenceType.PROGNOSTIC_IMPLICATION, matchedTumorType, StringUtils.isEmpty(query.getTumorType()) ? null : relevantUpwardTumorTypes, query.getHugoSymbol(), null)); + if (indicatorQuery.getPrognosticImplications().size() > 0) { + indicatorQuery.setHighestPrognosticImplicationLevel(LevelUtils.getHighestPrognosticImplicationLevel(indicatorQuery.getPrognosticImplications().stream().map(implication -> implication.getLevelOfEvidence()).collect(Collectors.toSet()))); } } @@ -536,23 +536,30 @@ private static List getImplicationFromEvidence(List evide return implications; } - private static List getImplications(Alteration matchedAlt, List alternativeAlleles, List relevantAlterations, EvidenceType evidenceType, TumorType matchedTumorType, List tumorTypes, String queryHugoSymbol, Set levelOfEvidences) { + private static List getImplications(Gene gene, Alteration matchedAlt, List alternativeAlleles, List relevantAlterations, EvidenceType evidenceType, TumorType matchedTumorType, List tumorTypes, String queryHugoSymbol, Set levelOfEvidences) { List implications = new ArrayList<>(); - // Find alteration specific evidence - List selfAltEvis = EvidenceUtils.getEvidence(Collections.singletonList(matchedAlt), Collections.singleton(evidenceType), matchedTumorType, tumorTypes, levelOfEvidences); - if (selfAltEvis != null && selfAltEvis.size() > 0) { - implications.addAll(getImplicationFromEvidence(selfAltEvis, queryHugoSymbol)); - } + if (matchedAlt != null && !StringUtils.isEmpty(matchedAlt.getAlteration())) { + // Find alteration specific evidence + List selfAltEvis = EvidenceUtils.getEvidence(Collections.singletonList(matchedAlt), Collections.singleton(evidenceType), matchedTumorType, tumorTypes, levelOfEvidences); + if (selfAltEvis != null && selfAltEvis.size() > 0) { + implications.addAll(getImplicationFromEvidence(selfAltEvis, queryHugoSymbol)); + } - List listToBeRemoved = new ArrayList<>(alternativeAlleles); - listToBeRemoved.add(matchedAlt); + List listToBeRemoved = new ArrayList<>(alternativeAlleles); + listToBeRemoved.add(matchedAlt); - for (Alteration alt : AlterationUtils.removeAlterationsFromList(relevantAlterations, listToBeRemoved)) { - List altEvis = EvidenceUtils.getEvidence(Collections.singletonList(alt), Collections.singleton(evidenceType), matchedTumorType, tumorTypes, levelOfEvidences); - if (altEvis != null && altEvis.size() > 0) { - implications.addAll(getImplicationFromEvidence(altEvis, queryHugoSymbol)); + for (Alteration alt : AlterationUtils.removeAlterationsFromList(relevantAlterations, listToBeRemoved)) { + List altEvis = EvidenceUtils.getEvidence(Collections.singletonList(alt), Collections.singleton(evidenceType), matchedTumorType, tumorTypes, levelOfEvidences); + if (altEvis != null && altEvis.size() > 0) { + implications.addAll(getImplicationFromEvidence(altEvis, queryHugoSymbol)); + } } + } else { + Set geneEvisSet = EvidenceUtils.getEvidenceByGeneAndEvidenceTypes(gene, Stream.of(evidenceType).collect(Collectors.toSet())); + List geneEvis = new ArrayList<>(); + geneEvis.addAll(geneEvisSet); + implications.addAll(getImplicationFromEvidence(geneEvis, queryHugoSymbol)); } return filterImplication(implications); } diff --git a/core/src/main/java/org/mskcc/cbio/oncokb/util/LevelUtils.java b/core/src/main/java/org/mskcc/cbio/oncokb/util/LevelUtils.java index 9e29187be..fb9a08d9f 100644 --- a/core/src/main/java/org/mskcc/cbio/oncokb/util/LevelUtils.java +++ b/core/src/main/java/org/mskcc/cbio/oncokb/util/LevelUtils.java @@ -272,6 +272,18 @@ public static List getIndexedTherapeuticLevels() { return new ArrayList<>(THERAPEUTIC_LEVELS_WITH_PRIORITY); } + public static List getIndexedDiagnosticLevels() { + return new ArrayList<>(DIAGNOSTIC_LEVELS); + } + + public static List getIndexedPrognosticLevels() { + return new ArrayList<>(PROGNOSTIC_LEVELS); + } + + public static List getIndexedFdaLevels() { + return new ArrayList<>(FDA_LEVELS); + } + public static Set getPrognosticLevels() { return new HashSet<>(CollectionUtils.intersection(PUBLIC_LEVELS, PROGNOSTIC_LEVELS)); } diff --git a/core/src/main/java/org/mskcc/cbio/oncokb/util/MainUtils.java b/core/src/main/java/org/mskcc/cbio/oncokb/util/MainUtils.java index d8f3cc1a7..547ced284 100644 --- a/core/src/main/java/org/mskcc/cbio/oncokb/util/MainUtils.java +++ b/core/src/main/java/org/mskcc/cbio/oncokb/util/MainUtils.java @@ -45,6 +45,15 @@ public class MainUtils { ) ); + private static final List PRIORITIZED_QUERY_TYPES = Collections.unmodifiableList( + Arrays.asList( + AnnotationSearchQueryType.GENE, + AnnotationSearchQueryType.VARIANT, + AnnotationSearchQueryType.CANCER_TYPE, + AnnotationSearchQueryType.DRUG + ) + ); + public static Oncogenicity getCuratedAlterationOncogenicity(Alteration alteration) { List selfAltOncogenicEvis = EvidenceUtils.getEvidence(Collections.singletonList(alteration), Collections.singleton(EvidenceType.ONCOGENIC), null); @@ -822,4 +831,32 @@ public static String toLowerCaseExceptAllCaps(String text) { } return sb.toString(); } + + public static LinkedHashSet getLimit(LinkedHashSet result, Integer limit) { + final Integer DEFAULT_RETURN_LIMIT = 5; + if (limit == null) + limit = DEFAULT_RETURN_LIMIT; + Integer count = 0; + LinkedHashSet firstFew = new LinkedHashSet<>(); + Iterator itr = result.iterator(); + while (itr.hasNext() && count < limit) { + firstFew.add(itr.next()); + count++; + } + return firstFew; + } + + public static Integer compareAnnotationSearchQueryType(AnnotationSearchQueryType q1, AnnotationSearchQueryType q2, Boolean asc) { + if (asc == null) { + asc = true; + } + if (q1 == null) { + if (q2 == null) + return 0; + return asc ? 1 : -1; + } + if (q2 == null) + return asc ? -1 : 1; + return (PRIORITIZED_QUERY_TYPES.indexOf(q2) - PRIORITIZED_QUERY_TYPES.indexOf(q1)) * (asc ? 1 : -1); + } } diff --git a/web/src/main/java/org/mskcc/cbio/oncokb/api/pub/v1/SearchApi.java b/web/src/main/java/org/mskcc/cbio/oncokb/api/pub/v1/SearchApi.java index f240a1ead..3c39c1cf8 100755 --- a/web/src/main/java/org/mskcc/cbio/oncokb/api/pub/v1/SearchApi.java +++ b/web/src/main/java/org/mskcc/cbio/oncokb/api/pub/v1/SearchApi.java @@ -2,10 +2,8 @@ import io.swagger.annotations.*; import org.mskcc.cbio.oncokb.config.annotation.PremiumPublicApi; -import org.mskcc.cbio.oncokb.config.annotation.PublicApi; import org.mskcc.cbio.oncokb.model.EvidenceQueries; import org.mskcc.cbio.oncokb.model.IndicatorQueryResp; -import org.mskcc.cbio.oncokb.model.ReferenceGenome; import org.mskcc.cbio.oncokb.model.StructuralVariantType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; diff --git a/web/src/main/java/org/mskcc/cbio/oncokb/api/pub/v1/VariantsApiController.java b/web/src/main/java/org/mskcc/cbio/oncokb/api/pub/v1/VariantsApiController.java index dd3ee707b..11153ff3e 100755 --- a/web/src/main/java/org/mskcc/cbio/oncokb/api/pub/v1/VariantsApiController.java +++ b/web/src/main/java/org/mskcc/cbio/oncokb/api/pub/v1/VariantsApiController.java @@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestParam; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -107,7 +108,7 @@ private List getVariants(VariantSearchQuery query) throws ApiExcepti alterationSet.addAll(allAlterations); } else { AlterationBo alterationBo = new ApplicationContextSingleton().getAlterationBo(); - List alterations = AlterationUtils.lookupVariant(query.getVariant(), true, allAlterations); + List alterations = AlterationUtils.lookupVariant(query.getVariant(), true, false, allAlterations); // If this variant is not annotated if (alterations == null || alterations.isEmpty()) { @@ -138,7 +139,7 @@ private List getVariants(VariantSearchQuery query) throws ApiExcepti alterationSet.addAll(AlterationUtils.getAlterationsByKnownEffectInGene(gene, AlterationUtils.getInferredAlterationsKnownEffect(query.getVariant()), false)); } } else { - alterationList = AlterationUtils.lookupVariant(query.getVariant(), false, AlterationUtils.getAllAlterations()); + alterationList = AlterationUtils.lookupVariant(query.getVariant(), false, false, AlterationUtils.getAllAlterations()); } } } diff --git a/web/src/main/java/org/mskcc/cbio/oncokb/api/pvt/PrivateAnnotationController.java b/web/src/main/java/org/mskcc/cbio/oncokb/api/pvt/PrivateAnnotationController.java new file mode 100644 index 000000000..37bb009c6 --- /dev/null +++ b/web/src/main/java/org/mskcc/cbio/oncokb/api/pvt/PrivateAnnotationController.java @@ -0,0 +1,353 @@ +package org.mskcc.cbio.oncokb.api.pvt; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import org.apache.commons.collections.map.HashedMap; +import org.apache.commons.lang3.StringUtils; +import org.kohsuke.github.GHCompare.Tree; +import org.mskcc.cbio.oncokb.config.annotation.PremiumPublicApi; +import org.mskcc.cbio.oncokb.util.AlterationUtils; +import org.mskcc.cbio.oncokb.util.CacheUtils; +import org.mskcc.cbio.oncokb.util.EvidenceTypeUtils; +import org.mskcc.cbio.oncokb.util.EvidenceUtils; +import org.mskcc.cbio.oncokb.util.GeneUtils; +import org.mskcc.cbio.oncokb.util.IndicatorUtils; +import org.mskcc.cbio.oncokb.util.LevelUtils; +import org.mskcc.cbio.oncokb.util.MainUtils; +import org.mskcc.cbio.oncokb.util.TumorTypeUtils; +import org.mskcc.cbio.oncokb.model.*; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; +import java.util.Objects; + +@RestController +@Api(tags = "Annotation") +public class PrivateAnnotationController { + + @PremiumPublicApi + @ApiOperation(value = "", notes = "Get annotations based on search", response = AnnotationSearchResult.class, responseContainer = "List") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK")}) + @RequestMapping(value = "/annotation/search", + produces = {"application/json"}, + method = RequestMethod.GET) + ResponseEntity> annotationSearchGet( + @ApiParam(value = "The search query, it could be hugoSymbol, variant or cancer type. At least two characters. Maximum two keywords are supported, separated by space", required = true) @RequestParam(value = "query") String query, + @ApiParam(value = "The limit of returned result.") @RequestParam(value = "limit", defaultValue = "10", required = false) Integer limit + ) { + final int DEFAULT_LIMIT = 10; + final int QUERY_MIN_LENGTH = 2; + TreeSet result = new TreeSet<>(new AnnotationSearchResultComp(query)); + if(limit == null) { + limit = DEFAULT_LIMIT; + } + if (query != null && query.length() >= QUERY_MIN_LENGTH) { + List keywords = Arrays.asList(query.trim().split("\\s+")); + + if (keywords.size() == 1) { + // Blur search gene + result.addAll(findActionableGenesByGeneSearch(keywords.get(0))); + + // // Blur search variant + result.addAll(findActionableGenesByAlterationSearch(keywords.get(0))); + + // // // Blur search cancer type + result.addAll(findActionalGenesByCancerType(keywords.get(0))); + + // // If the keyword contains dash and result is empty, then we should return both fusion genes + if (keywords.get(0).contains("-") && result.isEmpty()) { + for (String subKeyword : keywords.get(0).split("-")) { + result.addAll(findActionableGenesByGeneSearch(subKeyword)); + } + } + } else { + // Assume that the first keyword is a gene, followed by alteration + // Todo: We should be able to find the gene even if it is not the first keyword. + Set geneMatches = new HashSet<>(); + if (keywords.get(0).contains("-")) { + Set subGenes = new HashSet<>(); + for (String subKeyword : keywords.get(0).split("-")) { + subGenes.addAll(GeneUtils.searchGene(subKeyword, false)); + } + geneMatches.addAll(subGenes); + } else { + geneMatches.addAll(GeneUtils.searchGene(keywords.get(0), false)); + } + + String alterationKeywords = StringUtils.join(keywords.subList(1, keywords.size()), " "); + List altMatches = AlterationUtils.lookupVariant(alterationKeywords, false, true, AlterationUtils.getAllAlterations()) + .stream() + .filter(alt -> geneMatches.contains(alt.getGene())) + .collect(Collectors.toList()); + for (Gene gene: geneMatches) { + for (Alteration alteration: altMatches) { + Query indicatorQuery = new Query(); + indicatorQuery.setEntrezGeneId(gene.getEntrezGeneId()); + indicatorQuery.setHugoSymbol(gene.getHugoSymbol()); + indicatorQuery.setAlteration(alteration.getAlteration()); + AnnotationSearchResult annotationSearchResult = new AnnotationSearchResult(); + annotationSearchResult.setQueryType(AnnotationSearchQueryType.VARIANT); + annotationSearchResult.setIndicatorQueryResp(IndicatorUtils.processQuery(indicatorQuery, null, null, null)); + if (annotationSearchResult.getIndicatorQueryResp().getVariantExist()) { + result.add(annotationSearchResult); + } + } + } + + // // If there is no match, the keywords could referring to a variant, try to do a blur variant search + String fullKeywords = StringUtils.join(keywords, " "); + result.addAll(findActionableGenesByAlterationSearch(fullKeywords)); + + // // // Blur search for multi-word cancer type + result.addAll(findActionalGenesByCancerType(fullKeywords)); + } + } + + LinkedHashSet orderedResult = new LinkedHashSet<>(); + orderedResult.addAll(result); + + return new ResponseEntity<>(MainUtils.getLimit(orderedResult, limit), HttpStatus.OK); + } + + private LinkedHashSet findActionableGenesByGeneSearch(String keyword) { + LinkedHashSet result = new LinkedHashSet<>(); + Set geneMatches = GeneUtils.searchGene(keyword, false); + for (Gene gene: geneMatches) { + Query query = new Query(); + query.setEntrezGeneId(gene.getEntrezGeneId()); + query.setHugoSymbol(gene.getHugoSymbol()); + AnnotationSearchResult annotationSearchResult = new AnnotationSearchResult(); + annotationSearchResult.setQueryType(AnnotationSearchQueryType.GENE); + annotationSearchResult.setIndicatorQueryResp(IndicatorUtils.processQuery(query, null, null, null)); + result.add(annotationSearchResult); + } + return result; + } + + private LinkedHashSet findActionableGenesByAlterationSearch(String keyword) { + LinkedHashSet result = new LinkedHashSet<>(); + List altMatches = AlterationUtils.lookupVariant(keyword, false, true, AlterationUtils.getAllAlterations()); + for (Alteration alteration: altMatches) { + Query indicatorQuery = new Query(); + indicatorQuery.setAlteration(alteration.getName()); + indicatorQuery.setEntrezGeneId(alteration.getGene().getEntrezGeneId()); + indicatorQuery.setHugoSymbol(alteration.getGene().getHugoSymbol()); + AnnotationSearchResult annotationSearchResult = new AnnotationSearchResult(); + annotationSearchResult.setQueryType(AnnotationSearchQueryType.VARIANT); + annotationSearchResult.setIndicatorQueryResp(IndicatorUtils.processQuery(indicatorQuery, null, null, null)); + result.add(annotationSearchResult); + } + return result; + } + + private LinkedHashSet findActionalGenesByCancerType(String query) { + + Set allImplicationEvidences = EvidenceUtils.getEvidenceByEvidenceTypesAndLevels(EvidenceTypeUtils.getImplicationEvidenceTypes(), LevelUtils.getPublicLevels()); + + query = query.toLowerCase(); + + Set tumorTypeMatches = new HashSet<>(); + for(Map.Entry subtype: CacheUtils.getLowercaseSubtypeTumorTypeMap().entrySet()) { + if(subtype.getKey().contains(query)) { + tumorTypeMatches.add(subtype.getValue()); + } + } + + for(Map.Entry mainType: CacheUtils.getMainTypeTumorTypeMap().entrySet()) { + if (mainType.getKey().toLowerCase().contains(query)) { + tumorTypeMatches.add(mainType.getValue()); + } + } + + if (tumorTypeMatches.isEmpty()) { + return new LinkedHashSet<>(); + } + + LinkedHashSet result = new LinkedHashSet<>(); + Set searchObjects = new HashSet<>(); + for (TumorType tumorType : tumorTypeMatches) { + for (Evidence evidence : allImplicationEvidences) { + if (TumorTypeUtils.findEvidenceRelevantCancerTypes(evidence).contains(tumorType)) { + SearchObject searchObject = new SearchObject(); + searchObject.setGene(evidence.getGene()); + searchObject.setAlteration(evidence.getAlterations().iterator().next()); + searchObject.setTumorType(tumorType); + searchObjects.add(searchObject); + } + } + } + + for (SearchObject searchObject: searchObjects) { + Query indicatorQuery = new Query(); + indicatorQuery.setEntrezGeneId(searchObject.getGene().getEntrezGeneId()); + indicatorQuery.setHugoSymbol(searchObject.getGene().getHugoSymbol()); + indicatorQuery.setAlteration(searchObject.getAlteration().getName()); + if (searchObject.getTumorType().getMainType().toLowerCase().contains(query)) { + indicatorQuery.setTumorType(searchObject.getTumorType().getMainType()); + } else { + indicatorQuery.setTumorType(searchObject.getTumorType().getSubtype()); + } + AnnotationSearchResult annotationSearchResult = new AnnotationSearchResult(); + annotationSearchResult.setQueryType(AnnotationSearchQueryType.CANCER_TYPE); + annotationSearchResult.setIndicatorQueryResp(IndicatorUtils.processQuery(indicatorQuery, null, null, null)); + result.add(annotationSearchResult); + } + + return result; + } + +} + + +class AnnotationSearchResultComp implements Comparator { + private String keyword; + + public AnnotationSearchResultComp(String keyword) { + this.keyword = keyword.toLowerCase(); + } + + @Override + public int compare(AnnotationSearchResult a1, AnnotationSearchResult a2) { + IndicatorQueryResp i1 = a1.getIndicatorQueryResp(); + IndicatorQueryResp i2 = a2.getIndicatorQueryResp(); + + // Compare by query type + Integer result = MainUtils.compareAnnotationSearchQueryType(a1.getQueryType(), a2.getQueryType(), true); + + String name1 = ""; + String name2 = ""; + if (result == 0) { + if (a1.getQueryType().equals(TypeaheadQueryType.GENE)) { + name1 = i1.getQuery().getHugoSymbol().toLowerCase(); + name2 = i2.getQuery().getHugoSymbol().toLowerCase(); + } + if (a1.getQueryType().equals(TypeaheadQueryType.VARIANT)) { + name1 = i1.getQuery().getAlteration().toLowerCase(); + name2 = i2.getQuery().getAlteration().toLowerCase(); + } + if (a1.getQueryType().equals(TypeaheadQueryType.CANCER_TYPE)) { + name1 = i1.getQuery().getTumorType().toLowerCase(); + name2 = i2.getQuery().getTumorType().toLowerCase(); + } + } + Integer index1 = name1.indexOf(this.keyword); + Integer index2 = name2.indexOf(this.keyword); + if (index1.equals(index2)) { + return compareLevel(i1, i2, name1, name2); + } else { + if (index1.equals(-1)) + return 1; + if (index2.equals(-1)) + return -1; + return compareLevel(i1, i2, name1, name2); + } + + } + + private Integer compareLevel(IndicatorQueryResp i1, IndicatorQueryResp i2, String name1, String name2) { + // Compare therapeutic levels + LevelOfEvidence i1Level = i1.getHighestSensitiveLevel(); + LevelOfEvidence i2Level = i2.getHighestSensitiveLevel(); + if (i1Level == null) { + i1Level = i1.getHighestResistanceLevel(); + } + if (i2Level == null) { + i2Level = i2.getHighestResistanceLevel(); + } + Integer result = LevelUtils.compareLevel(i1Level, i2Level, LevelUtils.getIndexedTherapeuticLevels()); + if (result == 0) { + // Compare diagnostic level + result = LevelUtils.compareLevel(i1.getHighestDiagnosticImplicationLevel(), i2.getHighestDiagnosticImplicationLevel(), LevelUtils.getIndexedDiagnosticLevels()); + if (result == 0) { + result = LevelUtils.compareLevel(i1.getHighestPrognosticImplicationLevel(), i2.getHighestPrognosticImplicationLevel(), LevelUtils.getIndexedPrognosticLevels()); + if (result == 0) { + result = LevelUtils.compareLevel(i1.getHighestFdaLevel(), i2.getHighestFdaLevel(), LevelUtils.getIndexedFdaLevels()); + if (result == 0) { + //Compare Oncogenicity. Treat YES, LIKELY as the same + Oncogenicity o1 = Oncogenicity.getByEffect(i1.getOncogenic()); + Oncogenicity o2 = Oncogenicity.getByEffect(i2.getOncogenic()); + if (o1 != null && o1.equals(Oncogenicity.LIKELY)) { + o1 = Oncogenicity.YES; + } + if (o2 != null && o2.equals(Oncogenicity.LIKELY)) { + o2 = Oncogenicity.YES; + } + result = MainUtils.compareOncogenicity(o1, o2, true); + if (result == 0) { + result = name1.compareTo(name2); + if (result == 0) { + result = i1.getQuery().getHugoSymbol().compareTo(i2.getQuery().getHugoSymbol()); + } + } + } + return result; + } + } + + } + return result; + } +} + +class SearchObject { + private Gene gene; + private Alteration alteration; + private TumorType tumorType; + + public Gene getGene() { + return this.gene; + } + + public void setGene(Gene gene) { + this.gene = gene; + } + + public Alteration getAlteration() { + return this.alteration; + } + + public void setAlteration(Alteration alteration) { + this.alteration = alteration; + } + + public TumorType getTumorType() { + return this.tumorType; + } + + public void setTumorType(TumorType tumorType) { + this.tumorType = tumorType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SearchObject)) return false; + SearchObject searchObject = (SearchObject) o; + return Objects.equals(getAlteration(), searchObject.getAlteration()) && + Objects.equals(getGene(), searchObject.getGene()) && + Objects.equals(getTumorType(), searchObject.getTumorType()); + } + + @Override + public int hashCode() { + return Objects.hash(getGene(), getAlteration(), getTumorType()); + } + +} + + diff --git a/web/src/main/java/org/mskcc/cbio/oncokb/api/pvt/PrivateSearchApiController.java b/web/src/main/java/org/mskcc/cbio/oncokb/api/pvt/PrivateSearchApiController.java index 6f314003f..f4e7e66a5 100644 --- a/web/src/main/java/org/mskcc/cbio/oncokb/api/pvt/PrivateSearchApiController.java +++ b/web/src/main/java/org/mskcc/cbio/oncokb/api/pvt/PrivateSearchApiController.java @@ -11,7 +11,6 @@ import org.mskcc.cbio.oncokb.cache.CacheFetcher; import org.mskcc.cbio.oncokb.genomenexus.GNVariantAnnotationType; import org.mskcc.cbio.oncokb.model.*; -import org.mskcc.cbio.oncokb.model.TumorType; import org.mskcc.cbio.oncokb.util.*; import org.oncokb.oncokb_transcript.ApiException; import org.springframework.beans.factory.annotation.Autowired; @@ -22,6 +21,7 @@ import java.util.*; import java.util.regex.Matcher; +import java.util.stream.Collector; import java.util.stream.Collectors; import static org.mskcc.cbio.oncokb.Constants.DEFAULT_REFERENCE_GENOME; @@ -150,7 +150,7 @@ public ResponseEntity> searchTypeAheadGet( result.addAll(convertGene(GeneUtils.searchGene(keywords.get(0), false), keywords.get(0))); // Blur search variant - result.addAll(convertVariant(AlterationUtils.lookupVariant(keywords.get(0), false, AlterationUtils.getAllAlterations()), keywords.get(0))); + result.addAll(convertVariant(AlterationUtils.lookupVariant(keywords.get(0), false, false, AlterationUtils.getAllAlterations()), keywords.get(0))); // Blur search drug result.addAll(findEvidencesWithDrugAssociated(keywords.get(0), false)); @@ -183,7 +183,7 @@ public ResponseEntity> searchTypeAheadGet( // If there is no match, the key words could referring to a variant, try to do a blur variant search String fullKeywords = StringUtils.join(keywords, " "); - result.addAll(convertVariant(AlterationUtils.lookupVariant(fullKeywords, false, AlterationUtils.getAllAlterations()), fullKeywords)); + result.addAll(convertVariant(AlterationUtils.lookupVariant(fullKeywords, false, false, AlterationUtils.getAllAlterations()), fullKeywords)); // Blur search for cancer type result.addAll(findMatchingCancerTypes(fullKeywords, false)); @@ -223,7 +223,7 @@ public ResponseEntity> searchTypeAheadGet( typeaheadSearchResp.setQueryType(TypeaheadQueryType.TEXT); result.add(typeaheadSearchResp); } - return new ResponseEntity<>(getLimit(result, limit), HttpStatus.OK); + return new ResponseEntity<>(MainUtils.getLimit(result, limit), HttpStatus.OK); } @Override @@ -235,7 +235,7 @@ public ResponseEntity> s limit = DEFAULT_RETURN_LIMIT; } OncokbTranscriptService oncokbTranscriptService = new OncokbTranscriptService(); - return new ResponseEntity<>(getLimit(new LinkedHashSet<>(oncokbTranscriptService.findDrugs(query)), limit), HttpStatus.OK); + return new ResponseEntity<>(MainUtils.getLimit(new LinkedHashSet<>(oncokbTranscriptService.findDrugs(query)), limit), HttpStatus.OK); } private List findMatchingCancerTypes(String query, Boolean exactMatch) { @@ -428,7 +428,7 @@ private LinkedHashSet getMatch(Map> map, Set keywordsMatches = null; for (String keyword : keywords) { if (!keyword.equals(entry.getKey())) { - List matches = AlterationUtils.lookupVariant(keyword, exactMatch, alterations); + List matches = AlterationUtils.lookupVariant(keyword, exactMatch, false, alterations); if (matches != null) { if (keywordsMatches == null) { keywordsMatches = new HashSet<>(); @@ -447,7 +447,7 @@ private LinkedHashSet getMatch(Map> map, } else { for (String keyword : keywords) { if (!keyword.equals(entry.getKey())) - result.addAll(convertVariant(AlterationUtils.lookupVariant(keyword, exactMatch, alterations), keyword)); + result.addAll(convertVariant(AlterationUtils.lookupVariant(keyword, exactMatch, false, alterations), keyword)); } } } @@ -667,19 +667,6 @@ private TypeaheadSearchResp newTypeaheadAnnotation(String query, GNVariantAnnota typeaheadSearchResp.setLink(link); return typeaheadSearchResp; } - - private LinkedHashSet getLimit(LinkedHashSet result, Integer limit) { - if (limit == null) - limit = DEFAULT_RETURN_LIMIT; - Integer count = 0; - LinkedHashSet firstFew = new LinkedHashSet<>(); - Iterator itr = result.iterator(); - while (itr.hasNext() && count < limit) { - firstFew.add(itr.next()); - count++; - } - return firstFew; - } } class GeneComp implements Comparator {