Clover icon

Coverage Report

  1. Project Clover database Mon Sep 2 2024 17:57:51 BST
  2. Package jalview.gui.structurechooser

File ThreeDBStructureChooserQuerySource.java

 

Coverage histogram

../../../img/srcFileCovDistChart6.png
37% of files have more coverage

Code metrics

78
208
21
1
643
477
69
0.33
9.9
21
3.29

Classes

Class Line # Actions
ThreeDBStructureChooserQuerySource 56 208 69
0.6091205560.9%
 

Contributing tests

This file is covered by 42 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
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.
11    *
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.
16    *
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.
20    */
21    package jalview.gui.structurechooser;
22   
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;
31    import java.util.Set;
32   
33    import javax.swing.JTable;
34   
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;
50   
51    /**
52    * logic for querying the 3DBeacons API for structures of sequences
53    *
54    * @author jprocter
55    */
 
56    public class ThreeDBStructureChooserQuerySource
57    extends StructureChooserQuerySource
58    {
59   
60    private Set<String> tdBeaconsFilters = null, defaultFilters = null;
61   
62    public static final String FILTER_TDBEACONS_COVERAGE = "3d_beacons_coverage";
63   
64    public static final String FILTER_FIRST_BEST_COVERAGE = "3d_beacons_first_best_coverage";
65   
66    private static final String FILTER_SOURCE_PREFIX = "only_";
67   
68    protected FTSRestRequest lastTdbRequest;
69   
70    protected FTSRestClientI tdbRestClient;
71   
72    private FTSRestRequest lastPdbRequest;
73   
 
74  70 toggle public ThreeDBStructureChooserQuerySource()
75    {
76  70 defaultFilters = new LinkedHashSet<String>();
77  70 defaultFilters.add(FILTER_TDBEACONS_COVERAGE);
78  70 defaultFilters.add(FILTER_FIRST_BEST_COVERAGE);
79   
80  70 tdbRestClient = TDBeaconsFTSRestClient.getInstance();
81  70 docFieldPrefs = new FTSDataColumnPreferences(
82    PreferenceSource.STRUCTURE_CHOOSER,
83    TDBeaconsFTSRestClient.getInstance());
84    }
85   
86    /**
87    * Builds a query string for a given sequences using its DBRef entries 3d
88    * Beacons is only useful for uniprot IDs
89    *
90    * @param seq
91    * the sequences to build a query for
92    * @return the built query string
93    */
94   
 
95  15 toggle @Override
96    public String buildQuery(SequenceI seq)
97    {
98  15 List<DBRefEntry> refs = seq.getDBRefs();
99  15 int ib = checkUniprotRefs(refs);
100  15 if (ib > -1)
101    {
102  14 return getDBRefId(refs.get(ib));
103    }
104  1 return null;
105    }
106   
107    /**
108    * Searches DBRefEntry for uniprot refs
109    *
110    * @param seq
111    * @return -2 if no uniprot refs, -1 if no canonical ref., otherwise index of
112    * Uniprot canonical DBRefEntry
113    */
 
114  83 toggle public static int checkUniprotRefs(List<DBRefEntry> refs)
115    {
116  83 boolean hasUniprot = false;
117  83 if (refs != null && refs.size() != 0)
118    {
119  57 for (int ib = 0, nb = refs.size(); ib < nb; ib++)
120    {
121  46 DBRefEntry dbRef = refs.get(ib);
122  46 if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT))
123    {
124  26 hasUniprot = true;
125  26 if (dbRef.isCanonical())
126    {
127  23 return ib;
128    }
129    }
130    }
131    }
132  60 return hasUniprot ? -1 : -2;
133    }
134   
135    /**
136    * Ensures sequence ref names are not less than 3 characters and does not
137    * contain a database name
138    *
139    * @param seqName
140    * @return
141    */
 
142  0 toggle static boolean isValidSeqName(String seqName)
143    {
144  0 String ignoreList = "pdb,uniprot,swiss-prot";
145  0 if (seqName.length() < 3)
146    {
147  0 return false;
148    }
149  0 if (seqName.contains(":"))
150    {
151  0 return false;
152    }
153  0 seqName = seqName.toLowerCase(Locale.ROOT);
154  0 for (String ignoredEntry : ignoreList.split(","))
155    {
156  0 if (seqName.contains(ignoredEntry))
157    {
158  0 return false;
159    }
160    }
161  0 return true;
162    }
163   
 
164  14 toggle static String getDBRefId(DBRefEntry dbRef)
165    {
166  14 String ref = dbRef.getAccessionId().replaceAll("GO:", "");
167  14 return ref;
168    }
169   
170    /**
171    * FTSRestClient specific query builder to recover associated structure data
172    * records for a sequence
173    *
174    * @param seq
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)
180    * @param b
181    * - sort ascending or descending
182    * @return
183    * @throws Exception
184    */
 
185  12 toggle @Override
186    public FTSRestResponse fetchStructuresMetaData(SequenceI seq,
187    Collection<FTSDataColumnI> wantedFields,
188    FilterOption selectedFilterOpt, boolean b) throws Exception
189    {
190  12 FTSRestResponse resultList;
191  12 if (selectedFilterOpt != null
192    && tdBeaconsFilter(selectedFilterOpt.getValue()))
193    {
194  12 FTSRestRequest tdbRequest = getTDBeaconsRequest(seq, wantedFields);
195  12 resultList = tdbRestClient.executeRequest(tdbRequest);
196   
197  12 lastTdbRequest = tdbRequest;
198  12 if (resultList != null)
199    { // Query the PDB and add additional metadata
200  12 List<FTSRestResponse> pdbResponse = fetchStructuresMetaDataFor(
201    getPDBQuerySource(), resultList);
202   
203  12 resultList = joinResponses(resultList, pdbResponse);
204    }
205  12 return resultList;
206    }
207    // use the PDBFTS directly
208  0 resultList = getPDBQuerySource().fetchStructuresMetaData(seq,
209    wantedFields, selectedFilterOpt, b);
210  0 lastTdbRequest = getPDBQuerySource().lastPdbRequest;
211  0 lastPdbRequest = lastTdbRequest; // both queries the same - indicates we
212    // rank using PDBe
213  0 return resultList;
214   
215    }
216   
217    PDBStructureChooserQuerySource pdbQuerySource = null;
218   
 
219  19 toggle private PDBStructureChooserQuerySource getPDBQuerySource()
220    {
221  19 if (pdbQuerySource == null)
222    {
223  6 pdbQuerySource = new PDBStructureChooserQuerySource();
224    }
225  19 return pdbQuerySource;
226    }
227   
 
228  12 toggle private FTSRestRequest getTDBeaconsRequest(SequenceI seq,
229    Collection<FTSDataColumnI> wantedFields)
230    {
231  12 FTSRestRequest pdbRequest = new FTSRestRequest();
232  12 pdbRequest.setAllowEmptySeq(false);
233  12 pdbRequest.setResponseSize(500);
234  12 pdbRequest.setWantedFields(wantedFields);
235  12 String query = buildQuery(seq);
236  12 if (query == null)
237    {
238  0 return null;
239    }
240  12 pdbRequest.setSearchTerm(query + ".json");
241  12 pdbRequest.setAssociatedSequence(seq);
242  12 return pdbRequest;
243    }
244   
 
245  7 toggle @Override
246    public List<FilterOption> getAvailableFilterOptions(String VIEWS_FILTER)
247    {
248  7 List<FilterOption> filters = getPDBQuerySource()
249    .getAvailableFilterOptions(VIEWS_FILTER);
250  7 tdBeaconsFilters = new LinkedHashSet<String>();
251  7 tdBeaconsFilters.addAll(defaultFilters);
252  7 filters.add(0, new FilterOption("Best 3D-Beacons Coverage",
253    FILTER_FIRST_BEST_COVERAGE, VIEWS_FILTER, false, this));
254  7 filters.add(1, new FilterOption("Multiple 3D-Beacons Coverage",
255    FILTER_TDBEACONS_COVERAGE, VIEWS_FILTER, true, this));
256   
257  7 return filters;
258    }
259   
 
260  7 toggle @Override
261    public void updateAvailableFilterOptions(String VIEWS_FILTER,
262    List<FilterOption> xtantOptions, Collection<FTSData> tdbEntries)
263    {
264  7 if (tdbEntries != null && lastTdbRequest != null)
265    {
266  6 boolean hasPDBe = false;
267  6 for (FTSData _row : tdbEntries)
268    {
269    // tdb returns custom object
270  2515 TDB_FTSData row = (TDB_FTSData) _row;
271  2515 String provider = row.getProvider();
272  2515 FilterOption providerOpt = new FilterOption(
273    "3DB Provider - " + provider,
274    FILTER_SOURCE_PREFIX + provider, VIEWS_FILTER, false, this);
275  2515 if (!xtantOptions.contains(providerOpt))
276    {
277  27 xtantOptions.add(1, providerOpt);
278  27 tdBeaconsFilters.add(FILTER_SOURCE_PREFIX + provider);
279  27 if ("PDBe".equalsIgnoreCase(provider))
280    {
281  6 hasPDBe = true;
282    }
283    }
284    }
285  6 if (!hasPDBe)
286    {
287    // remove the PDBe options from the available filters
288  0 int op = 0;
289  0 while (op < xtantOptions.size())
290    {
291  0 FilterOption filter = xtantOptions.get(op);
292  0 if (filter
293    .getQuerySource() instanceof PDBStructureChooserQuerySource)
294    {
295  0 xtantOptions.remove(op);
296    }
297    else
298    {
299  0 op++;
300    }
301    }
302    }
303    }
304   
305    }
306   
 
307  19 toggle private boolean tdBeaconsFilter(String fieldToFilterBy)
308    {
309  19 return tdBeaconsFilters != null
310    && tdBeaconsFilters.contains(fieldToFilterBy);
311    }
312   
 
313  7 toggle protected String remove_prefix(String fieldToFilterBy)
314    {
315  7 if (tdBeaconsFilters != null
316    && tdBeaconsFilters.contains(fieldToFilterBy)
317    && !defaultFilters.contains(fieldToFilterBy))
318    {
319  0 return fieldToFilterBy.substring(FILTER_SOURCE_PREFIX.length());
320    }
321    else
322    {
323  7 return null;
324    }
325    }
326   
 
327  1 toggle @Override
328    public boolean needsRefetch(FilterOption selectedFilterOpt)
329    {
330  1 return selectedFilterOpt == null
331    || !tdBeaconsFilter(selectedFilterOpt.getValue())
332    && lastPdbRequest != lastTdbRequest;
333    }
334   
335    /**
336    * FTSRestClient specific query builder to pick top ranked entry from a
337    * fetchStructuresMetaData query
338    *
339    * @param seq
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)
345    * @param b
346    * - sort ascending or descending
347    * @return
348    * @throws Exception
349    */
 
350  6 toggle @Override
351    public FTSRestResponse selectFirstRankedQuery(SequenceI seq,
352    Collection<FTSData> collectedResults,
353    Collection<FTSDataColumnI> wantedFields, String fieldToFilterBy,
354    boolean b) throws Exception
355    {
356  6 if (fieldToFilterBy != null && tdBeaconsFilter(fieldToFilterBy))
357    {
358  6 TDBResultAnalyser analyser = new TDBResultAnalyser(seq,
359    collectedResults, lastTdbRequest, fieldToFilterBy,
360    remove_prefix(fieldToFilterBy));
361   
362  6 FTSRestResponse resultList = new FTSRestResponse();
363   
364  6 List<FTSData> filteredResponse = analyser.getFilteredResponse();
365   
366  6 List<FTSData> selectedStructures = analyser
367    .selectStructures(filteredResponse);
368  6 resultList.setNumberOfItemsFound(selectedStructures.size());
369  6 resultList.setSearchSummary(selectedStructures);
370  6 return resultList;
371    }
372    // Fall back to PDBe rankings
373  0 return getPDBQuerySource().selectFirstRankedQuery(seq, collectedResults,
374    wantedFields, fieldToFilterBy, b);
375    }
376   
 
377  0 toggle @Override
378    public PDBEntry[] collectSelectedRows(JTable restable, int[] selectedRows,
379    List<SequenceI> selectedSeqsToView)
380    {
381  0 int refSeqColIndex = restable.getColumn("Ref Sequence").getModelIndex();
382   
383  0 PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
384  0 int count = 0;
385  0 int idColumnIndex = restable.getColumn("Model id").getModelIndex();
386  0 int urlColumnIndex = restable.getColumn("Url").getModelIndex();
387  0 int typeColumnIndex = restable.getColumn("Provider").getModelIndex();
388  0 int humanUrl = restable.getColumn("Page URL").getModelIndex();
389  0 int modelformat = restable.getColumn("Model Format").getModelIndex();
390  0 int idx_mcat = restable.getColumn("Model Category").getModelIndex();
391   
392  0 final int up_start_idx = restable.getColumn("Uniprot Start")
393    .getModelIndex();
394  0 final int up_end_idx = restable.getColumn("Uniprot End")
395    .getModelIndex();
396  0 int i = 0;
397   
398    // bleugh!
399  0 Integer[] sellist = new Integer[selectedRows.length];
400  0 for (Integer row : selectedRows)
401    {
402  0 sellist[i++] = row;
403    }
404    // Sort rows by coverage
405  0 Arrays.sort(sellist, new Comparator<Integer>()
406    {
 
407  0 toggle @Override
408    public int compare(Integer o1, Integer o2)
409    {
410  0 int o1_xt = ((Integer) restable.getValueAt(o1, up_end_idx))
411    - (Integer) restable.getValueAt(o1, up_start_idx);
412  0 int o2_xt = ((Integer) restable.getValueAt(o2, up_end_idx))
413    - (Integer) restable.getValueAt(o2, up_start_idx);
414  0 return o2_xt - o1_xt;
415    }
416    });
417   
418  0 for (int row : sellist)
419    {
420    // unique id - could be a horrible hash
421   
422  0 String pdbIdStr = restable.getValueAt(row, idColumnIndex).toString();
423  0 String urlStr = restable.getValueAt(row, urlColumnIndex).toString();
424  0 String typeColumn = restable.getValueAt(row, typeColumnIndex)
425    .toString();
426  0 String modelPage = humanUrl < 1 ? null
427    : (String) restable.getValueAt(row, humanUrl);
428   
429  0 String modelCategory = idx_mcat < 1 ? null
430    : (String) restable.getValueAt(row, idx_mcat);
431   
432  0 String strucFormat = restable.getValueAt(row, modelformat).toString();
433   
434  0 SequenceI selectedSeq = (SequenceI) restable.getValueAt(row,
435    refSeqColIndex);
436  0 selectedSeqsToView.add(selectedSeq);
437  0 PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
438  0 if (pdbEntry == null)
439    {
440  0 pdbEntry = getFindEntry(pdbIdStr, selectedSeq.getAllPDBEntries());
441    }
442   
443  0 if (pdbEntry == null)
444    {
445  0 pdbEntry = new PDBEntry();
446  0 pdbEntry.setId(pdbIdStr);
447  0 pdbEntry.setAuthoritative(true);
448  0 try
449    {
450  0 pdbEntry.setType(PDBEntry.Type.valueOf(strucFormat));
451    } catch (Exception q)
452    {
453  0 Console.warn("Unknown filetype for 3D Beacons Model from: "
454    + strucFormat + " - " + pdbIdStr + " - " + modelPage);
455    }
456   
457  0 if (!"PDBe".equalsIgnoreCase(typeColumn))
458    {
459  0 pdbEntry.setRetrievalUrl(urlStr);
460    }
461  0 pdbEntry.setProvider(typeColumn);
462  0 if (modelPage != null)
463    {
464  0 pdbEntry.setProviderPage(modelPage);
465    }
466  0 pdbEntry.setProviderCategory(modelCategory);
467  0 selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
468    }
469  0 pdbEntriesToView[count++] = pdbEntry;
470    }
471  0 return pdbEntriesToView;
472    }
473   
 
474  98 toggle @Override
475    protected FTSRestRequest getLastFTSRequest()
476    {
477  98 return lastTdbRequest;
478    }
479   
480    /**
481    * generate a query for PDBFTS to retrieve structure metadata
482    *
483    * @param ftsRestRequest
484    * @param upResponse
485    * @return
486    */
487   
 
488  22 toggle public List<String> buildPDBFTSQueryFor(FTSRestResponse upResponse)
489    {
490  22 List<String> ftsQueries = new ArrayList<String>();
491  22 Set<String> pdbIds = new HashSet<String>();
492  22 int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
493  22 int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
494  22 for (FTSData row : upResponse.getSearchSummary())
495    {
496  9934 String id = (String) row.getSummaryData()[idx_modelId];
497  9934 String provider = (String) row.getSummaryData()[idx_provider];
498  9934 if ("PDBe".equalsIgnoreCase(provider))
499    {
500  9468 pdbIds.add(id);
501    }
502    }
503  22 StringBuilder sb = new StringBuilder();
504  22 for (String pdbId : pdbIds)
505    {
506  7556 if (sb.length() > 2500)
507    {
508  20 ftsQueries.add(sb.toString());
509  20 sb.setLength(0);
510    }
511  7556 if (sb.length() > 0)
512    {
513  7514 sb.append(" OR ");
514    }
515  7556 sb.append(pdbId);
516    }
517  22 if (sb.length() > 0)
518    {
519  22 ftsQueries.add(sb.toString());
520    }
521  22 return ftsQueries;
522    }
523   
524    /**
525    * query PDBe for structure metadata
526    *
527    * @param pdbquery
528    * @param upResponse
529    * @return FTSRestResponse via PDBStructureChooserQuerySource
530    */
 
531  17 toggle public List<FTSRestResponse> fetchStructuresMetaDataFor(
532    PDBStructureChooserQuerySource pdbquery,
533    FTSRestResponse upResponse) throws Exception
534    {
535  17 List<String> pdb_Queries = buildPDBFTSQueryFor(upResponse);
536  17 if (pdb_Queries.size() == 0)
537    {
538  0 return null;
539    }
540  17 List<FTSRestResponse> results = new ArrayList<FTSRestResponse>();
541   
542  17 for (String pdb_Query : pdb_Queries)
543    {
544  32 FTSRestResponse resultList;
545  32 FTSRestRequest pdbRequest = new FTSRestRequest();
546  32 pdbRequest.setAllowEmptySeq(false);
547  32 pdbRequest.setResponseSize(500);
548  32 pdbRequest.setFieldToSearchBy("(");
549    // pdbRequest.setFieldToSortBy("pdb_id");
550  32 pdbRequest.setWantedFields(
551    pdbquery.getDocFieldPrefs().getStructureSummaryFields());
552  32 pdbRequest.setSearchTerm(pdb_Query + ")");
553   
554    // handle exceptions like server errors here - means the threedbeacons
555    // discovery isn't broken by issues to do with the PDBe SOLR api
556  32 try
557    {
558  32 resultList = pdbquery.executePDBFTSRestRequest(pdbRequest);
559  32 if (resultList.getNumberOfItemsFound() == 0)
560    {
561  0 Console.info("Unexpectedly returned no results for pdbe query: "
562    + pdb_Query);
563    }
564  32 results.add(resultList);
565  32 lastPdbRequest = pdbRequest;
566    } catch (Exception ex)
567    {
568  0 Console.error("PDBFTSQuery failed", ex);
569    }
570   
571    }
572   
573  17 return results;
574    }
575   
 
576  17 toggle public FTSRestResponse joinResponses(FTSRestResponse upResponse,
577    List<FTSRestResponse> pdbResponses)
578    {
579  17 boolean hasPdbResp = lastPdbRequest != null;
580   
581  17 int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
582    // join on
583  17 int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
584  17 int pdbIdx = hasPdbResp ? lastPdbRequest.getFieldIndex("PDB Id") : -1;
585  17 int pdbTitle_idx = hasPdbResp ? lastPdbRequest.getFieldIndex("Title")
586    : -1;
587  17 int tdbTitle_idx = getLastFTSRequest().getFieldIndex("Title");
588   
589  17 for (final FTSData row : upResponse.getSearchSummary())
590    {
591  7482 String id = (String) row.getSummaryData()[idx_modelId];
592  7482 String provider = (String) row.getSummaryData()[idx_provider];
593  7482 if ("PDBe".equalsIgnoreCase(provider))
594    {
595  7116 if (!hasPdbResp)
596    {
597  0 jalview.bin.Console.outPrintln(
598    "Warning: seems like we couldn't get to the PDBe search interface.");
599    }
600    else
601    {
602  7116 for (final FTSRestResponse pdbResponse : pdbResponses)
603    {
604  29043 for (final FTSData pdbrow : pdbResponse.getSearchSummary())
605    {
606  11189136 String pdbid = (String) pdbrow.getSummaryData()[pdbIdx];
607  11189136 if (id.equalsIgnoreCase(pdbid))
608    {
609  12527 row.getSummaryData()[tdbTitle_idx] = pdbrow
610    .getSummaryData()[pdbTitle_idx];
611    }
612    }
613    }
614    }
615   
616    }
617    else
618    {
619  366 row.getSummaryData()[tdbTitle_idx] = "Model from TDB";
620    }
621    }
622  17 return upResponse;
623    }
624   
 
625  0 toggle public TDB_FTSData getFTSDataFor(JTable restable, int selectedRow,
626    Collection<FTSData> discoveredStructuresSet)
627    {
628  0 int idColumnIndex = restable.getColumn("Model id").getModelIndex();
629   
630  0 String modelId = (String) restable.getValueAt(selectedRow,
631    idColumnIndex);
632  0 for (FTSData row : discoveredStructuresSet)
633    {
634  0 if (row instanceof TDB_FTSData
635    && ((TDB_FTSData) row).getModelId().equals(modelId))
636    {
637  0 return ((TDB_FTSData) row);
638    }
639    }
640  0 return null;
641    }
642   
643    }