JAL-3829 allow client-side selection of structures for a sequence by passing in disco...
[jalview.git] / src / jalview / gui / structurechooser / ThreeDBStructureChooserQuerySource.java
index 6c2123b..1c04d3a 100644 (file)
@@ -1,14 +1,16 @@
 package jalview.gui.structurechooser;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Objects;
 import java.util.Set;
 
 import javax.swing.JTable;
-import javax.swing.table.TableModel;
 
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
@@ -21,12 +23,12 @@ import jalview.fts.core.FTSDataColumnPreferences;
 import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
 import jalview.fts.core.FTSRestRequest;
 import jalview.fts.core.FTSRestResponse;
-import jalview.fts.service.pdb.PDBFTSRestClient;
 import jalview.fts.service.threedbeacons.TDBeaconsFTSRestClient;
-import jalview.jbgui.GStructureChooser.FilterOption;
+import jalview.jbgui.FilterOption;
+import jalview.util.MessageManager;
 
 /**
- * logic for querying the PDBe API for structures of sequences
+ * logic for querying the 3DBeacons API for structures of sequences
  * 
  * @author jprocter
  */
@@ -36,19 +38,25 @@ public class ThreeDBStructureChooserQuerySource
 
   private static int MAX_QLENGTH = 7820;
 
+  protected FTSRestRequest lastTdbRequest;
+
+  protected FTSRestClientI tdbRestClient;
+
+  private FTSRestRequest lastPdbRequest;
+
   public ThreeDBStructureChooserQuerySource()
   {
-    pdbRestClient = TDBeaconsFTSRestClient.getInstance();
+    tdbRestClient = TDBeaconsFTSRestClient.getInstance();
     docFieldPrefs = new FTSDataColumnPreferences(
             PreferenceSource.STRUCTURE_CHOOSER,
             TDBeaconsFTSRestClient.getInstance());
 
   }
 
-
   /**
-   * Builds a query string for a given sequences using its DBRef entries
-   * 3d Beacons is only useful for uniprot IDs
+   * Builds a query string for a given sequences using its DBRef entries 3d
+   * Beacons is only useful for uniprot IDs
+   * 
    * @param seq
    *          the sequences to build a query for
    * @return the built query string
@@ -75,12 +83,13 @@ public class ThreeDBStructureChooserQuerySource
         if (isValidSeqName(getDBRefId(dbRef))
                 && queryBuilder.length() < MAX_QLENGTH)
         {
-          if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT) && dbRef.isCanonical())
+          if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT)
+                  && dbRef.isCanonical())
           {
-            // TODO: pick best Uniprot accession 
-            isUniProtRefsFound=true;
+            // TODO: pick best Uniprot accession
+            isUniProtRefsFound = true;
             return getDBRefId(dbRef);
-            
+
           }
         }
       }
@@ -88,8 +97,6 @@ public class ThreeDBStructureChooserQuerySource
     return null;
   }
 
-
   /**
    * Ensures sequence ref names are not less than 3 characters and does not
    * contain a database name
@@ -146,22 +153,23 @@ public class ThreeDBStructureChooserQuerySource
           FilterOption selectedFilterOpt, boolean b) throws Exception
   {
     FTSRestResponse resultList;
-    FTSRestRequest pdbRequest = getTDBeaconsRequest(seq, wantedFields);
-    resultList = pdbRestClient.executeRequest(pdbRequest);
+    FTSRestRequest tdbRequest = getTDBeaconsRequest(seq, wantedFields);
+    resultList = tdbRestClient.executeRequest(tdbRequest);
 
-    lastPdbRequest = pdbRequest;
+    lastTdbRequest = tdbRequest;
     return resultList;
   }
-  
 
-  private FTSRestRequest getTDBeaconsRequest(SequenceI seq, Collection<FTSDataColumnI> wantedFields)
+  private FTSRestRequest getTDBeaconsRequest(SequenceI seq,
+          Collection<FTSDataColumnI> wantedFields)
   {
     FTSRestRequest pdbRequest = new FTSRestRequest();
     pdbRequest.setAllowEmptySeq(false);
     pdbRequest.setResponseSize(500);
     pdbRequest.setWantedFields(wantedFields);
     String query = buildQuery(seq);
-    if (query==null)  {
+    if (query == null)
+    {
       return null;
     }
     pdbRequest.setSearchTerm(query + ".json");
@@ -169,6 +177,38 @@ public class ThreeDBStructureChooserQuerySource
     return pdbRequest;
   }
 
+  @Override
+  public List<FilterOption> getAvailableFilterOptions(String VIEWS_FILTER)
+  {
+    List<FilterOption> filters = new ArrayList<FilterOption>();
+    filters.add(
+            new FilterOption(MessageManager.getString("label.best_quality"),
+                    "overall_quality", VIEWS_FILTER, false));
+    filters.add(new FilterOption(
+            MessageManager.getString("label.best_resolution"), "resolution",
+            VIEWS_FILTER, false));
+    filters.add(new FilterOption(
+            MessageManager.getString("label.most_protein_chain"),
+            "number_of_protein_chains", VIEWS_FILTER, false));
+    filters.add(new FilterOption(
+            MessageManager.getString("label.most_bound_molecules"),
+            "number_of_bound_molecules", VIEWS_FILTER, false));
+    filters.add(new FilterOption(
+            MessageManager.getString("label.most_polymer_residues"),
+            "number_of_polymer_residues", VIEWS_FILTER, true));
+
+    return filters;
+  }
+
+  /**
+   * model categories - update as needed. warnings output if unknown types
+   * encountered.
+   * 
+   * Order denotes 'trust'
+   */
+  private static List<String> EXP_CATEGORIES = Arrays
+          .asList(new String[]
+          { "EXPERIMENTALLY DETERMINED", "DEEP LEARNING", "TEMPLATE-BASED" });
 
   /**
    * FTSRestClient specific query builder to pick top ranked entry from a
@@ -186,21 +226,104 @@ public class ThreeDBStructureChooserQuerySource
    * @throws Exception
    */
   public FTSRestResponse selectFirstRankedQuery(SequenceI seq,
+          Collection<FTSData> collectedResults,
           Collection<FTSDataColumnI> wantedFields, String fieldToFilterBy,
           boolean b) throws Exception
   {
 
-    FTSRestResponse resultList;
-    FTSRestRequest pdbRequest = getTDBeaconsRequest(seq, wantedFields);
-    if (pdbRequest == null) {
-      return null;
+    List<FTSData> filteredResponse = new ArrayList<FTSData>();
+    final int idx_ups = lastTdbRequest.getFieldIndex("Uniprot Start");
+    final int idx_upe = lastTdbRequest.getFieldIndex("Uniprot End");
+    final int idx_mcat = lastTdbRequest.getFieldIndex("Model Category");
+    final int idx_mqual = lastTdbRequest.getFieldIndex("Qmean");
+    final int idx_resol = lastTdbRequest.getFieldIndex("Resolution");
+
+    // ignore anything outside the sequence region
+    for (FTSData row : collectedResults)
+    {
+      int up_s = (Integer) row.getSummaryData()[idx_ups];
+      int up_e = (Integer) row.getSummaryData()[idx_upe];
+
+      if (seq == row.getSummaryData()[0] && up_e > seq.getStart()
+              && up_s < seq.getEnd())
+      {
+        filteredResponse.add(row);
+      }
     }
-    pdbRequest.setResponseSize(1);
-    resultList = pdbRestClient.executeRequest(pdbRequest);
-    
-    // TODO: client side filtering - sort results and pick top one (or N)
+    // sort according to decreasing length,
+    // increasing start
+    Collections.sort(filteredResponse, new Comparator<FTSData>()
+    {
 
-    lastPdbRequest = pdbRequest;
+      private final int scoreCategory(String cat)
+      {
+        // TODO: make quicker
+        int idx = EXP_CATEGORIES.indexOf(cat.toUpperCase());
+        if (idx == -1)
+        {
+          System.out.println("Unknown category: '" + cat + "'");
+        }
+        return -EXP_CATEGORIES.size() - idx;
+      }
+
+      @Override
+      public int compare(FTSData o1, FTSData o2)
+      {
+        int o1_s = (Integer) o1.getSummaryData()[idx_ups];
+        int o1_e = (Integer) o1.getSummaryData()[idx_upe];
+        int o1_cat = scoreCategory((String) o1.getSummaryData()[idx_mcat]);
+        int o2_s = (Integer) o2.getSummaryData()[idx_ups];
+        int o2_e = (Integer) o2.getSummaryData()[idx_upe];
+        int o2_cat = scoreCategory((String) o2.getSummaryData()[idx_mcat]);
+
+        if (o1_cat == o2_cat)
+        {
+          if (o1_s == o2_s)
+          {
+            int o1_xtent = o1_e - o1_s;
+            int o2_xtent = o2_e - o2_s;
+            if (o1_xtent == o2_xtent)
+            {
+              if (o1_cat == scoreCategory(EXP_CATEGORIES.get(0)))
+              {
+                // experimental structures, so rank on quality
+                double o1_res = (Double) o1.getSummaryData()[idx_resol];
+                double o2_res = (Double) o2.getSummaryData()[idx_resol];
+                return (o2_res < o1_res) ? 1 : (o2_res == o1_res) ? 0 : -1;
+              }
+              else
+              {
+                // models, so rank on qmean
+                float o1_mq = (Float) o1.getSummaryData()[idx_mqual];
+                float o2_mq = (Float) o2.getSummaryData()[idx_mqual];
+                return (o2_mq < o1_mq) ? 1 : (o2_mq == o1_mq) ? 0 : -1;
+              }
+            }
+            else
+            {
+              return o1_xtent - o2_xtent;
+            }
+          }
+          else
+          {
+            return o1_s - o2_s;
+          }
+        }
+        else
+        {
+          return o2_cat - o1_cat;
+        }
+      }
+
+      @Override
+      public boolean equals(Object obj)
+      {
+        return super.equals(obj);
+      }
+    });
+    FTSRestResponse resultList = new FTSRestResponse();
+    resultList.setNumberOfItemsFound(filteredResponse.size());
+    resultList.setSearchSummary(filteredResponse);
     return resultList;
   }
 
@@ -208,25 +331,23 @@ public class ThreeDBStructureChooserQuerySource
   public PDBEntry[] collectSelectedRows(JTable restable, int[] selectedRows,
           List<SequenceI> selectedSeqsToView)
   {
-    int refSeqColIndex = restable.getColumn("Ref Sequence")
-            .getModelIndex();
+    int refSeqColIndex = restable.getColumn("Ref Sequence").getModelIndex();
 
-    PDBEntry[] pdbEntriesToView=new PDBEntry[selectedRows.length];
+    PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
     int count = 0;
     int idColumnIndex = restable.getColumn("Model id").getModelIndex();
     int urlColumnIndex = restable.getColumn("Url").getModelIndex();
     int typeColumnIndex = restable.getColumn("Provider").getModelIndex();
-    int categoryColumnIndex = restable.getColumn("Model Category").getModelIndex();
-    
+    int categoryColumnIndex = restable.getColumn("Model Category")
+            .getModelIndex();
+
     for (int row : selectedRows)
     {
       // unique id - could be a horrible hash
-      
-      String pdbIdStr = restable.getValueAt(row,idColumnIndex)
-              .toString();
-      String urlStr = restable.getValueAt(row,urlColumnIndex)
-              .toString();
-      String typeColumn = restable.getValueAt(row,typeColumnIndex)
+
+      String pdbIdStr = restable.getValueAt(row, idColumnIndex).toString();
+      String urlStr = restable.getValueAt(row, urlColumnIndex).toString();
+      String typeColumn = restable.getValueAt(row, typeColumnIndex)
               .toString();
       SequenceI selectedSeq = (SequenceI) restable.getValueAt(row,
               refSeqColIndex);
@@ -234,8 +355,7 @@ public class ThreeDBStructureChooserQuerySource
       PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
       if (pdbEntry == null)
       {
-        pdbEntry = getFindEntry(pdbIdStr,
-                selectedSeq.getAllPDBEntries());
+        pdbEntry = getFindEntry(pdbIdStr, selectedSeq.getAllPDBEntries());
       }
 
       if (pdbEntry == null)
@@ -253,4 +373,94 @@ public class ThreeDBStructureChooserQuerySource
     }
     return pdbEntriesToView;
   }
+
+  @Override
+  protected FTSRestRequest getLastFTSRequest()
+  {
+    return lastTdbRequest;
+  }
+
+  /**
+   * generate a query for PDBFTS to retrieve structure metadata
+   * 
+   * @param ftsRestRequest
+   * @param upResponse
+   * @return
+   */
+
+  public String buildPDBFTSQueryFor(FTSRestResponse upResponse)
+  {
+    List<String> pdbIds = new ArrayList<String>();
+    int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
+    int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
+    for (FTSData row : upResponse.getSearchSummary())
+    {
+      String id = (String) row.getSummaryData()[idx_modelId];
+      String provider = (String) row.getSummaryData()[idx_provider];
+      if ("PDBe".equalsIgnoreCase(provider))
+      {
+        pdbIds.add(id);
+      }
+    }
+    return String.join(" OR ", pdbIds).toString();
+  }
+
+  /**
+   * query PDBe for structure metadata
+   * 
+   * @param pdbquery
+   * @param upResponse
+   * @return FTSRestResponse via PDBStructureChooserQuerySource
+   */
+  public FTSRestResponse fetchStructuresMetaDataFor(
+          PDBStructureChooserQuerySource pdbquery,
+          FTSRestResponse upResponse) throws Exception
+  {
+
+    String pdb_Query = buildPDBFTSQueryFor(upResponse);
+
+    FTSRestResponse resultList;
+    FTSRestRequest pdbRequest = new FTSRestRequest();
+    pdbRequest.setAllowEmptySeq(false);
+    pdbRequest.setResponseSize(500);
+    pdbRequest.setFieldToSearchBy("(");
+    // pdbRequest.setFieldToSortBy("pdb_id");
+    pdbRequest.setWantedFields(
+            pdbquery.getDocFieldPrefs().getStructureSummaryFields());
+    pdbRequest.setSearchTerm(pdb_Query + ")");
+    resultList = pdbquery.executePDBFTSRestRequest(pdbRequest);
+
+    lastPdbRequest = pdbRequest;
+    return resultList;
+  }
+
+  public FTSRestResponse joinResponses(FTSRestResponse upResponse,
+          FTSRestResponse pdbResponse)
+  {
+    int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
+    // join on
+    int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
+    int pdbIdx = lastPdbRequest.getFieldIndex("pdb_id");
+    for (FTSData row : upResponse.getSearchSummary())
+    {
+      String id = (String) row.getSummaryData()[idx_modelId];
+      String provider = (String) row.getSummaryData()[idx_provider];
+      if ("PDBe".equalsIgnoreCase(provider))
+      {
+        for (FTSData pdbrow : pdbResponse.getSearchSummary())
+        {
+          String pdbid = (String) pdbrow.getSummaryData()[pdbIdx];
+          if (id.equalsIgnoreCase(pdbid))
+          {
+            // often multiple entries per PDB ID so we bail after first
+            // get wanted fields
+            // append to FTSRestResponse array
+          }
+        }
+      }
+    }
+    // TODO Auto-generated method stub
+    return null;
+  }
+
 }
\ No newline at end of file