X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2Fstructurechooser%2FThreeDBStructureChooserQuerySource.java;h=b1ad65fc432e30d532df17f4b0119cb207bdf3e7;hb=a1984b1c8c273ed33c7ce9283039f4027dcae2de;hp=0bf9fdb484d6861143b4da43574dcb33cfa70582;hpb=d64daffb893f7efbf8e424e41f3ddb22f04ab121;p=jalview.git diff --git a/src/jalview/gui/structurechooser/ThreeDBStructureChooserQuerySource.java b/src/jalview/gui/structurechooser/ThreeDBStructureChooserQuerySource.java index 0bf9fdb..b1ad65f 100644 --- a/src/jalview/gui/structurechooser/ThreeDBStructureChooserQuerySource.java +++ b/src/jalview/gui/structurechooser/ThreeDBStructureChooserQuerySource.java @@ -1,27 +1,32 @@ package jalview.gui.structurechooser; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import java.util.Set; import javax.swing.JTable; +import jalview.bin.Cache; import jalview.datamodel.DBRefEntry; import jalview.datamodel.DBRefSource; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; +import jalview.fts.api.FTSData; import jalview.fts.api.FTSDataColumnI; import jalview.fts.api.FTSRestClientI; import jalview.fts.core.FTSDataColumnPreferences; import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource; import jalview.fts.core.FTSRestRequest; import jalview.fts.core.FTSRestResponse; +import jalview.fts.service.threedbeacons.TDB_FTSData; import jalview.fts.service.threedbeacons.TDBeaconsFTSRestClient; import jalview.jbgui.FilterOption; -import jalview.util.MessageManager; /** * logic for querying the 3DBeacons API for structures of sequences @@ -32,25 +37,36 @@ public class ThreeDBStructureChooserQuerySource extends StructureChooserQuerySource { - private static int MAX_QLENGTH = 7820; + private Set tdBeaconsFilters = null, defaultFilters = null; + + public static final String FILTER_TDBEACONS_COVERAGE = "3d_beacons_coverage"; + + public static final String FILTER_FIRST_BEST_COVERAGE = "3d_beacons_first_best_coverage"; + + private static final String FILTER_SOURCE_PREFIX = "only_"; protected FTSRestRequest lastTdbRequest; protected FTSRestClientI tdbRestClient; + private FTSRestRequest lastPdbRequest; + public ThreeDBStructureChooserQuerySource() { + defaultFilters = new LinkedHashSet(); + defaultFilters.add(FILTER_TDBEACONS_COVERAGE); + defaultFilters.add(FILTER_FIRST_BEST_COVERAGE); + tdbRestClient = TDBeaconsFTSRestClient.getInstance(); docFieldPrefs = new FTSDataColumnPreferences( PreferenceSource.STRUCTURE_CHOOSER, TDBeaconsFTSRestClient.getInstance()); - } - /** - * Builds a query string for a given sequences using its DBRef entries - * 3d Beacons is only useful for uniprot IDs + * Builds a query string for a given sequences using its DBRef entries 3d + * Beacons is only useful for uniprot IDs + * * @param seq * the sequences to build a query for * @return the built query string @@ -58,40 +74,43 @@ public class ThreeDBStructureChooserQuerySource public String buildQuery(SequenceI seq) { - boolean isPDBRefsFound = false; - boolean isUniProtRefsFound = false; - StringBuilder queryBuilder = new StringBuilder(); - Set seqRefs = new LinkedHashSet<>(); - - /* - * note PDBs as DBRefEntry so they are not duplicated in query - */ - Set pdbids = new HashSet<>(); - List refs = seq.getDBRefs(); + int ib = checkUniprotRefs(refs); + if (ib > -1) + { + return getDBRefId(refs.get(ib)); + } + return null; + } + + /** + * Searches DBRefEntry for uniprot refs + * + * @param seq + * @return -2 if no uniprot refs, -1 if no canonical ref., otherwise index of + * Uniprot canonical DBRefEntry + */ + public static int checkUniprotRefs(List refs) + { + boolean hasUniprot = false; if (refs != null && refs.size() != 0) { for (int ib = 0, nb = refs.size(); ib < nb; ib++) { DBRefEntry dbRef = refs.get(ib); - if (isValidSeqName(getDBRefId(dbRef)) - && queryBuilder.length() < MAX_QLENGTH) + if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT)) { - if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT) && dbRef.isCanonical()) + hasUniprot = true; + if (dbRef.isCanonical()) { - // TODO: pick best Uniprot accession - isUniProtRefsFound=true; - return getDBRefId(dbRef); - + return ib; } } } } - return null; + return hasUniprot ? -1 : -2; } - - /** * Ensures sequence ref names are not less than 3 characters and does not * contain a database name @@ -101,7 +120,6 @@ public class ThreeDBStructureChooserQuerySource */ static boolean isValidSeqName(String seqName) { - // System.out.println("seqName : " + seqName); String ignoreList = "pdb,uniprot,swiss-prot"; if (seqName.length() < 3) { @@ -111,7 +129,7 @@ public class ThreeDBStructureChooserQuerySource { return false; } - seqName = seqName.toLowerCase(); + seqName = seqName.toLowerCase(Locale.ROOT); for (String ignoredEntry : ignoreList.split(",")) { if (seqName.contains(ignoredEntry)) @@ -148,22 +166,53 @@ public class ThreeDBStructureChooserQuerySource FilterOption selectedFilterOpt, boolean b) throws Exception { FTSRestResponse resultList; - FTSRestRequest tdbRequest = getTDBeaconsRequest(seq, wantedFields); - resultList = tdbRestClient.executeRequest(tdbRequest); + if (selectedFilterOpt != null + && tdBeaconsFilter(selectedFilterOpt.getValue())) + { + FTSRestRequest tdbRequest = getTDBeaconsRequest(seq, wantedFields); + resultList = tdbRestClient.executeRequest(tdbRequest); - lastTdbRequest = tdbRequest; + lastTdbRequest = tdbRequest; + if (resultList != null) + { // Query the PDB and add additional metadata + FTSRestResponse pdbResponse = fetchStructuresMetaDataFor( + getPDBQuerySource(), resultList); + + resultList = joinResponses(resultList, pdbResponse); + } + return resultList; + } + // use the PDBFTS directly + resultList = getPDBQuerySource().fetchStructuresMetaData(seq, + wantedFields, selectedFilterOpt, b); + lastTdbRequest = getPDBQuerySource().lastPdbRequest; + lastPdbRequest = lastTdbRequest; // both queries the same - indicates we + // rank using PDBe return resultList; + + } + + PDBStructureChooserQuerySource pdbQuerySource = null; + + private PDBStructureChooserQuerySource getPDBQuerySource() + { + if (pdbQuerySource == null) + { + pdbQuerySource = new PDBStructureChooserQuerySource(); + } + return pdbQuerySource; } - - private FTSRestRequest getTDBeaconsRequest(SequenceI seq, Collection wantedFields) + private FTSRestRequest getTDBeaconsRequest(SequenceI seq, + Collection wantedFields) { FTSRestRequest pdbRequest = new FTSRestRequest(); pdbRequest.setAllowEmptySeq(false); pdbRequest.setResponseSize(500); pdbRequest.setWantedFields(wantedFields); String query = buildQuery(seq); - if (query==null) { + if (query == null) + { return null; } pdbRequest.setSearchTerm(query + ".json"); @@ -171,28 +220,96 @@ public class ThreeDBStructureChooserQuerySource return pdbRequest; } -@Override + @Override public List getAvailableFilterOptions(String VIEWS_FILTER) { - List filters = new ArrayList(); - filters.add(new FilterOption( - MessageManager.getString("label.best_quality"), - "overall_quality", VIEWS_FILTER, false)); - filters.add(new FilterOption( - MessageManager.getString("label.best_resolution"), - "resolution", VIEWS_FILTER, false)); - filters.add(new FilterOption( - MessageManager.getString("label.most_protein_chain"), - "number_of_protein_chains", VIEWS_FILTER, false)); - filters.add(new FilterOption( - MessageManager.getString("label.most_bound_molecules"), - "number_of_bound_molecules", VIEWS_FILTER, false)); - filters.add(new FilterOption( - MessageManager.getString("label.most_polymer_residues"), - "number_of_polymer_residues", VIEWS_FILTER, true)); - + List filters = getPDBQuerySource() + .getAvailableFilterOptions(VIEWS_FILTER); + tdBeaconsFilters = new LinkedHashSet(); + tdBeaconsFilters.addAll(defaultFilters); + filters.add(0, new FilterOption("Best 3D-Beacons Coverage", + FILTER_FIRST_BEST_COVERAGE, VIEWS_FILTER, false, this)); + filters.add(1, new FilterOption("Multiple 3D-Beacons Coverage", + FILTER_TDBEACONS_COVERAGE, VIEWS_FILTER, true, this)); + return filters; } + + @Override + public void updateAvailableFilterOptions(String VIEWS_FILTER, + List xtantOptions, Collection tdbEntries) + { + if (tdbEntries != null && lastTdbRequest != null) + { + boolean hasPDBe = false; + for (FTSData _row : tdbEntries) + { + // tdb returns custom object + TDB_FTSData row = (TDB_FTSData) _row; + String provider = (String) row.getProvider(); + FilterOption providerOpt = new FilterOption( + "3DB Provider - " + provider, + FILTER_SOURCE_PREFIX + provider, VIEWS_FILTER, false, this); + if (!xtantOptions.contains(providerOpt)) + { + xtantOptions.add(1, providerOpt); + tdBeaconsFilters.add(FILTER_SOURCE_PREFIX + provider); + if ("PDBe".equalsIgnoreCase(provider)) + { + hasPDBe = true; + } + } + } + if (!hasPDBe) + { + // remove the PDBe options from the available filters + int op = 0; + while (op < xtantOptions.size()) + { + FilterOption filter = xtantOptions.get(op); + if (filter + .getQuerySource() instanceof PDBStructureChooserQuerySource) + { + xtantOptions.remove(op); + } + else + { + op++; + } + } + } + } + + } + + private boolean tdBeaconsFilter(String fieldToFilterBy) + { + return tdBeaconsFilters != null + && tdBeaconsFilters.contains(fieldToFilterBy); + } + + private String remove_prefix(String fieldToFilterBy) + { + if (tdBeaconsFilters != null + && tdBeaconsFilters.contains(fieldToFilterBy) + && !defaultFilters.contains(fieldToFilterBy)) + { + return fieldToFilterBy.substring(FILTER_SOURCE_PREFIX.length()); + } + else + { + return null; + } + } + + @Override + public boolean needsRefetch(FilterOption selectedFilterOpt) + { + return selectedFilterOpt == null + || !tdBeaconsFilter(selectedFilterOpt.getValue()) + && lastPdbRequest != lastTdbRequest; + } + /** * FTSRestClient specific query builder to pick top ranked entry from a * fetchStructuresMetaData query @@ -209,67 +326,110 @@ public class ThreeDBStructureChooserQuerySource * @throws Exception */ public FTSRestResponse selectFirstRankedQuery(SequenceI seq, + Collection collectedResults, Collection wantedFields, String fieldToFilterBy, boolean b) throws Exception { + if (fieldToFilterBy != null && tdBeaconsFilter(fieldToFilterBy)) + { + TDBResultAnalyser analyser = new TDBResultAnalyser(seq, + collectedResults, lastTdbRequest, fieldToFilterBy, + remove_prefix(fieldToFilterBy)); - FTSRestResponse resultList; - FTSRestRequest pdbRequest = getTDBeaconsRequest(seq, wantedFields); - if (pdbRequest == null) { - return null; - } - pdbRequest.setResponseSize(1); - resultList = tdbRestClient.executeRequest(pdbRequest); - - // TODO: client side filtering - sort results and pick top one (or N) + FTSRestResponse resultList = new FTSRestResponse(); - lastTdbRequest = pdbRequest; - return resultList; + List filteredResponse = analyser.getFilteredResponse(); + + List selectedStructures = analyser + .selectStructures(filteredResponse); + resultList.setNumberOfItemsFound(selectedStructures.size()); + resultList.setSearchSummary(selectedStructures); + return resultList; + } + // Fall back to PDBe rankings + return getPDBQuerySource().selectFirstRankedQuery(seq, collectedResults, + wantedFields, fieldToFilterBy, b); } @Override public PDBEntry[] collectSelectedRows(JTable restable, int[] selectedRows, List selectedSeqsToView) { - int refSeqColIndex = restable.getColumn("Ref Sequence") - .getModelIndex(); + int refSeqColIndex = restable.getColumn("Ref Sequence").getModelIndex(); - PDBEntry[] pdbEntriesToView=new PDBEntry[selectedRows.length]; + PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length]; int count = 0; int idColumnIndex = restable.getColumn("Model id").getModelIndex(); int urlColumnIndex = restable.getColumn("Url").getModelIndex(); int typeColumnIndex = restable.getColumn("Provider").getModelIndex(); - int categoryColumnIndex = restable.getColumn("Model Category").getModelIndex(); - - for (int row : selectedRows) + int humanUrl = restable.getColumn("Page URL").getModelIndex(); + int modelformat = restable.getColumn("Model Format").getModelIndex(); + final int up_start_idx = restable.getColumn("Uniprot Start") + .getModelIndex(); + final int up_end_idx = restable.getColumn("Uniprot End") + .getModelIndex(); + int i = 0; + + // bleugh! + Integer[] sellist = new Integer[selectedRows.length]; + for (Integer row : selectedRows) + { + sellist[i++] = row; + } + // Sort rows by coverage + Arrays.sort(sellist, new Comparator() + { + @Override + public int compare(Integer o1, Integer o2) + { + int o1_xt = ((Integer) restable.getValueAt(o1, up_end_idx)) + - (Integer) restable.getValueAt(o1, up_start_idx); + int o2_xt = ((Integer) restable.getValueAt(o2, up_end_idx)) + - (Integer) restable.getValueAt(o2, up_start_idx); + return o2_xt - o1_xt; + } + }); + + for (int row : sellist) { // unique id - could be a horrible hash - - String pdbIdStr = restable.getValueAt(row,idColumnIndex) - .toString(); - String urlStr = restable.getValueAt(row,urlColumnIndex) - .toString(); - String typeColumn = restable.getValueAt(row,typeColumnIndex) + + String pdbIdStr = restable.getValueAt(row, idColumnIndex).toString(); + String urlStr = restable.getValueAt(row, urlColumnIndex).toString(); + String typeColumn = restable.getValueAt(row, typeColumnIndex) .toString(); + String modelPage = humanUrl < 1 ? null + : (String) restable.getValueAt(row, humanUrl); + String strucFormat = restable.getValueAt(row, modelformat).toString(); + SequenceI selectedSeq = (SequenceI) restable.getValueAt(row, refSeqColIndex); selectedSeqsToView.add(selectedSeq); PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr); if (pdbEntry == null) { - pdbEntry = getFindEntry(pdbIdStr, - selectedSeq.getAllPDBEntries()); + pdbEntry = getFindEntry(pdbIdStr, selectedSeq.getAllPDBEntries()); } if (pdbEntry == null) { pdbEntry = new PDBEntry(); pdbEntry.setId(pdbIdStr); - pdbEntry.setType(PDBEntry.Type.MMCIF); + try + { + pdbEntry.setType(PDBEntry.Type.valueOf(strucFormat)); + } catch (Exception q) + { + Cache.warn("Unknown filetype for 3D Beacons Model from: " + + strucFormat + " - " + pdbIdStr + " - " + modelPage); + } + if (!"PDBe".equalsIgnoreCase(typeColumn)) { pdbEntry.setRetrievalUrl(urlStr); } + pdbEntry.setProvider(typeColumn); + pdbEntry.setProviderPage(modelPage); selectedSeq.getDatasetSequence().addPDBId(pdbEntry); } pdbEntriesToView[count++] = pdbEntry; @@ -277,10 +437,132 @@ public class ThreeDBStructureChooserQuerySource return pdbEntriesToView; } - @Override protected FTSRestRequest getLastFTSRequest() { return lastTdbRequest; } + + /** + * generate a query for PDBFTS to retrieve structure metadata + * + * @param ftsRestRequest + * @param upResponse + * @return + */ + + public String buildPDBFTSQueryFor(FTSRestResponse upResponse) + { + Set pdbIds = new HashSet(); + int idx_modelId = getLastFTSRequest().getFieldIndex("Model id"); + int idx_provider = getLastFTSRequest().getFieldIndex("Provider"); + for (FTSData row : upResponse.getSearchSummary()) + { + String id = (String) row.getSummaryData()[idx_modelId]; + String provider = (String) row.getSummaryData()[idx_provider]; + if ("PDBe".equalsIgnoreCase(provider)) + { + pdbIds.add(id); + } + } + return String.join(" OR ", pdbIds).toString(); + } + + /** + * query PDBe for structure metadata + * + * @param pdbquery + * @param upResponse + * @return FTSRestResponse via PDBStructureChooserQuerySource + */ + public FTSRestResponse fetchStructuresMetaDataFor( + PDBStructureChooserQuerySource pdbquery, + FTSRestResponse upResponse) throws Exception + { + + String pdb_Query = buildPDBFTSQueryFor(upResponse); + if (pdb_Query.length() == 0) + { + return null; + } + FTSRestResponse resultList; + FTSRestRequest pdbRequest = new FTSRestRequest(); + pdbRequest.setAllowEmptySeq(false); + pdbRequest.setResponseSize(500); + pdbRequest.setFieldToSearchBy("("); + // pdbRequest.setFieldToSortBy("pdb_id"); + pdbRequest.setWantedFields( + pdbquery.getDocFieldPrefs().getStructureSummaryFields()); + pdbRequest.setSearchTerm(pdb_Query + ")"); + + resultList = pdbquery.executePDBFTSRestRequest(pdbRequest); + + lastPdbRequest = pdbRequest; + return resultList; + } + + public FTSRestResponse joinResponses(FTSRestResponse upResponse, + FTSRestResponse pdbResponse) + { + boolean hasPdbResp = lastPdbRequest != null; + + int idx_provider = getLastFTSRequest().getFieldIndex("Provider"); + // join on + int idx_modelId = getLastFTSRequest().getFieldIndex("Model id"); + int pdbIdx = hasPdbResp ? lastPdbRequest.getFieldIndex("PDB Id") : -1; + int pdbTitle_idx = hasPdbResp ? lastPdbRequest.getFieldIndex("Title") + : -1; + int tdbTitle_idx = getLastFTSRequest().getFieldIndex("Title"); + + for (final FTSData row : upResponse.getSearchSummary()) + { + String id = (String) row.getSummaryData()[idx_modelId]; + String provider = (String) row.getSummaryData()[idx_provider]; + if ("PDBe".equalsIgnoreCase(provider)) + { + if (!hasPdbResp) + { + System.out.println( + "Warning: seems like we couldn't get to the PDBe search interface."); + } + else + { + for (final FTSData pdbrow : pdbResponse.getSearchSummary()) + { + String pdbid = (String) pdbrow.getSummaryData()[pdbIdx]; + if (id.equalsIgnoreCase(pdbid)) + { + row.getSummaryData()[tdbTitle_idx] = pdbrow + .getSummaryData()[pdbTitle_idx]; + } + } + } + + } + else + { + row.getSummaryData()[tdbTitle_idx] = "Model from TDB"; + } + } + return upResponse; + } + + public TDB_FTSData getFTSDataFor(JTable restable, int selectedRow, + Collection discoveredStructuresSet) + { + int idColumnIndex = restable.getColumn("Model id").getModelIndex(); + + String modelId = (String) restable.getValueAt(selectedRow, + idColumnIndex); + for (FTSData row : discoveredStructuresSet) + { + if (row instanceof TDB_FTSData + && ((TDB_FTSData) row).getModelId().equals(modelId)) + { + return ((TDB_FTSData) row); + } + } + return null; + } + } \ No newline at end of file