1 package jalview.gui.structurechooser;
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collection;
6 import java.util.Comparator;
7 import java.util.LinkedHashSet;
9 import java.util.Locale;
12 import javax.swing.JTable;
14 import jalview.bin.Cache;
15 import jalview.datamodel.DBRefEntry;
16 import jalview.datamodel.DBRefSource;
17 import jalview.datamodel.PDBEntry;
18 import jalview.datamodel.SequenceI;
19 import jalview.fts.api.FTSData;
20 import jalview.fts.api.FTSDataColumnI;
21 import jalview.fts.api.FTSRestClientI;
22 import jalview.fts.core.FTSDataColumnPreferences;
23 import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
24 import jalview.fts.core.FTSRestRequest;
25 import jalview.fts.core.FTSRestResponse;
26 import jalview.fts.service.threedbeacons.TDB_FTSData;
27 import jalview.fts.service.threedbeacons.TDBeaconsFTSRestClient;
28 import jalview.jbgui.FilterOption;
31 * logic for querying the 3DBeacons API for structures of sequences
35 public class ThreeDBStructureChooserQuerySource
36 extends StructureChooserQuerySource
39 private Set<String> tdBeaconsFilters = null, defaultFilters = null;
41 public static final String FILTER_TDBEACONS_COVERAGE = "3d_beacons_coverage";
43 public static final String FILTER_FIRST_BEST_COVERAGE = "3d_beacons_first_best_coverage";
45 private static final String FILTER_SOURCE_PREFIX = "only_";
47 protected FTSRestRequest lastTdbRequest;
49 protected FTSRestClientI tdbRestClient;
51 private FTSRestRequest lastPdbRequest;
53 public ThreeDBStructureChooserQuerySource()
55 defaultFilters = new LinkedHashSet<String>();
56 defaultFilters.add(FILTER_TDBEACONS_COVERAGE);
57 defaultFilters.add(FILTER_FIRST_BEST_COVERAGE);
59 tdbRestClient = TDBeaconsFTSRestClient.getInstance();
60 docFieldPrefs = new FTSDataColumnPreferences(
61 PreferenceSource.STRUCTURE_CHOOSER,
62 TDBeaconsFTSRestClient.getInstance());
66 * Builds a query string for a given sequences using its DBRef entries 3d
67 * Beacons is only useful for uniprot IDs
70 * the sequences to build a query for
71 * @return the built query string
74 public String buildQuery(SequenceI seq)
76 List<DBRefEntry> refs = seq.getDBRefs();
77 int ib = checkUniprotRefs(refs);
80 return getDBRefId(refs.get(ib));
86 * Searches DBRefEntry for uniprot refs
89 * @return -2 if no uniprot refs, -1 if no canonical ref., otherwise index of
90 * Uniprot canonical DBRefEntry
92 public static int checkUniprotRefs(List<DBRefEntry> refs)
94 boolean hasUniprot = false;
95 if (refs != null && refs.size() != 0)
97 for (int ib = 0, nb = refs.size(); ib < nb; ib++)
99 DBRefEntry dbRef = refs.get(ib);
100 if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT))
103 if (dbRef.isCanonical())
110 return hasUniprot ? -1 : -2;
114 * Ensures sequence ref names are not less than 3 characters and does not
115 * contain a database name
120 static boolean isValidSeqName(String seqName)
122 String ignoreList = "pdb,uniprot,swiss-prot";
123 if (seqName.length() < 3)
127 if (seqName.contains(":"))
131 seqName = seqName.toLowerCase(Locale.ROOT);
132 for (String ignoredEntry : ignoreList.split(","))
134 if (seqName.contains(ignoredEntry))
142 static String getDBRefId(DBRefEntry dbRef)
144 String ref = dbRef.getAccessionId().replaceAll("GO:", "");
149 * FTSRestClient specific query builder to recover associated structure data
150 * records for a sequence
153 * - seq to generate a query for
154 * @param wantedFields
155 * - fields to retrieve
156 * @param selectedFilterOpt
157 * - criterion for ranking results (e.g. resolution)
159 * - sort ascending or descending
163 public FTSRestResponse fetchStructuresMetaData(SequenceI seq,
164 Collection<FTSDataColumnI> wantedFields,
165 FilterOption selectedFilterOpt, boolean b) throws Exception
167 FTSRestResponse resultList;
168 if (selectedFilterOpt != null
169 && tdBeaconsFilter(selectedFilterOpt.getValue()))
171 FTSRestRequest tdbRequest = getTDBeaconsRequest(seq, wantedFields);
172 resultList = tdbRestClient.executeRequest(tdbRequest);
174 lastTdbRequest = tdbRequest;
175 if (resultList != null)
176 { // Query the PDB and add additional metadata
177 FTSRestResponse pdbResponse = fetchStructuresMetaDataFor(
178 getPDBQuerySource(), resultList);
180 resultList = joinResponses(resultList, pdbResponse);
184 // use the PDBFTS directly
185 resultList = getPDBQuerySource().fetchStructuresMetaData(seq,
186 wantedFields, selectedFilterOpt, b);
187 lastTdbRequest = getPDBQuerySource().lastPdbRequest;
188 lastPdbRequest = lastTdbRequest; // both queries the same - indicates we
194 PDBStructureChooserQuerySource pdbQuerySource = null;
196 private PDBStructureChooserQuerySource getPDBQuerySource()
198 if (pdbQuerySource == null)
200 pdbQuerySource = new PDBStructureChooserQuerySource();
202 return pdbQuerySource;
205 private FTSRestRequest getTDBeaconsRequest(SequenceI seq,
206 Collection<FTSDataColumnI> wantedFields)
208 FTSRestRequest pdbRequest = new FTSRestRequest();
209 pdbRequest.setAllowEmptySeq(false);
210 pdbRequest.setResponseSize(500);
211 pdbRequest.setWantedFields(wantedFields);
212 String query = buildQuery(seq);
217 pdbRequest.setSearchTerm(query + ".json");
218 pdbRequest.setAssociatedSequence(seq);
223 public List<FilterOption> getAvailableFilterOptions(String VIEWS_FILTER)
225 List<FilterOption> filters = getPDBQuerySource()
226 .getAvailableFilterOptions(VIEWS_FILTER);
227 tdBeaconsFilters = new LinkedHashSet<String>();
228 tdBeaconsFilters.addAll(defaultFilters);
229 filters.add(0, new FilterOption("Best 3D-Beacons Coverage",
230 FILTER_FIRST_BEST_COVERAGE, VIEWS_FILTER, false, this));
231 filters.add(1, new FilterOption("Multiple 3D-Beacons Coverage",
232 FILTER_TDBEACONS_COVERAGE, VIEWS_FILTER, true, this));
238 public void updateAvailableFilterOptions(String VIEWS_FILTER,
239 List<FilterOption> xtantOptions, Collection<FTSData> tdbEntries)
241 if (tdbEntries != null && lastTdbRequest != null)
243 boolean hasPDBe = false;
244 for (FTSData _row : tdbEntries)
246 // tdb returns custom object
247 TDB_FTSData row = (TDB_FTSData) _row;
248 String provider = (String) row.getProvider();
249 FilterOption providerOpt = new FilterOption(
250 "3DB Provider - " + provider,
251 FILTER_SOURCE_PREFIX + provider, VIEWS_FILTER, false, this);
252 if (!xtantOptions.contains(providerOpt))
254 xtantOptions.add(1, providerOpt);
255 tdBeaconsFilters.add(FILTER_SOURCE_PREFIX + provider);
256 if ("PDBe".equalsIgnoreCase(provider))
264 // remove the PDBe options from the available filters
266 while (op < xtantOptions.size())
268 FilterOption filter = xtantOptions.get(op);
270 .getQuerySource() instanceof PDBStructureChooserQuerySource)
272 xtantOptions.remove(op);
284 private boolean tdBeaconsFilter(String fieldToFilterBy)
286 return tdBeaconsFilters != null
287 && tdBeaconsFilters.contains(fieldToFilterBy);
290 private String remove_prefix(String fieldToFilterBy)
292 if (tdBeaconsFilters != null
293 && tdBeaconsFilters.contains(fieldToFilterBy)
294 && !defaultFilters.contains(fieldToFilterBy))
296 return fieldToFilterBy.substring(FILTER_SOURCE_PREFIX.length());
305 public boolean needsRefetch(FilterOption selectedFilterOpt)
307 return selectedFilterOpt == null
308 || !tdBeaconsFilter(selectedFilterOpt.getValue())
309 && lastPdbRequest != lastTdbRequest;
313 * FTSRestClient specific query builder to pick top ranked entry from a
314 * fetchStructuresMetaData query
317 * - seq to generate a query for
318 * @param wantedFields
319 * - fields to retrieve
320 * @param selectedFilterOpt
321 * - criterion for ranking results (e.g. resolution)
323 * - sort ascending or descending
327 public FTSRestResponse selectFirstRankedQuery(SequenceI seq,
328 Collection<FTSData> collectedResults,
329 Collection<FTSDataColumnI> wantedFields, String fieldToFilterBy,
330 boolean b) throws Exception
332 if (fieldToFilterBy != null && tdBeaconsFilter(fieldToFilterBy))
334 TDBResultAnalyser analyser = new TDBResultAnalyser(seq,
335 collectedResults, lastTdbRequest, fieldToFilterBy,
336 remove_prefix(fieldToFilterBy));
338 FTSRestResponse resultList = new FTSRestResponse();
340 List<FTSData> filteredResponse = analyser.getFilteredResponse();
342 List<FTSData> selectedStructures = analyser
343 .selectStructures(filteredResponse);
344 resultList.setNumberOfItemsFound(selectedStructures.size());
345 resultList.setSearchSummary(selectedStructures);
348 // Fall back to PDBe rankings
349 return getPDBQuerySource().selectFirstRankedQuery(seq, collectedResults,
350 wantedFields, fieldToFilterBy, b);
354 public PDBEntry[] collectSelectedRows(JTable restable, int[] selectedRows,
355 List<SequenceI> selectedSeqsToView)
357 int refSeqColIndex = restable.getColumn("Ref Sequence").getModelIndex();
359 PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
361 int idColumnIndex = restable.getColumn("Model id").getModelIndex();
362 int urlColumnIndex = restable.getColumn("Url").getModelIndex();
363 int typeColumnIndex = restable.getColumn("Provider").getModelIndex();
364 int humanUrl = restable.getColumn("Page URL").getModelIndex();
365 int modelformat = restable.getColumn("Model Format").getModelIndex();
366 final int up_start_idx = restable.getColumn("Uniprot Start")
368 final int up_end_idx = restable.getColumn("Uniprot End")
373 Integer[] sellist = new Integer[selectedRows.length];
374 for (Integer row : selectedRows)
378 // Sort rows by coverage
379 Arrays.sort(sellist, new Comparator<Integer>()
382 public int compare(Integer o1, Integer o2)
384 int o1_xt = ((Integer) restable.getValueAt(o1, up_end_idx))
385 - (Integer) restable.getValueAt(o1, up_start_idx);
386 int o2_xt = ((Integer) restable.getValueAt(o2, up_end_idx))
387 - (Integer) restable.getValueAt(o2, up_start_idx);
388 return o2_xt - o1_xt;
392 for (int row : sellist)
394 // unique id - could be a horrible hash
396 String pdbIdStr = restable.getValueAt(row, idColumnIndex).toString();
397 String urlStr = restable.getValueAt(row, urlColumnIndex).toString();
398 String typeColumn = restable.getValueAt(row, typeColumnIndex)
400 String modelPage = humanUrl < 1 ? null
401 : (String) restable.getValueAt(row, humanUrl);
402 String strucFormat = restable.getValueAt(row, modelformat).toString();
404 SequenceI selectedSeq = (SequenceI) restable.getValueAt(row,
406 selectedSeqsToView.add(selectedSeq);
407 PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
408 if (pdbEntry == null)
410 pdbEntry = getFindEntry(pdbIdStr, selectedSeq.getAllPDBEntries());
413 if (pdbEntry == null)
415 pdbEntry = new PDBEntry();
416 pdbEntry.setId(pdbIdStr);
419 pdbEntry.setType(PDBEntry.Type.valueOf(strucFormat));
420 } catch (Exception q)
422 Cache.log.warn("Unknown filetype for 3D Beacons Model from: "
423 + strucFormat + " - " + pdbIdStr + " - " + modelPage);
426 if (!"PDBe".equalsIgnoreCase(typeColumn))
428 pdbEntry.setRetrievalUrl(urlStr);
430 pdbEntry.setProvider(typeColumn);
431 pdbEntry.setProviderPage(modelPage);
432 selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
434 pdbEntriesToView[count++] = pdbEntry;
436 return pdbEntriesToView;
440 protected FTSRestRequest getLastFTSRequest()
442 return lastTdbRequest;
446 * generate a query for PDBFTS to retrieve structure metadata
448 * @param ftsRestRequest
453 public String buildPDBFTSQueryFor(FTSRestResponse upResponse)
455 List<String> pdbIds = new ArrayList<String>();
456 int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
457 int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
458 for (FTSData row : upResponse.getSearchSummary())
460 String id = (String) row.getSummaryData()[idx_modelId];
461 String provider = (String) row.getSummaryData()[idx_provider];
462 if ("PDBe".equalsIgnoreCase(provider))
467 return String.join(" OR ", pdbIds).toString();
471 * query PDBe for structure metadata
475 * @return FTSRestResponse via PDBStructureChooserQuerySource
477 public FTSRestResponse fetchStructuresMetaDataFor(
478 PDBStructureChooserQuerySource pdbquery,
479 FTSRestResponse upResponse) throws Exception
482 String pdb_Query = buildPDBFTSQueryFor(upResponse);
483 if (pdb_Query.length() == 0)
487 FTSRestResponse resultList;
488 FTSRestRequest pdbRequest = new FTSRestRequest();
489 pdbRequest.setAllowEmptySeq(false);
490 pdbRequest.setResponseSize(500);
491 pdbRequest.setFieldToSearchBy("(");
492 // pdbRequest.setFieldToSortBy("pdb_id");
493 pdbRequest.setWantedFields(
494 pdbquery.getDocFieldPrefs().getStructureSummaryFields());
495 pdbRequest.setSearchTerm(pdb_Query + ")");
497 resultList = pdbquery.executePDBFTSRestRequest(pdbRequest);
499 lastPdbRequest = pdbRequest;
503 public FTSRestResponse joinResponses(FTSRestResponse upResponse,
504 FTSRestResponse pdbResponse)
506 boolean hasPdbResp = lastPdbRequest != null;
508 int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
510 int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
511 int pdbIdx = hasPdbResp ? lastPdbRequest.getFieldIndex("PDB Id") : -1;
512 int pdbTitle_idx = hasPdbResp ? lastPdbRequest.getFieldIndex("Title")
514 int tdbTitle_idx = getLastFTSRequest().getFieldIndex("Title");
516 for (final FTSData row : upResponse.getSearchSummary())
518 String id = (String) row.getSummaryData()[idx_modelId];
519 String provider = (String) row.getSummaryData()[idx_provider];
520 if ("PDBe".equalsIgnoreCase(provider))
525 "Warning: seems like we couldn't get to the PDBe search interface.");
529 for (final FTSData pdbrow : pdbResponse.getSearchSummary())
531 String pdbid = (String) pdbrow.getSummaryData()[pdbIdx];
532 if (id.equalsIgnoreCase(pdbid))
534 row.getSummaryData()[tdbTitle_idx] = pdbrow
535 .getSummaryData()[pdbTitle_idx];
543 row.getSummaryData()[tdbTitle_idx] = "Model from TDB";
549 public TDB_FTSData getFTSDataFor(JTable restable, int selectedRow,
550 Collection<FTSData> discoveredStructuresSet)
552 int idColumnIndex = restable.getColumn("Model id").getModelIndex();
554 String modelId = (String) restable.getValueAt(selectedRow,
556 for (FTSData row : discoveredStructuresSet)
558 if (row instanceof TDB_FTSData
559 && ((TDB_FTSData) row).getModelId().equals(modelId))
561 return ((TDB_FTSData) row);