e5f96be4be2f63159f383d0e7fe0bcc72e022cda
[jalview.git] / src / jalview / gui / structurechooser / ThreeDBStructureChooserQuerySource.java
1 package jalview.gui.structurechooser;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collection;
6 import java.util.Collections;
7 import java.util.Comparator;
8 import java.util.HashSet;
9 import java.util.LinkedHashSet;
10 import java.util.List;
11 import java.util.Set;
12
13 import javax.swing.JTable;
14
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.TDBeaconsFTSRestClient;
27 import jalview.jbgui.FilterOption;
28 import jalview.util.MessageManager;
29
30 /**
31  * logic for querying the 3DBeacons API for structures of sequences
32  * 
33  * @author jprocter
34  */
35 public class ThreeDBStructureChooserQuerySource
36         extends StructureChooserQuerySource
37 {
38
39   private static int MAX_QLENGTH = 7820;
40
41   protected FTSRestRequest lastTdbRequest;
42
43   protected FTSRestClientI tdbRestClient;
44
45   private FTSRestRequest lastPdbRequest;
46
47   public ThreeDBStructureChooserQuerySource()
48   {
49     tdbRestClient = TDBeaconsFTSRestClient.getInstance();
50     docFieldPrefs = new FTSDataColumnPreferences(
51             PreferenceSource.STRUCTURE_CHOOSER,
52             TDBeaconsFTSRestClient.getInstance());
53
54   }
55
56   /**
57    * Builds a query string for a given sequences using its DBRef entries 3d
58    * Beacons is only useful for uniprot IDs
59    * 
60    * @param seq
61    *          the sequences to build a query for
62    * @return the built query string
63    */
64
65   public String buildQuery(SequenceI seq)
66   {
67     boolean isPDBRefsFound = false;
68     boolean isUniProtRefsFound = false;
69     StringBuilder queryBuilder = new StringBuilder();
70     Set<String> seqRefs = new LinkedHashSet<>();
71
72     /*
73      * note PDBs as DBRefEntry so they are not duplicated in query
74      */
75     Set<String> pdbids = new HashSet<>();
76
77     List<DBRefEntry> refs = seq.getDBRefs();
78     if (refs != null && refs.size() != 0)
79     {
80       for (int ib = 0, nb = refs.size(); ib < nb; ib++)
81       {
82         DBRefEntry dbRef = refs.get(ib);
83         if (isValidSeqName(getDBRefId(dbRef))
84                 && queryBuilder.length() < MAX_QLENGTH)
85         {
86           if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT)
87                   && dbRef.isCanonical())
88           {
89             // TODO: pick best Uniprot accession
90             isUniProtRefsFound = true;
91             return getDBRefId(dbRef);
92
93           }
94         }
95       }
96     }
97     return null;
98   }
99
100   /**
101    * Ensures sequence ref names are not less than 3 characters and does not
102    * contain a database name
103    * 
104    * @param seqName
105    * @return
106    */
107   static boolean isValidSeqName(String seqName)
108   {
109     // System.out.println("seqName : " + seqName);
110     String ignoreList = "pdb,uniprot,swiss-prot";
111     if (seqName.length() < 3)
112     {
113       return false;
114     }
115     if (seqName.contains(":"))
116     {
117       return false;
118     }
119     seqName = seqName.toLowerCase();
120     for (String ignoredEntry : ignoreList.split(","))
121     {
122       if (seqName.contains(ignoredEntry))
123       {
124         return false;
125       }
126     }
127     return true;
128   }
129
130   static String getDBRefId(DBRefEntry dbRef)
131   {
132     String ref = dbRef.getAccessionId().replaceAll("GO:", "");
133     return ref;
134   }
135
136   /**
137    * FTSRestClient specific query builder to recover associated structure data
138    * records for a sequence
139    * 
140    * @param seq
141    *          - seq to generate a query for
142    * @param wantedFields
143    *          - fields to retrieve
144    * @param selectedFilterOpt
145    *          - criterion for ranking results (e.g. resolution)
146    * @param b
147    *          - sort ascending or descending
148    * @return
149    * @throws Exception
150    */
151   public FTSRestResponse fetchStructuresMetaData(SequenceI seq,
152           Collection<FTSDataColumnI> wantedFields,
153           FilterOption selectedFilterOpt, boolean b) throws Exception
154   {
155     FTSRestResponse resultList;
156     FTSRestRequest tdbRequest = getTDBeaconsRequest(seq, wantedFields);
157     resultList = tdbRestClient.executeRequest(tdbRequest);
158
159     lastTdbRequest = tdbRequest;
160     return resultList;
161   }
162
163   private FTSRestRequest getTDBeaconsRequest(SequenceI seq,
164           Collection<FTSDataColumnI> wantedFields)
165   {
166     FTSRestRequest pdbRequest = new FTSRestRequest();
167     pdbRequest.setAllowEmptySeq(false);
168     pdbRequest.setResponseSize(500);
169     pdbRequest.setWantedFields(wantedFields);
170     String query = buildQuery(seq);
171     if (query == null)
172     {
173       return null;
174     }
175     pdbRequest.setSearchTerm(query + ".json");
176     pdbRequest.setAssociatedSequence(seq);
177     return pdbRequest;
178   }
179
180   @Override
181   public List<FilterOption> getAvailableFilterOptions(String VIEWS_FILTER)
182   {
183     List<FilterOption> filters = new ArrayList<FilterOption>();
184     filters.add(
185             new FilterOption(MessageManager.getString("label.best_quality"),
186                     "overall_quality", VIEWS_FILTER, false));
187     filters.add(new FilterOption(
188             MessageManager.getString("label.best_resolution"), "resolution",
189             VIEWS_FILTER, false));
190     filters.add(new FilterOption(
191             MessageManager.getString("label.most_protein_chain"),
192             "number_of_protein_chains", VIEWS_FILTER, false));
193     filters.add(new FilterOption(
194             MessageManager.getString("label.most_bound_molecules"),
195             "number_of_bound_molecules", VIEWS_FILTER, false));
196     filters.add(new FilterOption(
197             MessageManager.getString("label.most_polymer_residues"),
198             "number_of_polymer_residues", VIEWS_FILTER, true));
199
200     return filters;
201   }
202
203   /**
204    * FTSRestClient specific query builder to pick top ranked entry from a
205    * fetchStructuresMetaData query
206    * 
207    * @param seq
208    *          - seq to generate a query for
209    * @param wantedFields
210    *          - fields to retrieve
211    * @param selectedFilterOpt
212    *          - criterion for ranking results (e.g. resolution)
213    * @param b
214    *          - sort ascending or descending
215    * @return
216    * @throws Exception
217    */
218   public FTSRestResponse selectFirstRankedQuery(SequenceI seq,
219           Collection<FTSData> collectedResults,
220           Collection<FTSDataColumnI> wantedFields, String fieldToFilterBy,
221           boolean b) throws Exception
222   {
223
224     TDBResultAnalyser analyser= new TDBResultAnalyser(seq,collectedResults, lastTdbRequest);
225     
226     
227     FTSRestResponse resultList = new FTSRestResponse();
228
229     List<FTSData> filteredResponse = analyser.getFilteredResponse();
230     
231     List<FTSData> selectedStructures = analyser.selectStructures(filteredResponse);
232     resultList.setNumberOfItemsFound(selectedStructures.size());
233     resultList.setSearchSummary(selectedStructures);
234     return resultList;
235   }
236
237   @Override
238   public PDBEntry[] collectSelectedRows(JTable restable, int[] selectedRows,
239           List<SequenceI> selectedSeqsToView)
240   {
241     int refSeqColIndex = restable.getColumn("Ref Sequence").getModelIndex();
242
243     PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
244     int count = 0;
245     int idColumnIndex = restable.getColumn("Model id").getModelIndex();
246     int urlColumnIndex = restable.getColumn("Url").getModelIndex();
247     int typeColumnIndex = restable.getColumn("Provider").getModelIndex();
248     int categoryColumnIndex = restable.getColumn("Model Category")
249             .getModelIndex();
250     final int up_start_idx = restable.getColumn("Uniprot Start").getModelIndex();
251     final int up_end_idx = restable.getColumn("Uniprot End").getModelIndex();
252     int i=0;
253     
254     // bleugh!
255     Integer[] sellist = new Integer[selectedRows.length];
256     for (Integer row: selectedRows)
257     {
258       sellist[i++] = row;
259     }
260     // Sort rows by coverage
261     Arrays.sort(sellist,new Comparator<Integer>()
262     {
263       @Override
264       public int compare(Integer o1, Integer o2)
265       {
266         int o1_xt = ((Integer)restable.getValueAt(o1, up_end_idx)) - (Integer)restable.getValueAt(o1, up_start_idx);
267         int o2_xt = ((Integer)restable.getValueAt(o2, up_end_idx)) - (Integer)restable.getValueAt(o2, up_start_idx);
268         return o2_xt-o1_xt;
269       }
270     });
271
272     
273     for (int row : sellist)
274     {
275       // unique id - could be a horrible hash
276
277       String pdbIdStr = restable.getValueAt(row, idColumnIndex).toString();
278       String urlStr = restable.getValueAt(row, urlColumnIndex).toString();
279       String typeColumn = restable.getValueAt(row, typeColumnIndex)
280               .toString();
281       SequenceI selectedSeq = (SequenceI) restable.getValueAt(row,
282               refSeqColIndex);
283       selectedSeqsToView.add(selectedSeq);
284       PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
285       if (pdbEntry == null)
286       {
287         pdbEntry = getFindEntry(pdbIdStr, selectedSeq.getAllPDBEntries());
288       }
289
290       if (pdbEntry == null)
291       {
292         pdbEntry = new PDBEntry();
293         pdbEntry.setId(pdbIdStr);
294         pdbEntry.setType(PDBEntry.Type.MMCIF);
295         if (!"PDBe".equalsIgnoreCase(typeColumn))
296         {
297           pdbEntry.setRetrievalUrl(urlStr);
298         }
299         selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
300       }
301       pdbEntriesToView[count++] = pdbEntry;
302     }
303     return pdbEntriesToView;
304   }
305
306   @Override
307   protected FTSRestRequest getLastFTSRequest()
308   {
309     return lastTdbRequest;
310   }
311
312   /**
313    * generate a query for PDBFTS to retrieve structure metadata
314    * 
315    * @param ftsRestRequest
316    * @param upResponse
317    * @return
318    */
319
320   public String buildPDBFTSQueryFor(FTSRestResponse upResponse)
321   {
322     List<String> pdbIds = new ArrayList<String>();
323     int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
324     int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
325     for (FTSData row : upResponse.getSearchSummary())
326     {
327       String id = (String) row.getSummaryData()[idx_modelId];
328       String provider = (String) row.getSummaryData()[idx_provider];
329       if ("PDBe".equalsIgnoreCase(provider))
330       {
331         pdbIds.add(id);
332       }
333     }
334     return String.join(" OR ", pdbIds).toString();
335   }
336
337   /**
338    * query PDBe for structure metadata
339    * 
340    * @param pdbquery
341    * @param upResponse
342    * @return FTSRestResponse via PDBStructureChooserQuerySource
343    */
344   public FTSRestResponse fetchStructuresMetaDataFor(
345           PDBStructureChooserQuerySource pdbquery,
346           FTSRestResponse upResponse) throws Exception
347   {
348
349     String pdb_Query = buildPDBFTSQueryFor(upResponse);
350
351     FTSRestResponse resultList;
352     FTSRestRequest pdbRequest = new FTSRestRequest();
353     pdbRequest.setAllowEmptySeq(false);
354     pdbRequest.setResponseSize(500);
355     pdbRequest.setFieldToSearchBy("(");
356     // pdbRequest.setFieldToSortBy("pdb_id");
357     pdbRequest.setWantedFields(
358             pdbquery.getDocFieldPrefs().getStructureSummaryFields());
359     pdbRequest.setSearchTerm(pdb_Query + ")");
360     resultList = pdbquery.executePDBFTSRestRequest(pdbRequest);
361
362     lastPdbRequest = pdbRequest;
363     return resultList;
364   }
365
366   public FTSRestResponse joinResponses(FTSRestResponse upResponse,
367           FTSRestResponse pdbResponse)
368   {
369     int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
370     // join on
371     int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
372     int pdbIdx = lastPdbRequest.getFieldIndex("pdb_id");
373     for (FTSData row : upResponse.getSearchSummary())
374     {
375       String id = (String) row.getSummaryData()[idx_modelId];
376       String provider = (String) row.getSummaryData()[idx_provider];
377       if ("PDBe".equalsIgnoreCase(provider))
378       {
379         for (FTSData pdbrow : pdbResponse.getSearchSummary())
380         {
381           String pdbid = (String) pdbrow.getSummaryData()[pdbIdx];
382           if (id.equalsIgnoreCase(pdbid))
383           {
384             // often multiple entries per PDB ID so we bail after first
385             // get wanted fields
386             // append to FTSRestResponse array
387           }
388         }
389       }
390     }
391     // TODO Auto-generated method stub
392     return null;
393   }
394
395 }