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.HashSet;
8 import java.util.LinkedHashSet;
10 import java.util.Locale;
13 import javax.swing.JTable;
15 import jalview.bin.Console;
16 import jalview.datamodel.DBRefEntry;
17 import jalview.datamodel.DBRefSource;
18 import jalview.datamodel.PDBEntry;
19 import jalview.datamodel.SequenceI;
20 import jalview.fts.api.FTSData;
21 import jalview.fts.api.FTSDataColumnI;
22 import jalview.fts.api.FTSRestClientI;
23 import jalview.fts.core.FTSDataColumnPreferences;
24 import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
25 import jalview.fts.core.FTSRestRequest;
26 import jalview.fts.core.FTSRestResponse;
27 import jalview.fts.service.threedbeacons.TDB_FTSData;
28 import jalview.fts.service.threedbeacons.TDBeaconsFTSRestClient;
29 import jalview.jbgui.FilterOption;
32 * logic for querying the 3DBeacons API for structures of sequences
36 public class ThreeDBStructureChooserQuerySource
37 extends StructureChooserQuerySource
40 private Set<String> tdBeaconsFilters = null, defaultFilters = null;
42 public static final String FILTER_TDBEACONS_COVERAGE = "3d_beacons_coverage";
44 public static final String FILTER_FIRST_BEST_COVERAGE = "3d_beacons_first_best_coverage";
46 private static final String FILTER_SOURCE_PREFIX = "only_";
48 protected FTSRestRequest lastTdbRequest;
50 protected FTSRestClientI tdbRestClient;
52 private FTSRestRequest lastPdbRequest;
54 public ThreeDBStructureChooserQuerySource()
56 defaultFilters = new LinkedHashSet<String>();
57 defaultFilters.add(FILTER_TDBEACONS_COVERAGE);
58 defaultFilters.add(FILTER_FIRST_BEST_COVERAGE);
60 tdbRestClient = TDBeaconsFTSRestClient.getInstance();
61 docFieldPrefs = new FTSDataColumnPreferences(
62 PreferenceSource.STRUCTURE_CHOOSER,
63 TDBeaconsFTSRestClient.getInstance());
67 * Builds a query string for a given sequences using its DBRef entries 3d
68 * Beacons is only useful for uniprot IDs
71 * the sequences to build a query for
72 * @return the built query string
75 public String buildQuery(SequenceI seq)
77 List<DBRefEntry> refs = seq.getDBRefs();
78 int ib = checkUniprotRefs(refs);
81 return getDBRefId(refs.get(ib));
87 * Searches DBRefEntry for uniprot refs
90 * @return -2 if no uniprot refs, -1 if no canonical ref., otherwise index of
91 * Uniprot canonical DBRefEntry
93 public static int checkUniprotRefs(List<DBRefEntry> refs)
95 boolean hasUniprot = false;
96 if (refs != null && refs.size() != 0)
98 for (int ib = 0, nb = refs.size(); ib < nb; ib++)
100 DBRefEntry dbRef = refs.get(ib);
101 if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT))
104 if (dbRef.isCanonical())
111 return hasUniprot ? -1 : -2;
115 * Ensures sequence ref names are not less than 3 characters and does not
116 * contain a database name
121 static boolean isValidSeqName(String seqName)
123 String ignoreList = "pdb,uniprot,swiss-prot";
124 if (seqName.length() < 3)
128 if (seqName.contains(":"))
132 seqName = seqName.toLowerCase(Locale.ROOT);
133 for (String ignoredEntry : ignoreList.split(","))
135 if (seqName.contains(ignoredEntry))
143 static String getDBRefId(DBRefEntry dbRef)
145 String ref = dbRef.getAccessionId().replaceAll("GO:", "");
150 * FTSRestClient specific query builder to recover associated structure data
151 * records for a sequence
154 * - seq to generate a query for
155 * @param wantedFields
156 * - fields to retrieve
157 * @param selectedFilterOpt
158 * - criterion for ranking results (e.g. resolution)
160 * - sort ascending or descending
164 public FTSRestResponse fetchStructuresMetaData(SequenceI seq,
165 Collection<FTSDataColumnI> wantedFields,
166 FilterOption selectedFilterOpt, boolean b) throws Exception
168 FTSRestResponse resultList;
169 if (selectedFilterOpt != null
170 && tdBeaconsFilter(selectedFilterOpt.getValue()))
172 FTSRestRequest tdbRequest = getTDBeaconsRequest(seq, wantedFields);
173 resultList = tdbRestClient.executeRequest(tdbRequest);
175 lastTdbRequest = tdbRequest;
176 if (resultList != null)
177 { // Query the PDB and add additional metadata
178 FTSRestResponse pdbResponse = fetchStructuresMetaDataFor(
179 getPDBQuerySource(), resultList);
181 resultList = joinResponses(resultList, pdbResponse);
185 // use the PDBFTS directly
186 resultList = getPDBQuerySource().fetchStructuresMetaData(seq,
187 wantedFields, selectedFilterOpt, b);
188 lastTdbRequest = getPDBQuerySource().lastPdbRequest;
189 lastPdbRequest = lastTdbRequest; // both queries the same - indicates we
195 PDBStructureChooserQuerySource pdbQuerySource = null;
197 private PDBStructureChooserQuerySource getPDBQuerySource()
199 if (pdbQuerySource == null)
201 pdbQuerySource = new PDBStructureChooserQuerySource();
203 return pdbQuerySource;
206 private FTSRestRequest getTDBeaconsRequest(SequenceI seq,
207 Collection<FTSDataColumnI> wantedFields)
209 FTSRestRequest pdbRequest = new FTSRestRequest();
210 pdbRequest.setAllowEmptySeq(false);
211 pdbRequest.setResponseSize(500);
212 pdbRequest.setWantedFields(wantedFields);
213 String query = buildQuery(seq);
218 pdbRequest.setSearchTerm(query + ".json");
219 pdbRequest.setAssociatedSequence(seq);
224 public List<FilterOption> getAvailableFilterOptions(String VIEWS_FILTER)
226 List<FilterOption> filters = getPDBQuerySource()
227 .getAvailableFilterOptions(VIEWS_FILTER);
228 tdBeaconsFilters = new LinkedHashSet<String>();
229 tdBeaconsFilters.addAll(defaultFilters);
230 filters.add(0, new FilterOption("Best 3D-Beacons Coverage",
231 FILTER_FIRST_BEST_COVERAGE, VIEWS_FILTER, false, this));
232 filters.add(1, new FilterOption("Multiple 3D-Beacons Coverage",
233 FILTER_TDBEACONS_COVERAGE, VIEWS_FILTER, true, this));
239 public void updateAvailableFilterOptions(String VIEWS_FILTER,
240 List<FilterOption> xtantOptions, Collection<FTSData> tdbEntries)
242 if (tdbEntries != null && lastTdbRequest != null)
244 boolean hasPDBe = false;
245 for (FTSData _row : tdbEntries)
247 // tdb returns custom object
248 TDB_FTSData row = (TDB_FTSData) _row;
249 String provider = (String) row.getProvider();
250 FilterOption providerOpt = new FilterOption(
251 "3DB Provider - " + provider,
252 FILTER_SOURCE_PREFIX + provider, VIEWS_FILTER, false, this);
253 if (!xtantOptions.contains(providerOpt))
255 xtantOptions.add(1, providerOpt);
256 tdBeaconsFilters.add(FILTER_SOURCE_PREFIX + provider);
257 if ("PDBe".equalsIgnoreCase(provider))
265 // remove the PDBe options from the available filters
267 while (op < xtantOptions.size())
269 FilterOption filter = xtantOptions.get(op);
271 .getQuerySource() instanceof PDBStructureChooserQuerySource)
273 xtantOptions.remove(op);
285 private boolean tdBeaconsFilter(String fieldToFilterBy)
287 return tdBeaconsFilters != null
288 && tdBeaconsFilters.contains(fieldToFilterBy);
291 private String remove_prefix(String fieldToFilterBy)
293 if (tdBeaconsFilters != null
294 && tdBeaconsFilters.contains(fieldToFilterBy)
295 && !defaultFilters.contains(fieldToFilterBy))
297 return fieldToFilterBy.substring(FILTER_SOURCE_PREFIX.length());
306 public boolean needsRefetch(FilterOption selectedFilterOpt)
308 return selectedFilterOpt == null
309 || !tdBeaconsFilter(selectedFilterOpt.getValue())
310 && lastPdbRequest != lastTdbRequest;
314 * FTSRestClient specific query builder to pick top ranked entry from a
315 * fetchStructuresMetaData query
318 * - seq to generate a query for
319 * @param wantedFields
320 * - fields to retrieve
321 * @param selectedFilterOpt
322 * - criterion for ranking results (e.g. resolution)
324 * - sort ascending or descending
328 public FTSRestResponse selectFirstRankedQuery(SequenceI seq,
329 Collection<FTSData> collectedResults,
330 Collection<FTSDataColumnI> wantedFields, String fieldToFilterBy,
331 boolean b) throws Exception
333 if (fieldToFilterBy != null && tdBeaconsFilter(fieldToFilterBy))
335 TDBResultAnalyser analyser = new TDBResultAnalyser(seq,
336 collectedResults, lastTdbRequest, fieldToFilterBy,
337 remove_prefix(fieldToFilterBy));
339 FTSRestResponse resultList = new FTSRestResponse();
341 List<FTSData> filteredResponse = analyser.getFilteredResponse();
343 List<FTSData> selectedStructures = analyser
344 .selectStructures(filteredResponse);
345 resultList.setNumberOfItemsFound(selectedStructures.size());
346 resultList.setSearchSummary(selectedStructures);
349 // Fall back to PDBe rankings
350 return getPDBQuerySource().selectFirstRankedQuery(seq, collectedResults,
351 wantedFields, fieldToFilterBy, b);
355 public PDBEntry[] collectSelectedRows(JTable restable, int[] selectedRows,
356 List<SequenceI> selectedSeqsToView)
358 int refSeqColIndex = restable.getColumn("Ref Sequence").getModelIndex();
360 PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
362 int idColumnIndex = restable.getColumn("Model id").getModelIndex();
363 int urlColumnIndex = restable.getColumn("Url").getModelIndex();
364 int typeColumnIndex = restable.getColumn("Provider").getModelIndex();
365 int humanUrl = restable.getColumn("Page URL").getModelIndex();
366 int modelformat = restable.getColumn("Model Format").getModelIndex();
367 final int up_start_idx = restable.getColumn("Uniprot Start")
369 final int up_end_idx = restable.getColumn("Uniprot End")
374 Integer[] sellist = new Integer[selectedRows.length];
375 for (Integer row : selectedRows)
379 // Sort rows by coverage
380 Arrays.sort(sellist, new Comparator<Integer>()
383 public int compare(Integer o1, Integer o2)
385 int o1_xt = ((Integer) restable.getValueAt(o1, up_end_idx))
386 - (Integer) restable.getValueAt(o1, up_start_idx);
387 int o2_xt = ((Integer) restable.getValueAt(o2, up_end_idx))
388 - (Integer) restable.getValueAt(o2, up_start_idx);
389 return o2_xt - o1_xt;
393 for (int row : sellist)
395 // unique id - could be a horrible hash
397 String pdbIdStr = restable.getValueAt(row, idColumnIndex).toString();
398 String urlStr = restable.getValueAt(row, urlColumnIndex).toString();
399 String typeColumn = restable.getValueAt(row, typeColumnIndex)
401 String modelPage = humanUrl < 1 ? null
402 : (String) restable.getValueAt(row, humanUrl);
403 String strucFormat = restable.getValueAt(row, modelformat).toString();
405 SequenceI selectedSeq = (SequenceI) restable.getValueAt(row,
407 selectedSeqsToView.add(selectedSeq);
408 PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
409 if (pdbEntry == null)
411 pdbEntry = getFindEntry(pdbIdStr, selectedSeq.getAllPDBEntries());
414 if (pdbEntry == null)
416 pdbEntry = new PDBEntry();
417 pdbEntry.setId(pdbIdStr);
418 pdbEntry.setAuthoritative(true);
421 pdbEntry.setType(PDBEntry.Type.valueOf(strucFormat));
422 } catch (Exception q)
424 Console.warn("Unknown filetype for 3D Beacons Model from: "
425 + strucFormat + " - " + pdbIdStr + " - " + modelPage);
428 if (!"PDBe".equalsIgnoreCase(typeColumn))
430 pdbEntry.setRetrievalUrl(urlStr);
432 pdbEntry.setProvider(typeColumn);
433 pdbEntry.setProviderPage(modelPage);
434 selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
436 pdbEntriesToView[count++] = pdbEntry;
438 return pdbEntriesToView;
442 protected FTSRestRequest getLastFTSRequest()
444 return lastTdbRequest;
448 * generate a query for PDBFTS to retrieve structure metadata
450 * @param ftsRestRequest
455 public String buildPDBFTSQueryFor(FTSRestResponse upResponse)
457 Set<String> pdbIds = new HashSet<String>();
458 int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
459 int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
460 for (FTSData row : upResponse.getSearchSummary())
462 String id = (String) row.getSummaryData()[idx_modelId];
463 String provider = (String) row.getSummaryData()[idx_provider];
464 if ("PDBe".equalsIgnoreCase(provider))
469 return String.join(" OR ", pdbIds).toString();
473 * query PDBe for structure metadata
477 * @return FTSRestResponse via PDBStructureChooserQuerySource
479 public FTSRestResponse fetchStructuresMetaDataFor(
480 PDBStructureChooserQuerySource pdbquery,
481 FTSRestResponse upResponse) throws Exception
484 String pdb_Query = buildPDBFTSQueryFor(upResponse);
485 if (pdb_Query.length() == 0)
489 FTSRestResponse resultList;
490 FTSRestRequest pdbRequest = new FTSRestRequest();
491 pdbRequest.setAllowEmptySeq(false);
492 pdbRequest.setResponseSize(500);
493 pdbRequest.setFieldToSearchBy("(");
494 // pdbRequest.setFieldToSortBy("pdb_id");
495 pdbRequest.setWantedFields(
496 pdbquery.getDocFieldPrefs().getStructureSummaryFields());
497 pdbRequest.setSearchTerm(pdb_Query + ")");
499 resultList = pdbquery.executePDBFTSRestRequest(pdbRequest);
501 lastPdbRequest = pdbRequest;
505 public FTSRestResponse joinResponses(FTSRestResponse upResponse,
506 FTSRestResponse pdbResponse)
508 boolean hasPdbResp = lastPdbRequest != null;
510 int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
512 int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
513 int pdbIdx = hasPdbResp ? lastPdbRequest.getFieldIndex("PDB Id") : -1;
514 int pdbTitle_idx = hasPdbResp ? lastPdbRequest.getFieldIndex("Title")
516 int tdbTitle_idx = getLastFTSRequest().getFieldIndex("Title");
518 for (final FTSData row : upResponse.getSearchSummary())
520 String id = (String) row.getSummaryData()[idx_modelId];
521 String provider = (String) row.getSummaryData()[idx_provider];
522 if ("PDBe".equalsIgnoreCase(provider))
527 "Warning: seems like we couldn't get to the PDBe search interface.");
531 for (final FTSData pdbrow : pdbResponse.getSearchSummary())
533 String pdbid = (String) pdbrow.getSummaryData()[pdbIdx];
534 if (id.equalsIgnoreCase(pdbid))
536 row.getSummaryData()[tdbTitle_idx] = pdbrow
537 .getSummaryData()[pdbTitle_idx];
545 row.getSummaryData()[tdbTitle_idx] = "Model from TDB";
551 public TDB_FTSData getFTSDataFor(JTable restable, int selectedRow,
552 Collection<FTSData> discoveredStructuresSet)
554 int idColumnIndex = restable.getColumn("Model id").getModelIndex();
556 String modelId = (String) restable.getValueAt(selectedRow,
558 for (FTSData row : discoveredStructuresSet)
560 if (row instanceof TDB_FTSData
561 && ((TDB_FTSData) row).getModelId().equals(modelId))
563 return ((TDB_FTSData) row);