1 package jalview.ws.jws2;
3 import static java.lang.String.format;
5 import java.util.Hashtable;
9 import jalview.analysis.SeqsetUtils;
10 import jalview.analysis.SeqsetUtils.SequenceInfo;
11 import jalview.bin.Cache;
12 import jalview.commands.RemoveGapsCommand;
13 import jalview.datamodel.Alignment;
14 import jalview.datamodel.AlignmentAnnotation;
15 import jalview.datamodel.AlignmentI;
16 import jalview.datamodel.AlignmentView;
17 import jalview.datamodel.HiddenColumns;
18 import jalview.datamodel.SequenceI;
19 import jalview.gui.AlignFrame;
20 import jalview.gui.Desktop;
21 import jalview.gui.WebserviceInfo;
22 import jalview.io.JnetAnnotationMaker;
23 import jalview.util.MessageManager;
24 import jalview.ws.AWSThread;
25 import jalview.ws.AWsJob;
26 import jalview.ws.JobStateSummary;
27 import jalview.ws.WSClientI;
28 import jalview.ws.api.CancellableI;
29 import jalview.ws.api.JPredServiceI;
30 import jalview.ws.gui.WsJob;
31 import jalview.ws.gui.WsJob.JobState;
34 public class JPredThread extends AWSThread implements WSClientI
37 private static class JPredJob extends WsJob
39 private final Object sequenceInfo;
40 private final List<SequenceI> msf;
41 private final int[] delMap;
42 private AlignmentI alignment = null;
43 private HiddenColumns hiddenCols = null;
45 private JPredJob(Object sequenceInfo, SequenceI[] msf, int[] delMap)
47 this.sequenceInfo = sequenceInfo;
48 this.msf = List.of(msf);
53 public boolean hasValidInput()
59 public boolean hasResults()
61 return (isSubjobComplete() && alignment != null);
64 public boolean isMSA()
66 return msf.size() > 1;
71 private JPredServiceI server;
73 private Object sequenceInfo;
74 private SequenceI[] msf;
77 public JPredThread(WebserviceInfo wsInfo, String title,
78 JPredServiceI server, Object sequenceInfo,
79 SequenceI[] msf, int[] delMap, AlignmentView view, AlignFrame frame,
82 super(frame, wsInfo, view, wsURL);
85 this.sequenceInfo = sequenceInfo;
88 JPredJob job = new JPredJob(sequenceInfo, msf, delMap);
89 this.jobs = new JPredJob[] { job };
93 public boolean isCancellable()
95 return server instanceof CancellableI;
99 public boolean canMergeResults()
105 public void cancelJob()
107 // TODO Auto-generated method stub
112 public void pollJob(AWsJob job_) throws Exception
114 var job = (JPredJob) job_;
115 server.updateStatus(job);
116 server.updateJobProgress(job);
120 public void StartJob(AWsJob job_)
122 if (!(job_ instanceof JPredJob))
123 throw new RuntimeException("Invalid job type");
124 var job = (JPredJob) job_;
125 if (job.isSubmitted())
132 var jobHandle = server.predict(job.msf, job.isMSA());
133 if (jobHandle != null)
134 job.setJobHandle(jobHandle);
136 catch (Throwable th) {
137 if (!server.handleSubmitError(th, job, wsInfo)) {
141 if (job.getJobId() != null) {
142 job.setSubmitted(true);
143 job.setSubjobComplete(false);
147 throw new Exception(MessageManager.formatMessage(
148 "exception.web_service_returned_null_try_later",
155 // For unexpected errors
156 System.err.println(WebServiceName
157 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
158 + "When contacting Server:" + WsUrl + "\n");
159 th.printStackTrace(System.err);
160 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
161 wsInfo.setStatus(job.getJobnum(),
162 WebserviceInfo.STATE_STOPPED_SERVERERROR);
167 if (!job.isSubmitted())
169 job.setAllowedServerExceptions(0);
170 wsInfo.appendProgressText(job.getJobnum(), MessageManager.getString(
171 "info.failed_to_submit_sequences_for_alignment"));
177 public void parseResult()
179 long progbar = (long) (Math.random() * ~(1L << 63));
180 wsInfo.setProgressBar(
181 MessageManager.getString("status.collecting_job_results"), progbar);
183 var finalState = new JobStateSummary();
186 for (int i = 0; i < jobs.length; i++) {
187 final var job = (JPredJob) jobs[i];
188 finalState.updateJobPanelState(wsInfo, OutputHeader, job);
189 if (job.isFinished()) {
191 server.updateJobProgress(job);
193 catch (Exception e) {
194 Cache.log.warn(format(
195 "Exception when retrieving remaining Job progress data " +
196 "for job %s on server %s", job.getJobId(), WsUrl));
199 // removed the waiting loop
200 Cache.log.debug(format("Job Execution file for job: %s " +
201 "on server %s%n%s", job.getJobId(), WsUrl, job.getStatus()));
203 prepareJobResult(job);
205 catch (Exception e) {
206 if (!server.handleCollectionException(e, job, wsInfo)) {
207 Cache.log.error("Could not get alignment for job.", e);
208 job.setState(JobState.SERVERERROR);
212 finalState.updateJobPanelState(wsInfo, OutputHeader, job);
213 if (job.isSubmitted() && job.isSubjobComplete() && job.hasResults()) {
218 catch (Exception e) {
220 "Unexpected exception when processing results for " + title, e);
221 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
224 wsInfo.showResultsNewFrame.addActionListener(
225 (evt) -> displayResults(true));
226 wsInfo.mergeResults.addActionListener(
227 (evt) -> displayResults(false));
228 wsInfo.setResultsReady();
231 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
232 wsInfo.appendInfoText("No jobs ran.");
233 wsInfo.setFinishedNoResults();
235 updateGlobalStatus(finalState);
236 wsInfo.removeProgressBar(progbar);
239 static final int msaIndex = 0;
241 private void prepareJobResult(JPredJob job) throws Exception
243 HiddenColumns hiddenCols = null;
245 AlignmentI alignment;
246 var prediction = server.getPrediction(job.getJobHandle());
247 var preds = prediction.getSeqsAsArray();
249 if (job.msf.size() > 1)
251 if (job.delMap != null)
253 Object[] alandcolsel = input
254 .getAlignmentAndHiddenColumns(getGapChar());
255 alignment = new Alignment((SequenceI[]) alandcolsel[0]);
256 hiddenCols = (HiddenColumns) alandcolsel[1];
260 alignment = server.getAlignment(job.getJobHandle());
261 var seqs = new SequenceI[alignment.getHeight()];
262 for (int i = 0; i < alignment.getHeight(); i++)
264 seqs[i] = alignment.getSequenceAt(i);
266 if (!SeqsetUtils.deuniquify((Map<String, SequenceInfo>)sequenceInfo, seqs))
268 throw (new Exception(MessageManager.getString(
269 "exception.couldnt_recover_sequence_properties_for_alignment")));
273 if (currentView.getDataset() != null)
275 alignment.setDataset(currentView.getDataset());
279 alignment.setDataset(null);
281 JnetAnnotationMaker.add_annotation(prediction, alignment, firstSeq, false,
286 alignment = new Alignment(preds);
287 firstSeq = prediction.getQuerySeqPosition();
288 if (job.delMap != null)
290 Object[] alanndcolsel = input.getAlignmentAndHiddenColumns(getGapChar());
291 SequenceI[] seqs = (SequenceI[]) alanndcolsel[0];
292 new RemoveGapsCommand(MessageManager.getString("label.remove_gaps"),
293 new SequenceI[] {seqs[msaIndex]}, currentView);
294 SequenceI profileSeq = alignment.getSequenceAt(firstSeq);
295 profileSeq.setSequence(seqs[msaIndex].getSequenceAsString());
297 if (!SeqsetUtils.SeqCharacterUnhash(
298 alignment.getSequenceAt(firstSeq), (SequenceInfo)sequenceInfo))
300 throw new Exception(MessageManager.getString(
301 "exception.couldnt_recover_sequence_props_for_jnet_query"));
303 alignment.setDataset(currentView.getDataset());
304 JnetAnnotationMaker.add_annotation(prediction, alignment, firstSeq, true,
306 SequenceI profileSeq = alignment.getSequenceAt(0);
307 alignToProfileSeq(alignment, profileSeq);
308 if (job.delMap != null)
310 hiddenCols = alignment.propagateInsertions(profileSeq, input);
314 for (var annot : alignment.getAlignmentAnnotation())
316 if (annot.sequenceRef != null)
318 replaceAnnotationOnAlignmentWith(annot, annot.label,
319 "jalview.ws.JPred", annot.sequenceRef);
322 job.alignment = alignment;
323 job.hiddenCols = hiddenCols;
326 private static void replaceAnnotationOnAlignmentWith(
327 AlignmentAnnotation newAnnot, String typeName, String calcId,
330 SequenceI dsseq = aSeq.getDatasetSequence();
331 while (dsseq.getDatasetSequence() != null)
333 dsseq = dsseq.getDatasetSequence();
335 // look for same annotation on dataset and lift this one over
336 List<AlignmentAnnotation> dsan = dsseq.getAlignmentAnnotations(calcId,
338 if (dsan != null && dsan.size() > 0)
340 for (AlignmentAnnotation dssan : dsan)
342 dsseq.removeAlignmentAnnotation(dssan);
345 AlignmentAnnotation dssan = new AlignmentAnnotation(newAnnot);
346 dsseq.addAlignmentAnnotation(dssan);
347 dssan.adjustForAlignment();
350 private static void alignToProfileSeq(AlignmentI al, SequenceI profileseq)
352 char gc = al.getGapCharacter();
353 int[] gapMap = profileseq.gapMap();
354 // insert gaps into profile
355 for (int lp = 0, r = 0; r < gapMap.length; r++)
357 if (gapMap[r] - lp > 1)
359 StringBuffer sb = new StringBuffer();
360 for (int s = 0, ns = gapMap[r] - lp; s < ns; s++)
364 for (int s = 1, ns = al.getHeight(); s < ns; s++)
366 String sq = al.getSequenceAt(s).getSequenceAsString();
367 int diff = gapMap[r] - sq.length();
372 while ((diff = gapMap[r] - sq.length()) > 0)
374 sq = sq + ((diff >= sb.length()) ? sb.toString()
375 : sb.substring(0, diff));
377 al.getSequenceAt(s).setSequence(sq);
381 al.getSequenceAt(s).setSequence(sq.substring(0, gapMap[r])
382 + sb.toString() + sq.substring(gapMap[r]));
390 private void displayResults(boolean newWindow)
392 if (jobs == null || jobs.length == 0)
396 var job = (JPredJob) jobs[0];
397 if (job.hasResults() && newWindow)
399 job.alignment.setSeqrep(job.alignment.getSequenceAt(0));
400 AlignFrame frame = new AlignFrame(job.alignment, job.hiddenCols,
401 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
402 Desktop.addInternalFrame(frame, title, AlignFrame.DEFAULT_WIDTH,
403 AlignFrame.DEFAULT_HEIGHT);