2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.gui.structurechooser;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Comparator;
27 import java.util.HashSet;
28 import java.util.LinkedHashSet;
29 import java.util.List;
30 import java.util.Locale;
33 import javax.swing.JTable;
35 import jalview.bin.Console;
36 import jalview.datamodel.DBRefEntry;
37 import jalview.datamodel.DBRefSource;
38 import jalview.datamodel.PDBEntry;
39 import jalview.datamodel.SequenceI;
40 import jalview.fts.api.FTSData;
41 import jalview.fts.api.FTSDataColumnI;
42 import jalview.fts.api.FTSRestClientI;
43 import jalview.fts.core.FTSDataColumnPreferences;
44 import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
45 import jalview.fts.core.FTSRestRequest;
46 import jalview.fts.core.FTSRestResponse;
47 import jalview.fts.service.threedbeacons.TDB_FTSData;
48 import jalview.fts.service.threedbeacons.TDBeaconsFTSRestClient;
49 import jalview.jbgui.FilterOption;
52 * logic for querying the 3DBeacons API for structures of sequences
56 public class ThreeDBStructureChooserQuerySource
57 extends StructureChooserQuerySource
60 private Set<String> tdBeaconsFilters = null, defaultFilters = null;
62 public static final String FILTER_TDBEACONS_COVERAGE = "3d_beacons_coverage";
64 public static final String FILTER_FIRST_BEST_COVERAGE = "3d_beacons_first_best_coverage";
66 private static final String FILTER_SOURCE_PREFIX = "only_";
68 protected FTSRestRequest lastTdbRequest;
70 protected FTSRestClientI tdbRestClient;
72 private FTSRestRequest lastPdbRequest;
74 public ThreeDBStructureChooserQuerySource()
76 defaultFilters = new LinkedHashSet<String>();
77 defaultFilters.add(FILTER_TDBEACONS_COVERAGE);
78 defaultFilters.add(FILTER_FIRST_BEST_COVERAGE);
80 tdbRestClient = TDBeaconsFTSRestClient.getInstance();
81 docFieldPrefs = new FTSDataColumnPreferences(
82 PreferenceSource.STRUCTURE_CHOOSER,
83 TDBeaconsFTSRestClient.getInstance());
87 * Builds a query string for a given sequences using its DBRef entries 3d
88 * Beacons is only useful for uniprot IDs
91 * the sequences to build a query for
92 * @return the built query string
96 public String buildQuery(SequenceI seq)
98 List<DBRefEntry> refs = seq.getDBRefs();
99 int ib = checkUniprotRefs(refs);
102 return getDBRefId(refs.get(ib));
108 * Searches DBRefEntry for uniprot refs
111 * @return -2 if no uniprot refs, -1 if no canonical ref., otherwise index of
112 * Uniprot canonical DBRefEntry
114 public static int checkUniprotRefs(List<DBRefEntry> refs)
116 boolean hasUniprot = false;
117 if (refs != null && refs.size() != 0)
119 for (int ib = 0, nb = refs.size(); ib < nb; ib++)
121 DBRefEntry dbRef = refs.get(ib);
122 if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT))
125 if (dbRef.isCanonical())
132 return hasUniprot ? -1 : -2;
136 * Ensures sequence ref names are not less than 3 characters and does not
137 * contain a database name
142 static boolean isValidSeqName(String seqName)
144 String ignoreList = "pdb,uniprot,swiss-prot";
145 if (seqName.length() < 3)
149 if (seqName.contains(":"))
153 seqName = seqName.toLowerCase(Locale.ROOT);
154 for (String ignoredEntry : ignoreList.split(","))
156 if (seqName.contains(ignoredEntry))
164 static String getDBRefId(DBRefEntry dbRef)
166 String ref = dbRef.getAccessionId().replaceAll("GO:", "");
171 * FTSRestClient specific query builder to recover associated structure data
172 * records for a sequence
175 * - seq to generate a query for
176 * @param wantedFields
177 * - fields to retrieve
178 * @param selectedFilterOpt
179 * - criterion for ranking results (e.g. resolution)
181 * - sort ascending or descending
186 public FTSRestResponse fetchStructuresMetaData(SequenceI seq,
187 Collection<FTSDataColumnI> wantedFields,
188 FilterOption selectedFilterOpt, boolean b) throws Exception
190 FTSRestResponse resultList;
191 if (selectedFilterOpt != null
192 && tdBeaconsFilter(selectedFilterOpt.getValue()))
194 FTSRestRequest tdbRequest = getTDBeaconsRequest(seq, wantedFields);
195 resultList = tdbRestClient.executeRequest(tdbRequest);
197 lastTdbRequest = tdbRequest;
198 if (resultList != null)
199 { // Query the PDB and add additional metadata
200 List<FTSRestResponse> pdbResponse = fetchStructuresMetaDataFor(
201 getPDBQuerySource(), resultList);
203 resultList = joinResponses(resultList, pdbResponse);
207 // use the PDBFTS directly
208 resultList = getPDBQuerySource().fetchStructuresMetaData(seq,
209 wantedFields, selectedFilterOpt, b);
210 lastTdbRequest = getPDBQuerySource().lastPdbRequest;
211 lastPdbRequest = lastTdbRequest; // both queries the same - indicates we
217 PDBStructureChooserQuerySource pdbQuerySource = null;
219 private PDBStructureChooserQuerySource getPDBQuerySource()
221 if (pdbQuerySource == null)
223 pdbQuerySource = new PDBStructureChooserQuerySource();
225 return pdbQuerySource;
228 private FTSRestRequest getTDBeaconsRequest(SequenceI seq,
229 Collection<FTSDataColumnI> wantedFields)
231 FTSRestRequest pdbRequest = new FTSRestRequest();
232 pdbRequest.setAllowEmptySeq(false);
233 pdbRequest.setResponseSize(500);
234 pdbRequest.setWantedFields(wantedFields);
235 String query = buildQuery(seq);
240 pdbRequest.setSearchTerm(query + ".json");
241 pdbRequest.setAssociatedSequence(seq);
246 public List<FilterOption> getAvailableFilterOptions(String VIEWS_FILTER)
248 List<FilterOption> filters = getPDBQuerySource()
249 .getAvailableFilterOptions(VIEWS_FILTER);
250 tdBeaconsFilters = new LinkedHashSet<String>();
251 tdBeaconsFilters.addAll(defaultFilters);
252 filters.add(0, new FilterOption("Best 3D-Beacons Coverage",
253 FILTER_FIRST_BEST_COVERAGE, VIEWS_FILTER, false, this));
254 filters.add(1, new FilterOption("Multiple 3D-Beacons Coverage",
255 FILTER_TDBEACONS_COVERAGE, VIEWS_FILTER, true, this));
261 public void updateAvailableFilterOptions(String VIEWS_FILTER,
262 List<FilterOption> xtantOptions, Collection<FTSData> tdbEntries)
264 if (tdbEntries != null && lastTdbRequest != null)
266 boolean hasPDBe = false;
267 for (FTSData _row : tdbEntries)
269 // tdb returns custom object
270 TDB_FTSData row = (TDB_FTSData) _row;
271 String provider = row.getProvider();
272 FilterOption providerOpt = new FilterOption(
273 "3DB Provider - " + provider,
274 FILTER_SOURCE_PREFIX + provider, VIEWS_FILTER, false, this);
275 if (!xtantOptions.contains(providerOpt))
277 xtantOptions.add(1, providerOpt);
278 tdBeaconsFilters.add(FILTER_SOURCE_PREFIX + provider);
279 if ("PDBe".equalsIgnoreCase(provider))
287 // remove the PDBe options from the available filters
289 while (op < xtantOptions.size())
291 FilterOption filter = xtantOptions.get(op);
293 .getQuerySource() instanceof PDBStructureChooserQuerySource)
295 xtantOptions.remove(op);
307 private boolean tdBeaconsFilter(String fieldToFilterBy)
309 return tdBeaconsFilters != null
310 && tdBeaconsFilters.contains(fieldToFilterBy);
313 protected String remove_prefix(String fieldToFilterBy)
315 if (tdBeaconsFilters != null
316 && tdBeaconsFilters.contains(fieldToFilterBy)
317 && !defaultFilters.contains(fieldToFilterBy))
319 return fieldToFilterBy.substring(FILTER_SOURCE_PREFIX.length());
328 public boolean needsRefetch(FilterOption selectedFilterOpt)
330 return selectedFilterOpt == null
331 || !tdBeaconsFilter(selectedFilterOpt.getValue())
332 && lastPdbRequest != lastTdbRequest;
336 * FTSRestClient specific query builder to pick top ranked entry from a
337 * fetchStructuresMetaData query
340 * - seq to generate a query for
341 * @param wantedFields
342 * - fields to retrieve
343 * @param selectedFilterOpt
344 * - criterion for ranking results (e.g. resolution)
346 * - sort ascending or descending
351 public FTSRestResponse selectFirstRankedQuery(SequenceI seq,
352 Collection<FTSData> collectedResults,
353 Collection<FTSDataColumnI> wantedFields, String fieldToFilterBy,
354 boolean b) throws Exception
356 if (fieldToFilterBy != null && tdBeaconsFilter(fieldToFilterBy))
358 TDBResultAnalyser analyser = new TDBResultAnalyser(seq,
359 collectedResults, lastTdbRequest, fieldToFilterBy,
360 remove_prefix(fieldToFilterBy));
362 FTSRestResponse resultList = new FTSRestResponse();
364 List<FTSData> filteredResponse = analyser.getFilteredResponse();
366 List<FTSData> selectedStructures = analyser
367 .selectStructures(filteredResponse);
368 resultList.setNumberOfItemsFound(selectedStructures.size());
369 resultList.setSearchSummary(selectedStructures);
372 // Fall back to PDBe rankings
373 return getPDBQuerySource().selectFirstRankedQuery(seq, collectedResults,
374 wantedFields, fieldToFilterBy, b);
378 public PDBEntry[] collectSelectedRows(JTable restable, int[] selectedRows,
379 List<SequenceI> selectedSeqsToView)
381 int refSeqColIndex = restable.getColumn("Ref Sequence").getModelIndex();
383 PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
385 int idColumnIndex = restable.getColumn("Model id").getModelIndex();
386 int urlColumnIndex = restable.getColumn("Url").getModelIndex();
387 int typeColumnIndex = restable.getColumn("Provider").getModelIndex();
388 int humanUrl = restable.getColumn("Page URL").getModelIndex();
389 int modelformat = restable.getColumn("Model Format").getModelIndex();
390 int tftype = restable.getColumn("Confidence Score Type").getModelIndex();
391 int tftypev = restable.getColumn("Confidence Score Version").getModelIndex();
393 final int up_start_idx = restable.getColumn("Uniprot Start")
395 final int up_end_idx = restable.getColumn("Uniprot End")
400 Integer[] sellist = new Integer[selectedRows.length];
401 for (Integer row : selectedRows)
405 // Sort rows by coverage
406 Arrays.sort(sellist, new Comparator<Integer>()
409 public int compare(Integer o1, Integer o2)
411 int o1_xt = ((Integer) restable.getValueAt(o1, up_end_idx))
412 - (Integer) restable.getValueAt(o1, up_start_idx);
413 int o2_xt = ((Integer) restable.getValueAt(o2, up_end_idx))
414 - (Integer) restable.getValueAt(o2, up_start_idx);
415 return o2_xt - o1_xt;
419 for (int row : sellist)
421 // unique id - could be a horrible hash
423 String pdbIdStr = restable.getValueAt(row, idColumnIndex).toString();
424 String urlStr = restable.getValueAt(row, urlColumnIndex).toString();
425 String typeColumn = restable.getValueAt(row, typeColumnIndex)
427 String modelPage = humanUrl < 1 ? null
428 : (String) restable.getValueAt(row, humanUrl);
429 String strucFormat = restable.getValueAt(row, modelformat).toString();
430 String strucTfType = tftype<1 ? null : restable.getValueAt(row, tftype)==null ? null : restable.getValueAt(row, tftype).toString();
431 String strucTfType_v = tftypev<1 ? null : restable.getValueAt(row, tftypev)==null ? null : restable.getValueAt(row, tftypev).toString();
432 SequenceI selectedSeq = (SequenceI) restable.getValueAt(row,
434 selectedSeqsToView.add(selectedSeq);
435 PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
436 if (pdbEntry == null)
438 pdbEntry = getFindEntry(pdbIdStr, selectedSeq.getAllPDBEntries());
441 if (pdbEntry == null)
443 pdbEntry = new PDBEntry();
444 pdbEntry.setId(pdbIdStr);
445 pdbEntry.setAuthoritative(true);
448 pdbEntry.setType(PDBEntry.Type.valueOf(strucFormat));
449 } catch (Exception q)
451 Console.warn("Unknown filetype for 3D Beacons Model from: "
452 + strucFormat + " - " + pdbIdStr + " - " + modelPage);
455 if (!"PDBe".equalsIgnoreCase(typeColumn))
457 pdbEntry.setRetrievalUrl(urlStr);
459 pdbEntry.setProvider(typeColumn);
460 if (modelPage != null)
462 pdbEntry.setProviderPage(modelPage);
466 if (strucTfType!=null && !"".equals(strucTfType)) {
467 pdbEntry.setTempFacType(strucTfType);
469 if (strucTfType_v!=null && !"".equals(strucTfType_v)) {
470 pdbEntry.setTempFacTypeVersion(strucTfType_v);
472 } catch (Exception q)
474 Console.warn("Unknown filetype for 3D Beacons Model from: "
475 + strucFormat + " - " + pdbIdStr + " - " + modelPage);
478 selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
480 pdbEntriesToView[count++] = pdbEntry;
482 return pdbEntriesToView;
486 protected FTSRestRequest getLastFTSRequest()
488 return lastTdbRequest;
492 * generate a query for PDBFTS to retrieve structure metadata
494 * @param ftsRestRequest
499 public List<String> buildPDBFTSQueryFor(FTSRestResponse upResponse)
501 List<String> ftsQueries = new ArrayList<String>();
502 Set<String> pdbIds = new HashSet<String>();
503 int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
504 int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
505 for (FTSData row : upResponse.getSearchSummary())
507 String id = (String) row.getSummaryData()[idx_modelId];
508 String provider = (String) row.getSummaryData()[idx_provider];
509 if ("PDBe".equalsIgnoreCase(provider))
514 StringBuilder sb = new StringBuilder();
515 for (String pdbId : pdbIds)
517 if (sb.length() > 2500)
519 ftsQueries.add(sb.toString());
530 ftsQueries.add(sb.toString());
536 * query PDBe for structure metadata
540 * @return FTSRestResponse via PDBStructureChooserQuerySource
542 public List<FTSRestResponse> fetchStructuresMetaDataFor(
543 PDBStructureChooserQuerySource pdbquery,
544 FTSRestResponse upResponse) throws Exception
546 List<String> pdb_Queries = buildPDBFTSQueryFor(upResponse);
547 if (pdb_Queries.size() == 0)
551 List<FTSRestResponse> results = new ArrayList<FTSRestResponse>();
553 for (String pdb_Query : pdb_Queries)
555 FTSRestResponse resultList;
556 FTSRestRequest pdbRequest = new FTSRestRequest();
557 pdbRequest.setAllowEmptySeq(false);
558 pdbRequest.setResponseSize(500);
559 pdbRequest.setFieldToSearchBy("(");
560 // pdbRequest.setFieldToSortBy("pdb_id");
561 pdbRequest.setWantedFields(
562 pdbquery.getDocFieldPrefs().getStructureSummaryFields());
563 pdbRequest.setSearchTerm(pdb_Query + ")");
565 // handle exceptions like server errors here - means the threedbeacons
566 // discovery isn't broken by issues to do with the PDBe SOLR api
569 resultList = pdbquery.executePDBFTSRestRequest(pdbRequest);
570 if (resultList.getNumberOfItemsFound() == 0)
572 Console.info("Unexpectedly returned no results for pdbe query: "
575 results.add(resultList);
576 lastPdbRequest = pdbRequest;
577 } catch (Exception ex)
579 Console.error("PDBFTSQuery failed", ex);
587 public FTSRestResponse joinResponses(FTSRestResponse upResponse,
588 List<FTSRestResponse> pdbResponses)
590 boolean hasPdbResp = lastPdbRequest != null;
592 int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
594 int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
595 int pdbIdx = hasPdbResp ? lastPdbRequest.getFieldIndex("PDB Id") : -1;
596 int pdbTitle_idx = hasPdbResp ? lastPdbRequest.getFieldIndex("Title")
598 int tdbTitle_idx = getLastFTSRequest().getFieldIndex("Title");
600 for (final FTSData row : upResponse.getSearchSummary())
602 String id = (String) row.getSummaryData()[idx_modelId];
603 String provider = (String) row.getSummaryData()[idx_provider];
604 if ("PDBe".equalsIgnoreCase(provider))
608 jalview.bin.Console.outPrintln(
609 "Warning: seems like we couldn't get to the PDBe search interface.");
613 for (final FTSRestResponse pdbResponse : pdbResponses)
615 for (final FTSData pdbrow : pdbResponse.getSearchSummary())
617 String pdbid = (String) pdbrow.getSummaryData()[pdbIdx];
618 if (id.equalsIgnoreCase(pdbid))
620 row.getSummaryData()[tdbTitle_idx] = pdbrow
621 .getSummaryData()[pdbTitle_idx];
630 row.getSummaryData()[tdbTitle_idx] = "Model from TDB";
636 public TDB_FTSData getFTSDataFor(JTable restable, int selectedRow,
637 Collection<FTSData> discoveredStructuresSet)
639 int idColumnIndex = restable.getColumn("Model id").getModelIndex();
641 String modelId = (String) restable.getValueAt(selectedRow,
643 for (FTSData row : discoveredStructuresSet)
645 if (row instanceof TDB_FTSData
646 && ((TDB_FTSData) row).getModelId().equals(modelId))
648 return ((TDB_FTSData) row);