From bc0c5127aee38909e972dc06233441b4ab0da7d5 Mon Sep 17 00:00:00 2001 From: Mateusz Warowny Date: Tue, 30 May 2023 17:41:34 +0200 Subject: [PATCH] JAL-3954 reuse spinner dialog to show search task status --- resources/lang/Messages.properties | 1 + src/jalview/ws2/client/ebi/PhmmerWSClient.java | 2 + src/jalview/ws2/gui/SearchServiceGuiHandler.java | 211 ++++++++++++++++++---- src/jalview/ws2/gui/WebServicesMenuManager.java | 2 +- 4 files changed, 183 insertions(+), 33 deletions(-) diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index f8c2b68..59c2bd3 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -1109,6 +1109,7 @@ info.alignment_object_method_notes = \nAlignment Object Method Notes\n info.server_exception = \n{0} Server exception\!\n{1} info.invalid_msa_input_mininfo = Need at least two sequences with at least 3 residues each, with no hidden regions between them. info.invalid_msa_notenough = Not enough sequence data to align +info.invalid_search_input = Invalid input for sequence search. status.processing_commandline_args = Processing commandline arguments... status.das_features_being_retrived = DAS features being retrieved... status.searching_for_sequences_from = Searching for sequences from {0} diff --git a/src/jalview/ws2/client/ebi/PhmmerWSClient.java b/src/jalview/ws2/client/ebi/PhmmerWSClient.java index 9c79644..0bcb2f9 100644 --- a/src/jalview/ws2/client/ebi/PhmmerWSClient.java +++ b/src/jalview/ws2/client/ebi/PhmmerWSClient.java @@ -5,6 +5,7 @@ import java.io.StringReader; import java.net.URI; import java.util.List; +import jalview.bin.Console; import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; import jalview.io.DataSourceType; @@ -58,6 +59,7 @@ public class PhmmerWSClient implements AlignmentWebServiceClientI var email = credentials.getEmail() != null ? credentials.getEmail() : "nouser@jalview.org"; var jobId = client.submit(request.build(), email); + Console.debug("Phmmer client submitted new job with id " + jobId); return new WebServiceJobHandle( getClientName(), "phmmer", getUrl(), jobId); } diff --git a/src/jalview/ws2/gui/SearchServiceGuiHandler.java b/src/jalview/ws2/gui/SearchServiceGuiHandler.java index b3ae5c1..519adc7 100644 --- a/src/jalview/ws2/gui/SearchServiceGuiHandler.java +++ b/src/jalview/ws2/gui/SearchServiceGuiHandler.java @@ -2,94 +2,241 @@ package jalview.ws2.gui; import java.util.List; +import javax.swing.SwingUtilities; + import jalview.bin.Console; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.gui.AlignFrame; import jalview.gui.Desktop; +import jalview.gui.JvOptionPane; +import jalview.gui.WebserviceInfo; +import jalview.util.ArrayUtils; +import jalview.util.MessageManager; +import jalview.ws2.actions.api.ActionI; import jalview.ws2.actions.api.JobI; import jalview.ws2.actions.api.TaskEventListener; import jalview.ws2.actions.api.TaskI; import jalview.ws2.api.JobStatus; +import jalview.ws2.api.WebService; +import jalview.ws2.helpers.WSClientTaskWrapper; + +import static java.lang.String.format; -public class SearchServiceGuiHandler implements TaskEventListener +class SearchServiceGuiHandler implements TaskEventListener { private final AlignFrame parentFrame; - - public SearchServiceGuiHandler(AlignFrame parentFrame) + + private final ActionI action; + + private final WebService service; + + private WebserviceInfo infoPanel; + + private JobI[] jobs = new JobI[0]; + + private int[] tabs = new int[0]; + + private int[] logOffset = new int[0]; + + private int[] errLogOffset = new int[0]; + + public SearchServiceGuiHandler(ActionI action, AlignFrame parentFrame) { this.parentFrame = parentFrame; + this.action = action; + this.service = action.getWebService(); + var info = String.format("%s search using service at %s%n%s", + service.getName(), service.getUrl(), service.getDescription()); + this.infoPanel = new WebserviceInfo(service.getName(), info, false); } @Override public void taskStarted(TaskI source, List subJobs) { - Console.info("task started with " + subJobs.size() + " jobs"); - // TODO Auto-generated method stub - + Console.debug(format("task %s#%x started with %d sub-jobs", + service.getName(), source.getUid(), subJobs.size())); + jobs = subJobs.toArray(new JobI[subJobs.size()]); + tabs = new int[subJobs.size()]; + logOffset = new int[subJobs.size()]; + errLogOffset = new int[subJobs.size()]; + for (int i = 0; i < subJobs.size(); i++) + { + JobI job = jobs[i]; + int tabIndex = infoPanel.addJobPane(); + tabs[i] = tabIndex; + infoPanel.setProgressName(format("region %d", i), tabIndex); + infoPanel.setProgressText(tabIndex, "Job details:\n"); + // jobs should not have states other than invalid or ready at this point + if (job.getStatus() == JobStatus.INVALID) + infoPanel.setStatus(tabIndex, WebserviceInfo.STATE_STOPPED_OK); + else if (job.getStatus() == JobStatus.READY) + infoPanel.setStatus(tabIndex, WebserviceInfo.STATE_QUEUING); + } } @Override public void taskStatusChanged(TaskI source, JobStatus status) { - Console.info("task status " + status); - // TODO Auto-generated method stub - + Console.debug(format("task %s#%x status changed to %s", + service.getName(), source.getUid(), status)); + switch (status) + { + case INVALID: + infoPanel.setVisible(false); + JvOptionPane.showMessageDialog(parentFrame, + MessageManager.getString("info.invalid_search_input"), + MessageManager.getString("info.invalid_search_input"), + JvOptionPane.INFORMATION_MESSAGE); + break; + case READY: + infoPanel.setthisService(new WSClientTaskWrapper(source)); + infoPanel.setVisible(true); + // intentional no break + case SUBMITTED: + case QUEUED: + infoPanel.setStatus(WebserviceInfo.STATE_QUEUING); + break; + case RUNNING: + case UNKNOWN: // unsure what to do with unknown + infoPanel.setStatus(WebserviceInfo.STATE_RUNNING); + break; + case COMPLETED: + infoPanel.setProgressBar( + MessageManager.getString("status.collecting_job_results"), + jobs[0].getInternalId()); + infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_OK); + break; + case FAILED: + infoPanel.removeProgressBar(jobs[0].getInternalId()); + infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_ERROR); + break; + case CANCELLED: + infoPanel.setStatus(WebserviceInfo.STATE_CANCELLED_OK); + break; + case SERVER_ERROR: + infoPanel.removeProgressBar(jobs[0].getInternalId()); + infoPanel.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR); + break; + } } @Override public void taskCompleted(TaskI source, AlignmentI result) { - Console.info("task completed"); - displayResultsNewFrame(result); + Console.debug(format("task %s#%x completed", service.getName(), + source.getUid())); + SwingUtilities.invokeLater( + () -> infoPanel.removeProgressBar(jobs[0].getInternalId())); + if (result == null) + { + SwingUtilities.invokeLater(infoPanel::setFinishedNoResults); + return; + } + infoPanel.showResultsNewFrame.addActionListener(evt -> { + // copy alignment for each frame to have its own instance + var alnCpy = new Alignment(result); + alnCpy.setGapCharacter(result.getGapCharacter()); + alnCpy.setDataset(result.getDataset()); + for (AlignmentAnnotation annotation : result.getAlignmentAnnotation()) + alnCpy.addAnnotation(new AlignmentAnnotation(annotation)); + displayResultsNewFrame(alnCpy); + }); + SwingUtilities.invokeLater(infoPanel::setResultsReady); + } + + private void displayResultsNewFrame(AlignmentI aln) + { + AlignFrame frame = new AlignFrame(aln, AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + frame.getFeatureRenderer().transferSettings( + parentFrame.getFeatureRenderer().getSettings()); + var actionName = action.getName() != null ? action.getName() : "Search"; + var title = String.format("%s %s of %s", service.getName(), actionName, + parentFrame.getTitle()); + Desktop.addInternalFrame(frame, title, AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); } @Override public void taskException(TaskI source, Exception e) { - Console.info("task failed", e); - // TODO Auto-generated method stub - + Console.error(format("Task %s#%x raised an exception.", + service.getName(), source.getUid()), e); + infoPanel.appendProgressText(e.getMessage()); } @Override public void taskRestarted(TaskI source) { - Console.info("task restarted"); - // TODO Auto-generated method stub - + // search services non-restartable } @Override public void subJobStatusChanged(TaskI source, JobI job, JobStatus status) { - Console.info("sub-job " + job.getInternalId() + " status " + status); - // TODO Auto-generated method stub - + Console.debug(format("sub-job %x status changed to %s", + job.getInternalId(), status)); + int i = ArrayUtils.indexOf(jobs, job); + assert i >= 0 : "job does not exist"; + if (i < 0) + // safeguard that should not happen irl + return; + int wsStatus; + switch (status) + { + case INVALID: + case COMPLETED: + wsStatus = WebserviceInfo.STATE_STOPPED_OK; + break; + case READY: + case SUBMITTED: + case QUEUED: + wsStatus = WebserviceInfo.STATE_QUEUING; + break; + case RUNNING: + case UNKNOWN: + wsStatus = WebserviceInfo.STATE_RUNNING; + break; + case FAILED: + wsStatus = WebserviceInfo.STATE_STOPPED_ERROR; + break; + case CANCELLED: + wsStatus = WebserviceInfo.STATE_CANCELLED_OK; + break; + case SERVER_ERROR: + wsStatus = WebserviceInfo.STATE_STOPPED_SERVERERROR; + break; + default: + throw new AssertionError("Non-exhaustive switch statement"); + } + infoPanel.setStatus(tabs[i], wsStatus); } @Override public void subJobLogChanged(TaskI source, JobI job, String log) { - // TODO Auto-generated method stub - + int i = ArrayUtils.indexOf(jobs, job); + assert i >= 0 : "job does not exist"; + if (i < 0) + // safeguard that should never happen + return; + infoPanel.appendProgressText(tabs[i], log.substring(logOffset[i])); } @Override public void subJobErrorLogChanged(TaskI source, JobI job, String log) { - // TODO Auto-generated method stub - - } - - private void displayResultsNewFrame(AlignmentI aln) - { - AlignFrame frame = new AlignFrame(aln, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); - frame.getFeatureRenderer().transferSettings( - parentFrame.getFeatureRenderer().getSettings()); - Desktop.addInternalFrame(frame, "title", AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); + int i = ArrayUtils.indexOf(jobs, job); + assert i >= 0 : "job does not exist"; + if (i < 0) + // safeguard that should never happen + return; + infoPanel.appendProgressText(tabs[i], log.substring(errLogOffset[i])); } } diff --git a/src/jalview/ws2/gui/WebServicesMenuManager.java b/src/jalview/ws2/gui/WebServicesMenuManager.java index 8e2dcee..12aeaa9 100644 --- a/src/jalview/ws2/gui/WebServicesMenuManager.java +++ b/src/jalview/ws2/gui/WebServicesMenuManager.java @@ -474,7 +474,7 @@ public class WebServicesMenuManager if (action instanceof PhmmerAction) { var _action = (PhmmerAction) action; - var handler = new SearchServiceGuiHandler(frame); + var handler = new SearchServiceGuiHandler(_action, frame); return _action.perform(viewport, args, credentials, handler); } Console.warn(String.format( -- 1.7.10.2