incremental refinement of results parsing
[jalview.git] / src / jalview / ws / rest / RestJobThread.java
index bd71813..c100a72 100644 (file)
@@ -2,15 +2,19 @@ package jalview.ws.rest;
 
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.ColumnSelection;
+import jalview.gui.AlignFrame;
 import jalview.gui.WebserviceInfo;
 import jalview.io.packed.DataProvider;
 import jalview.io.packed.JalviewDataset;
+import jalview.io.packed.JalviewDataset.AlignmentSet;
 import jalview.io.packed.ParsePackedSet;
 import jalview.io.packed.SimpleDataProvider;
 import jalview.io.packed.DataProvider.JvDataType;
 import jalview.ws.AWSThread;
 import jalview.ws.AWsJob;
 
+import java.awt.Desktop;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.io.IOException;
@@ -44,7 +48,8 @@ public class RestJobThread extends AWSThread
 
   public RestJobThread(RestClient restClient)
   {
-    super();
+    super(restClient.af, null, restClient._input,
+            restClient.service.postUrl);
     this.restClient = restClient; // may not be needed
     // Test Code
     // minimal job - submit given input and parse result onto alignment as
@@ -60,13 +65,15 @@ public class RestJobThread extends AWSThread
       jobs = new RestJob[1];
       jobs[0] = new RestJob(0, this,
               restClient._input.getVisibleAlignment(restClient.service
-                      .getGapCharacter()));
+                      .getGapCharacter()),
+              restClient._input.getVisibleContigs());
       // need a function to get a range on a view/alignment and return both
       // annotation, groups and selection subsetted to just that region.
 
     }
     else
     {
+      int[] viscontig = restClient._input.getVisibleContigs();
       AlignmentI[] viscontigals = restClient._input
               .getVisibleContigAlignments(restClient.service
                       .getGapCharacter());
@@ -75,13 +82,15 @@ public class RestJobThread extends AWSThread
         jobs = new RestJob[viscontigals.length];
         for (int j = 0; j < jobs.length; j++)
         {
+          int[] visc = new int[]
+          { viscontig[j * 2], viscontig[j * 2 + 1] };
           if (j != 0)
           {
-            jobs[j] = new RestJob(j, this, viscontigals[j]);
+            jobs[j] = new RestJob(j, this, viscontigals[j], visc);
           }
           else
           {
-            jobs[j] = new RestJob(0, this, viscontigals[j]);
+            jobs[j] = new RestJob(0, this, viscontigals[j], visc);
           }
         }
       }
@@ -178,7 +187,8 @@ public class RestJobThread extends AWSThread
           throws Exception
   {
     StringBuffer respText = new StringBuffer();
-    // con.setContentHandlerFactory(new jalview.ws.io.mime.HttpContentHandler());
+    // con.setContentHandlerFactory(new
+    // jalview.ws.io.mime.HttpContentHandler());
     HttpRequestBase request = null;
     String messages = "";
     if (stg == Stage.SUBMIT)
@@ -244,8 +254,12 @@ public class RestJobThread extends AWSThread
         processResultSet(rj, response, request);
         break;
       case 202:
-        rj.statMessage = "Job submitted successfully. Results available at this URL:\n"
-                + rj.getJobId() + "\n";
+        rj.statMessage = "<br>Job submitted successfully. Results available at this URL:\n"
+                + "<a href="
+                + rj.getJobId()
+                + "\">"
+                + rj.getJobId()
+                + "</a><br>";
         rj.running = true;
         break;
       case 302:
@@ -274,6 +288,7 @@ public class RestJobThread extends AWSThread
         rj.setAllowedServerExceptions(0);
         rj.setSubjobComplete(true);
         rj.error = true;
+        rj.running = false;
         completeStatus(rj, response, "" + getStage(stg)
                 + "failed. Reason below:\n");
         break;
@@ -346,9 +361,24 @@ public class RestJobThread extends AWSThread
     }
     HttpEntity en = con.getEntity();
     /*
-     * Just show the content as a string.
+     * Just append the content as a string.
      */
-    rj.statMessage = EntityUtils.toString(en);
+    String f;
+    StringBuffer content = new StringBuffer(f = EntityUtils.toString(en));
+    f = f.toLowerCase();
+    int body = f.indexOf("<body");
+    if (body > -1)
+    {
+      content.delete(0, f.indexOf(">", body));
+    }
+    if (body > -1 && sb.length() > 0)
+    {
+      sb.append("\n");
+      content.insert(0, sb);
+      sb = null;
+    }
+    f = null;
+    rj.statMessage = content.toString();
   }
 
   @Override
@@ -379,108 +409,220 @@ public class RestJobThread extends AWSThread
   public void parseResult()
   {
     // crazy users will see this message
-    System.err.println("WARNING: Rest job result parser is INCOMPLETE!");
+    // TODO: finish this! and remove the message below!
+    Cache.log.warn("Rest job result parser is currently INCOMPLETE!");
+    int validres = 0;
     for (RestJob rj : (RestJob[]) jobs)
     {
-      // TODO: call each jobs processResults() method and collect valid
-      // contexts.
       if (rj.hasResponse() && rj.resSet != null && rj.resSet.isValid())
       {
         String ln = null;
-        System.out.println("Parsing data for job " + rj.getJobId());
-        if (!restClient.isAlignmentModified())
+        try
         {
-          try
-          {
-            /*
-             * while ((ln=rj.resSet.nextLine())!=null) { System.out.println(ln);
-             * } }
-             */
-            List<DataProvider> dp = new ArrayList<DataProvider>();
-            restClient.af.newView_actionPerformed(null);
-            dp.add(new SimpleDataProvider(JvDataType.ANNOTATION, rj.resSet, null));
-            JalviewDataset context = new JalviewDataset(restClient.av.getAlignment().getDataset(), null, null,restClient.av.getAlignment());
-            ParsePackedSet pps = new ParsePackedSet();
-            pps.getAlignment(context, dp);
-            
-            // do an ap.refresh restClient.av.alignmentChanged(Desktop.getAlignmentPanels(restClient.av.getViewId())[0]);
-            System.out.println("Finished parsing data for job "
-                    + rj.getJobId());
-
-          } catch (Exception ex)
+          Cache.log.debug("Parsing data for job " + rj.getJobId());
+          rj.parseResultSet();
+          if (rj.hasResults())
           {
-            System.out.println("Failed to finish parsing data for job "
-                    + rj.getJobId());
-            ex.printStackTrace();
+            validres++;
           }
+          Cache.log.debug("Finished parsing data for job " + rj.getJobId());
+
+        } catch (Error ex)
+        {
+          Cache.log.warn("Failed to finish parsing data for job "
+                  + rj.getJobId());
+          ex.printStackTrace();
+        } catch (Exception ex)
+        {
+          Cache.log.warn("Failed to finish parsing data for job "
+                  + rj.getJobId());
+          ex.printStackTrace();
         }
       }
     }
-    /**
-     * decisions based on job result content + state of alignFrame that
-     * originated the job:
-     */
-    /*
-     * 1. Can/Should this job automatically open a new window for results
-     */
-    wsInfo.setViewResultsImmediatly(false);
+    if (validres > 0)
+    {
+      // add listeners and activate result display gui elements
+      /**
+       * decisions based on job result content + state of alignFrame that
+       * originated the job:
+       */
+      /*
+       * 1. Can/Should this job automatically open a new window for results
+       */
+      if (true)
+      {
+        wsInfo.setViewResultsImmediatly(false);
+      }
+      else
+      {
+        // realiseResults(true, true);
+      }
+      // otherwise, should automatically view results
+
+      // TODO: check if at least one or more contexts are valid - if so, enable
+      // gui
+      wsInfo.showResultsNewFrame.addActionListener(new ActionListener()
+      {
+
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          realiseResults(false);
+        }
+
+      });
+      wsInfo.mergeResults.addActionListener(new ActionListener()
+      {
+
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          realiseResults(true);
+        }
+
+      });
+
+      wsInfo.setResultsReady();
+    }
+    else
+    {
+      // tell the user nothing was returned.
+    }
+  }
+
+  public void realiseResults(boolean merge)
+  {
     /*
      * 2. Should the job modify the parent alignment frame/view(s) (if they
      * still exist and the alignment hasn't been edited) in order to display new
      * annotation/features.
      */
     /**
-     * alignments. New alignments are added to dataset, and subsequently
-     * annotated/visualised accordingly. 1. New alignment frame created for new
-     * alignment. Decide if any vis settings should be inherited from old
-     * alignment frame (e.g. sequence ordering ?). 2. Subsequent data added to
-     * alignment as below:
+     * alignment panels derived from each alignment set returned by service. 
      */
+    ArrayList<jalview.gui.AlignmentPanel> destPanels = new ArrayList<jalview.gui.AlignmentPanel>();
     /**
-     * annotation update to original/newly created context alignment: 1.
-     * identify alignment where annotation is to be loaded onto. 2. Add
-     * annotation, excluding any duplicates. 3. Ensure annotation is visible on
-     * alignment - honouring ordering given by file.
+     * current pane being worked with
      */
+    jalview.gui.AlignmentPanel destPanel;
     /**
-     * features updated to original or newly created context alignment: 1.
-     * Features are(or were already) added to dataset. 2. Feature settings
-     * modified to ensure all features are displayed - honouring any ordering
-     * given by result file. Consider merging action with the code used by the
-     * DAS fetcher to update alignment views with new info.
+     * when false, zeroth pane is panel derived from input deta.
      */
-    /**
-     * Seq associated data files (PDB files). 1. locate seq association in
-     * current dataset/alignment context and add file as normal - keep handle of
-     * any created ref objects. 2. decide if new data should be displayed : PDB
-     * display: if alignment has PDB display already, should new pdb files be
-     * aligned to it ?
-     * 
-     */
-    // TODO: check if at least one or more contexts are valid - if so, enable
-    // gui
-    wsInfo.showResultsNewFrame.addActionListener(new ActionListener()
+    boolean newAlignment = false;
+    if (merge)
     {
-
-      @Override
-      public void actionPerformed(ActionEvent e)
+      if (!restClient.isAlignmentModified())
       {
-        // TODO: call method to show results in new window
+        destPanel = restClient.recoverAlignPanelForView();
+        if (restClient.isShowResultsInNewView())
+        {
+          destPanel = destPanel.alignFrame.newView(false);
+        }
+        // add the destination panel to frame zero of result panel set
+        destPanels.add(destPanel);
       }
-
-    });
-    wsInfo.mergeResults.addActionListener(new ActionListener()
+    }
+    if (destPanels.size()==0)
     {
-
-      @Override
-      public void actionPerformed(ActionEvent e)
+      Object[] idat = input.getAlignmentAndColumnSelection(restClient.av
+              .getGapCharacter());
+      AlignFrame af = new AlignFrame((AlignmentI) idat[0],
+              (ColumnSelection) idat[1], AlignFrame.DEFAULT_WIDTH,
+              AlignFrame.DEFAULT_HEIGHT);
+      jalview.gui.Desktop.addInternalFrame(af,
+              "Results for " + restClient.service.details.Name + " "
+                      + restClient.service.details.Action + " on "
+                      + restClient.af.getTitle(), AlignFrame.DEFAULT_WIDTH,
+              AlignFrame.DEFAULT_HEIGHT);
+      destPanel = af.alignPanel;
+      // create totally new alignment from stashed data/results
+      newAlignment = true;
+    }
+    // Now process results, adding/creating new views as necessary.
+    {
+      boolean hsepjobs = restClient.service.isHseparable();
+      boolean vsepjobs = restClient.service.isVseparable();
+      // total number of distinct alignment sets generated by job set.
+      int totalSets = 0, numAlSets = 0;
+      for (int j = 0; j < jobs.length; j++)
       {
-        // TODO: call method to merge results into existing window
-      }
+        RestJob rj = (RestJob) jobs[j];
+        if (rj.hasResults())
+        {
+          JalviewDataset rset = rj.context;
+          numAlSets = rset.hasAlignments() ? 0 : rset.getAl().size();
+          if (numAlSets > 0)
+          {
+            for (int als = 0; als < numAlSets; als++)
+            {
+              // gather data from context
+              if (vsepjobs)
+              {
+                // todo: merge data from each group/sequence onto whole
+                // alignment
+              }
+              else
+              {
+                if (hsepjobs)
+                {
+                  // map single result back on to all visible region of original alignment
+                  if (als==0 && rj.isInputContextModified())
+                  {
+                    // transfer features, annotation, groups, etc, from input context to align panel derived from input data
+                    new jalview.datamodel.Alignment(new jalview.datamodel.SequenceI[] {null}).getAlignmentAnnotation();
+                  }
+                  
+                }
+                else
+                {
+                  // map result onto visible contigs.
+                  AlignmentSet alset = rset.getAl().get(als);
+                  if (als>0 )
+                  if (als == 0)
+                  {
+                    
+                    // alignment is added straight to 
+                  }
+                }
+              }
+            }
+          }
+        }
+        // transfer results onto panel
 
-    });
+      }
+      /**
+       * alignments. New alignments are added to dataset, and subsequently
+       * annotated/visualised accordingly. 1. New alignment frame created for
+       * new alignment. Decide if any vis settings should be inherited from old
+       * alignment frame (e.g. sequence ordering ?). 2. Subsequent data added to
+       * alignment as below:
+       */
+      /**
+       * annotation update to original/newly created context alignment: 1.
+       * identify alignment where annotation is to be loaded onto. 2. Add
+       * annotation, excluding any duplicates. 3. Ensure annotation is visible
+       * on alignment - honouring ordering given by file.
+       */
+      /**
+       * features updated to original or newly created context alignment: 1.
+       * Features are(or were already) added to dataset. 2. Feature settings
+       * modified to ensure all features are displayed - honouring any ordering
+       * given by result file. Consider merging action with the code used by the
+       * DAS fetcher to update alignment views with new info.
+       */
+      /**
+       * Seq associated data files (PDB files). 1. locate seq association in
+       * current dataset/alignment context and add file as normal - keep handle
+       * of any created ref objects. 2. decide if new data should be displayed :
+       * PDB display: if alignment has PDB display already, should new pdb files
+       * be aligned to it ?
+       * 
+       */
 
-    wsInfo.setResultsReady();
+    }
+    // destPanel.adjustAnnotationHeight();
 
   }