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