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.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 private static int MAX_QLENGTH = 7820;
49 protected FTSRestRequest lastTdbRequest;
51 protected FTSRestClientI tdbRestClient;
53 private FTSRestRequest lastPdbRequest;
55 public ThreeDBStructureChooserQuerySource()
57 defaultFilters = new LinkedHashSet<String>();
58 defaultFilters.add(FILTER_TDBEACONS_COVERAGE);
59 defaultFilters.add(FILTER_FIRST_BEST_COVERAGE);
61 tdbRestClient = TDBeaconsFTSRestClient.getInstance();
62 docFieldPrefs = new FTSDataColumnPreferences(
63 PreferenceSource.STRUCTURE_CHOOSER,
64 TDBeaconsFTSRestClient.getInstance());
68 * Builds a query string for a given sequences using its DBRef entries 3d
69 * Beacons is only useful for uniprot IDs
72 * the sequences to build a query for
73 * @return the built query string
76 public String buildQuery(SequenceI seq)
78 boolean isPDBRefsFound = false;
79 boolean isUniProtRefsFound = false;
80 StringBuilder queryBuilder = new StringBuilder();
81 Set<String> seqRefs = new LinkedHashSet<>();
84 * note PDBs as DBRefEntry so they are not duplicated in query
86 Set<String> pdbids = new HashSet<>();
88 List<DBRefEntry> refs = seq.getDBRefs();
89 int ib = checkUniprotRefs(refs);
92 return getDBRefId(refs.get(ib));
98 * Searches DBRefEntry for uniprot refs
100 * @return -2 if no uniprot refs, -1 if no canonical ref., otherwise index of Uniprot canonical DBRefEntry
102 public static int checkUniprotRefs(List<DBRefEntry> refs)
104 boolean hasUniprot = false;
105 if (refs != null && refs.size() != 0)
107 for (int ib = 0, nb = refs.size(); ib < nb; ib++)
109 DBRefEntry dbRef = refs.get(ib);
110 if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT))
113 if (dbRef.isCanonical())
120 return hasUniprot ? -1 : -2;
124 * Ensures sequence ref names are not less than 3 characters and does not
125 * contain a database name
130 static boolean isValidSeqName(String seqName)
132 // System.out.println("seqName : " + seqName);
133 String ignoreList = "pdb,uniprot,swiss-prot";
134 if (seqName.length() < 3)
138 if (seqName.contains(":"))
142 seqName = seqName.toLowerCase(Locale.ROOT);
143 for (String ignoredEntry : ignoreList.split(","))
145 if (seqName.contains(ignoredEntry))
153 static String getDBRefId(DBRefEntry dbRef)
155 String ref = dbRef.getAccessionId().replaceAll("GO:", "");
160 * FTSRestClient specific query builder to recover associated structure data
161 * records for a sequence
164 * - seq to generate a query for
165 * @param wantedFields
166 * - fields to retrieve
167 * @param selectedFilterOpt
168 * - criterion for ranking results (e.g. resolution)
170 * - sort ascending or descending
174 public FTSRestResponse fetchStructuresMetaData(SequenceI seq,
175 Collection<FTSDataColumnI> wantedFields,
176 FilterOption selectedFilterOpt, boolean b) throws Exception
178 FTSRestResponse resultList;
179 if (selectedFilterOpt != null
180 && tdBeaconsFilter(selectedFilterOpt.getValue()))
182 FTSRestRequest tdbRequest = getTDBeaconsRequest(seq, wantedFields);
183 resultList = tdbRestClient.executeRequest(tdbRequest);
185 lastTdbRequest = tdbRequest;
186 if (resultList!=null)
187 { // Query the PDB and add additional metadata
188 FTSRestResponse pdbResponse = fetchStructuresMetaDataFor(
189 getPDBQuerySource(), resultList);
190 FTSRestResponse joinedResp = joinResponses(resultList, pdbResponse);
194 // use the PDBFTS directly
195 resultList = getPDBQuerySource().fetchStructuresMetaData(seq,
196 wantedFields, selectedFilterOpt, b);
197 lastTdbRequest = getPDBQuerySource().lastPdbRequest;
198 lastPdbRequest = lastTdbRequest; // both queries the same - indicates we
204 PDBStructureChooserQuerySource pdbQuerySource = null;
206 private PDBStructureChooserQuerySource getPDBQuerySource()
208 if (pdbQuerySource == null)
210 pdbQuerySource = new PDBStructureChooserQuerySource();
212 return pdbQuerySource;
215 private FTSRestRequest getTDBeaconsRequest(SequenceI seq,
216 Collection<FTSDataColumnI> wantedFields)
218 FTSRestRequest pdbRequest = new FTSRestRequest();
219 pdbRequest.setAllowEmptySeq(false);
220 pdbRequest.setResponseSize(500);
221 pdbRequest.setWantedFields(wantedFields);
222 String query = buildQuery(seq);
227 pdbRequest.setSearchTerm(query + ".json");
228 pdbRequest.setAssociatedSequence(seq);
233 public List<FilterOption> getAvailableFilterOptions(String VIEWS_FILTER)
235 List<FilterOption> filters = getPDBQuerySource()
236 .getAvailableFilterOptions(VIEWS_FILTER);
237 tdBeaconsFilters = new LinkedHashSet<String>();
238 tdBeaconsFilters.addAll(defaultFilters);
239 filters.add(0, new FilterOption("Best 3D-Beacons Coverage",
240 FILTER_FIRST_BEST_COVERAGE, VIEWS_FILTER, false, this));
241 filters.add(1, new FilterOption("Multiple 3D-Beacons Coverage",
242 FILTER_TDBEACONS_COVERAGE, VIEWS_FILTER, true, this));
248 public void updateAvailableFilterOptions(String VIEWS_FILTER,
249 List<FilterOption> xtantOptions, Collection<FTSData> tdbEntries)
251 if (tdbEntries != null && lastTdbRequest != null)
253 int prov_idx = lastTdbRequest.getFieldIndex("Provider");
254 boolean hasPDBe=false;
255 for (FTSData _row : tdbEntries)
257 // tdb returns custom object
258 TDB_FTSData row = (TDB_FTSData) _row;
259 String provider = (String) row.getProvider();
260 FilterOption providerOpt = new FilterOption(
261 "3DB Provider - " + provider,
262 FILTER_SOURCE_PREFIX + provider, VIEWS_FILTER, false, this);
263 if (!xtantOptions.contains(providerOpt))
265 xtantOptions.add(1, providerOpt);
266 tdBeaconsFilters.add(FILTER_SOURCE_PREFIX + provider);
267 if ("PDBe".equalsIgnoreCase(provider))
275 // remove the PDBe options from the available filters
277 while (op<xtantOptions.size())
279 FilterOption filter = xtantOptions.get(op);
280 if (filter.getQuerySource() instanceof PDBStructureChooserQuerySource)
282 xtantOptions.remove(op);
292 private boolean tdBeaconsFilter(String fieldToFilterBy)
294 return tdBeaconsFilters != null
295 && tdBeaconsFilters.contains(fieldToFilterBy);
298 private String remove_prefix(String fieldToFilterBy)
300 if (tdBeaconsFilters != null
301 && tdBeaconsFilters.contains(fieldToFilterBy)
302 && !defaultFilters.contains(fieldToFilterBy))
304 return fieldToFilterBy.substring(FILTER_SOURCE_PREFIX.length());
313 public boolean needsRefetch(FilterOption selectedFilterOpt)
315 return selectedFilterOpt == null
316 || !tdBeaconsFilter(selectedFilterOpt.getValue())
317 && lastPdbRequest != lastTdbRequest;
321 * FTSRestClient specific query builder to pick top ranked entry from a
322 * fetchStructuresMetaData query
325 * - seq to generate a query for
326 * @param wantedFields
327 * - fields to retrieve
328 * @param selectedFilterOpt
329 * - criterion for ranking results (e.g. resolution)
331 * - sort ascending or descending
335 public FTSRestResponse selectFirstRankedQuery(SequenceI seq,
336 Collection<FTSData> collectedResults,
337 Collection<FTSDataColumnI> wantedFields, String fieldToFilterBy,
338 boolean b) throws Exception
340 if (fieldToFilterBy != null && tdBeaconsFilter(fieldToFilterBy))
342 TDBResultAnalyser analyser = new TDBResultAnalyser(seq,
343 collectedResults, lastTdbRequest, fieldToFilterBy,
344 remove_prefix(fieldToFilterBy));
346 FTSRestResponse resultList = new FTSRestResponse();
348 List<FTSData> filteredResponse = analyser.getFilteredResponse();
350 List<FTSData> selectedStructures = analyser
351 .selectStructures(filteredResponse);
352 resultList.setNumberOfItemsFound(selectedStructures.size());
353 resultList.setSearchSummary(selectedStructures);
356 // Fall back to PDBe rankings
357 return getPDBQuerySource().selectFirstRankedQuery(seq, collectedResults,
358 wantedFields, fieldToFilterBy, b);
362 public PDBEntry[] collectSelectedRows(JTable restable, int[] selectedRows,
363 List<SequenceI> selectedSeqsToView)
365 int refSeqColIndex = restable.getColumn("Ref Sequence").getModelIndex();
367 PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
369 int idColumnIndex = restable.getColumn("Model id").getModelIndex();
370 int urlColumnIndex = restable.getColumn("Url").getModelIndex();
371 int typeColumnIndex = restable.getColumn("Provider").getModelIndex();
372 int humanUrl = restable.getColumn("Page URL").getModelIndex();
373 int categoryColumnIndex = restable.getColumn("Model Category")
375 final int up_start_idx = restable.getColumn("Uniprot Start")
377 final int up_end_idx = restable.getColumn("Uniprot End")
382 Integer[] sellist = new Integer[selectedRows.length];
383 for (Integer row : selectedRows)
387 // Sort rows by coverage
388 Arrays.sort(sellist, new Comparator<Integer>()
391 public int compare(Integer o1, Integer o2)
393 int o1_xt = ((Integer) restable.getValueAt(o1, up_end_idx))
394 - (Integer) restable.getValueAt(o1, up_start_idx);
395 int o2_xt = ((Integer) restable.getValueAt(o2, up_end_idx))
396 - (Integer) restable.getValueAt(o2, up_start_idx);
397 return o2_xt - o1_xt;
401 for (int row : sellist)
403 // unique id - could be a horrible hash
405 String pdbIdStr = restable.getValueAt(row, idColumnIndex).toString();
406 String urlStr = restable.getValueAt(row, urlColumnIndex).toString();
407 String typeColumn = restable.getValueAt(row, typeColumnIndex)
409 String modelPage = humanUrl < 1 ? null
410 : (String) restable.getValueAt(row, humanUrl);
411 SequenceI selectedSeq = (SequenceI) restable.getValueAt(row,
413 selectedSeqsToView.add(selectedSeq);
414 PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
415 if (pdbEntry == null)
417 pdbEntry = getFindEntry(pdbIdStr, selectedSeq.getAllPDBEntries());
420 if (pdbEntry == null)
422 pdbEntry = new PDBEntry();
423 pdbEntry.setId(pdbIdStr);
424 boolean hasCif = urlStr.toLowerCase(Locale.ENGLISH).endsWith("cif");
425 boolean probablyPdb = urlStr.toLowerCase(Locale.ENGLISH)
427 pdbEntry.setType(hasCif ? PDBEntry.Type.MMCIF
428 : probablyPdb ? PDBEntry.Type.PDB : PDBEntry.Type.FILE);
429 if (!"PDBe".equalsIgnoreCase(typeColumn))
431 pdbEntry.setRetrievalUrl(urlStr);
433 pdbEntry.setProvider(typeColumn);
434 pdbEntry.setProviderPage(modelPage);
435 selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
437 pdbEntriesToView[count++] = pdbEntry;
439 return pdbEntriesToView;
443 protected FTSRestRequest getLastFTSRequest()
445 return lastTdbRequest;
449 * generate a query for PDBFTS to retrieve structure metadata
451 * @param ftsRestRequest
456 public String buildPDBFTSQueryFor(FTSRestResponse upResponse)
458 List<String> pdbIds = new ArrayList<String>();
459 int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
460 int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
461 for (FTSData row : upResponse.getSearchSummary())
463 String id = (String) row.getSummaryData()[idx_modelId];
464 String provider = (String) row.getSummaryData()[idx_provider];
465 if ("PDBe".equalsIgnoreCase(provider))
470 return String.join(" OR ", pdbIds).toString();
474 * query PDBe for structure metadata
478 * @return FTSRestResponse via PDBStructureChooserQuerySource
480 public FTSRestResponse fetchStructuresMetaDataFor(
481 PDBStructureChooserQuerySource pdbquery,
482 FTSRestResponse upResponse) throws Exception
485 String pdb_Query = buildPDBFTSQueryFor(upResponse);
486 if (pdb_Query.length() == 0)
490 FTSRestResponse resultList;
491 FTSRestRequest pdbRequest = new FTSRestRequest();
492 pdbRequest.setAllowEmptySeq(false);
493 pdbRequest.setResponseSize(500);
494 pdbRequest.setFieldToSearchBy("(");
495 // pdbRequest.setFieldToSortBy("pdb_id");
496 pdbRequest.setWantedFields(
497 pdbquery.getDocFieldPrefs().getStructureSummaryFields());
498 pdbRequest.setSearchTerm(pdb_Query + ")");
500 resultList = pdbquery.executePDBFTSRestRequest(pdbRequest);
502 lastPdbRequest = pdbRequest;
506 public FTSRestResponse joinResponses(FTSRestResponse upResponse,
507 FTSRestResponse pdbResponse)
509 boolean hasPdbResp = lastPdbRequest != null;
511 int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
513 int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
514 int pdbIdx = hasPdbResp ? lastPdbRequest.getFieldIndex("PDB Id") : -1;
515 int pdbTitle_idx = hasPdbResp ? lastPdbRequest.getFieldIndex("Title")
517 int tdbTitle_idx = getLastFTSRequest().getFieldIndex("Title");
519 List<FTSData> joinedRows = new ArrayList<FTSData>();
520 for (final FTSData row : upResponse.getSearchSummary())
522 String id = (String) row.getSummaryData()[idx_modelId];
523 String provider = (String) row.getSummaryData()[idx_provider];
524 if ("PDBe".equalsIgnoreCase(provider))
529 "Warning: seems like we couldn't get to the PDBe search interface.");
533 for (final FTSData pdbrow : pdbResponse.getSearchSummary())
535 String pdbid = (String) pdbrow.getSummaryData()[pdbIdx];
536 if (id.equalsIgnoreCase(pdbid))
538 row.getSummaryData()[tdbTitle_idx] = pdbrow
539 .getSummaryData()[pdbTitle_idx];
547 row.getSummaryData()[tdbTitle_idx] = "Model from TDB";