JAL-3807 - Re-implement results processing.
[jalview.git] / src / jalview / ws / JPredThread.java
1 package jalview.ws;
2
3 import static java.lang.String.format;
4
5 import java.util.Hashtable;
6 import java.util.List;
7
8 import jalview.analysis.SeqsetUtils;
9 import jalview.bin.Cache;
10 import jalview.datamodel.Alignment;
11 import jalview.datamodel.AlignmentAnnotation;
12 import jalview.datamodel.AlignmentI;
13 import jalview.datamodel.AlignmentView;
14 import jalview.datamodel.HiddenColumns;
15 import jalview.datamodel.SequenceI;
16 import jalview.gui.AlignFrame;
17 import jalview.gui.Desktop;
18 import jalview.gui.WebserviceInfo;
19 import jalview.io.JPredFile;
20 import jalview.io.JnetAnnotationMaker;
21 import jalview.util.MessageManager;
22 import jalview.ws.api.CancellableI;
23 import jalview.ws.api.JPredMutlipleAlignmentServiceI;
24 import jalview.ws.gui.WsJob;
25 import jalview.ws.gui.WsJob.JobState;
26
27
28 class JPredJob extends WsJob
29 {
30   Hashtable<?, ?> sequenceInfo;
31   List<SequenceI> msf;
32   int[] delMap;
33   AlignmentI alignment = null;
34   HiddenColumns hiddenCols = null;
35
36   public JPredJob(Hashtable<?, ?> sequenceInfo, SequenceI[] msf, int[] delMap)
37   {
38     this.sequenceInfo = sequenceInfo;
39     this.msf = List.of(msf);
40     this.delMap = delMap;
41   }
42
43   @Override
44   public boolean hasValidInput()
45   {
46     return true;
47   }
48
49   @Override
50   public boolean hasResults()
51   {
52     return (isSubjobComplete() && alignment != null);
53   }
54 }
55
56
57 public class JPredThread extends AWSThread implements WSClientI
58 {
59
60   private JPredMutlipleAlignmentServiceI server;
61   private String title;
62   private Hashtable<?, ?> sequenceInfo;
63   private SequenceI[] msf;
64   private int[] delMap;
65
66   public JPredThread(WebserviceInfo wsInfo, String title,
67       JPredMutlipleAlignmentServiceI server, Hashtable<?, ?> sequenceInfo,
68       SequenceI[] msf, int[] delMap, AlignmentView view, AlignFrame frame,
69       String wsURL)
70   {
71     super(frame, wsInfo, view, wsURL);
72     this.server = server;
73     this.title = title;
74     this.sequenceInfo = sequenceInfo;
75     this.msf = msf;
76     this.delMap = delMap;
77     JPredJob job = new JPredJob(sequenceInfo, msf, delMap);
78     this.jobs = new JPredJob[] { job };
79   }
80
81   @Override
82   public boolean isCancellable()
83   {
84     return server instanceof CancellableI;
85   }
86
87   @Override
88   public boolean canMergeResults()
89   {
90     return false;
91   }
92
93   @Override
94   public void cancelJob()
95   {
96     // TODO Auto-generated method stub
97
98   }
99
100   @Override
101   public void pollJob(AWsJob job_) throws Exception
102   {
103     var job = (JPredJob) job_;
104     server.updateStatus(job);
105     server.updateJobProgress(job);
106   }
107
108   @Override
109   public void StartJob(AWsJob job_)
110   {
111     if (!(job_ instanceof JPredJob))
112       throw new RuntimeException("Invalid job type");
113     var job = (JPredJob) job_;
114     if (job.isSubmitted())
115     {
116       return;
117     }
118     try {
119       try
120       {
121         var jobHandle = server.align(job.msf);
122         if (jobHandle != null)
123           job.setJobHandle(jobHandle);
124       }
125       catch (Throwable th) {
126         if (!server.handleSubmitError(th, job, wsInfo)) {
127           throw th;
128         }
129       }
130       if (job.getJobId() != null) {
131         job.setSubmitted(true);
132         job.setSubjobComplete(false);
133         return;
134       }
135       else {
136         throw new Exception(MessageManager.formatMessage(
137                 "exception.web_service_returned_null_try_later",
138                 new String[]
139                 { WsUrl }));
140       }
141     }
142     catch (Throwable th)
143     {
144       // For unexpected errors
145       System.err.println(WebServiceName
146               + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
147               + "When contacting Server:" + WsUrl + "\n");
148       th.printStackTrace(System.err);
149       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
150       wsInfo.setStatus(job.getJobnum(),
151               WebserviceInfo.STATE_STOPPED_SERVERERROR);
152
153     }
154     finally
155     {
156       if (!job.isSubmitted())
157       {
158         job.setAllowedServerExceptions(0);
159         wsInfo.appendProgressText(job.getJobnum(), MessageManager.getString(
160                 "info.failed_to_submit_sequences_for_alignment"));
161       }
162     }
163   }
164
165   @Override
166   public void parseResult()
167   {
168     long progbar = (long) (Math.random() * ~(1L << 63));
169     wsInfo.setProgressBar(
170         MessageManager.getString("status.collecting_job_results"), progbar);
171     int results = 0;
172     var finalState = new JobStateSummary();
173     try
174     {
175       for (int i = 0; i < jobs.length; i++) {
176         final var job = (JPredJob) jobs[i];
177         finalState.updateJobPanelState(wsInfo, OutputHeader, job);
178         if (job.isFinished()) {
179           try {
180             server.updateJobProgress(job);
181           }
182           catch (Exception e) {
183             Cache.log.warn(format(
184                 "Exception when retrieving remaining Job progress data " +
185                 "for job %s on server %s", job.getJobId(), WsUrl));
186             e.printStackTrace();
187           }
188           // removed the waiting loop
189           Cache.log.debug(format("Job Execution file for job: %s " +
190               "on server %s%n%s", job.getJobId(), WsUrl, job.getStatus()));
191           try {
192             prepareJobResult(job);
193           }
194           catch (Exception e) {
195             if (!server.handleCollectionException(e, job, wsInfo)) {
196               Cache.log.error("Could not get alignment for job.", e);
197               job.setState(JobState.SERVERERROR);
198             }
199           }
200         }
201         finalState.updateJobPanelState(wsInfo, OutputHeader, job);
202         if (job.isSubmitted() && job.isSubjobComplete() && job.hasResults()) {
203           results++;
204         }
205       }
206     }
207     catch (Exception e) {
208       Cache.log.error(
209           "Unexpected exception when processing results for " + title, e);
210       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
211     }
212     if (results > 0) {
213       wsInfo.showResultsNewFrame.addActionListener(
214           (evt) -> displayResults(true));
215       wsInfo.mergeResults.addActionListener(
216           (evt) -> displayResults(false));
217       wsInfo.setResultsReady();
218     }
219     else {
220       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
221       wsInfo.appendInfoText("No jobs ran.");
222       wsInfo.setFinishedNoResults();
223     }
224     updateGlobalStatus(finalState);
225     wsInfo.removeProgressBar(progbar);
226   }
227
228   private void prepareJobResult(JPredJob job) throws Exception
229   {
230     HiddenColumns hiddenCols = null;
231     int firstSeq = -1;
232     AlignmentI alignment;
233     var prediction = server.getPrediction(job.getJobHandle());
234     var preds = prediction.getSeqsAsArray();
235
236     if (job.delMap != null)
237     {
238       Object[] alandcolsel = input
239               .getAlignmentAndHiddenColumns(getGapChar());
240       alignment = new Alignment((SequenceI[]) alandcolsel[0]);
241       hiddenCols = (HiddenColumns) alandcolsel[1];
242     }
243     else
244     {
245       alignment = server.getAlignment(job.getJobHandle());
246       var seqs = new SequenceI[alignment.getHeight()];
247       for (int i = 0; i < alignment.getHeight(); i++)
248       {
249         seqs[i] = alignment.getSequenceAt(i);
250       }
251       if (!SeqsetUtils.deuniquify(sequenceInfo, seqs))
252       {
253         throw (new Exception(MessageManager.getString(
254                 "exception.couldnt_recover_sequence_properties_for_alignment")));
255       }
256     }
257     firstSeq = 0;
258     if (currentView.getDataset() != null)
259     {
260       alignment.setDataset(currentView.getDataset());
261     }
262     else
263     {
264       alignment.setDataset(null);
265     }
266     JnetAnnotationMaker.add_annotation(prediction, alignment, firstSeq, false,
267             job.delMap);
268
269     for (var annot : alignment.getAlignmentAnnotation())
270     {
271       if (annot.sequenceRef != null)
272       {
273         replaceAnnotationOnAlignmentWith(annot, annot.label,
274                 "jalview.ws.JPred", annot.sequenceRef);
275       }
276     }
277     job.alignment = alignment;
278     job.hiddenCols = hiddenCols;
279   }
280
281   private static void replaceAnnotationOnAlignmentWith(
282           AlignmentAnnotation newAnnot, String typeName, String calcId,
283           SequenceI aSeq)
284   {
285     SequenceI dsseq = aSeq.getDatasetSequence();
286     while (dsseq.getDatasetSequence() != null)
287     {
288       dsseq = dsseq.getDatasetSequence();
289     }
290     // look for same annotation on dataset and lift this one over
291     List<AlignmentAnnotation> dsan = dsseq.getAlignmentAnnotations(calcId,
292             typeName);
293     if (dsan != null && dsan.size() > 0)
294     {
295       for (AlignmentAnnotation dssan : dsan)
296       {
297         dsseq.removeAlignmentAnnotation(dssan);
298       }
299     }
300     AlignmentAnnotation dssan = new AlignmentAnnotation(newAnnot);
301     dsseq.addAlignmentAnnotation(dssan);
302     dssan.adjustForAlignment();
303   }
304
305   private void displayResults(boolean newWindow)
306   {
307     if (jobs == null || jobs.length == 0)
308     {
309       return;
310     }
311     var job = (JPredJob) jobs[0];
312     if (job.hasResults() && newWindow)
313     {
314       AlignFrame frame;
315       job.alignment.setSeqrep(job.alignment.getSequenceAt(0));
316       frame = new AlignFrame(job.alignment, job.hiddenCols,
317               AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
318       Desktop.addInternalFrame(frame, title, AlignFrame.DEFAULT_WIDTH,
319               AlignFrame.DEFAULT_HEIGHT);
320     }
321   }
322
323 }