From 872210cd099a8d81f9427fd3547775c8ec043a38 Mon Sep 17 00:00:00 2001 From: Mateusz Warowny Date: Fri, 12 Feb 2021 17:19:54 +0100 Subject: [PATCH] JAL-3807 - Add single sequence capabilities to jws2.JPredClient --- ...leAlignmentServiceI.java => JPredServiceI.java} | 4 +- src/jalview/ws/jws2/JPredClient.java | 32 ++-- src/jalview/ws/jws2/JPredThread.java | 183 ++++++++++++++------ .../ws/slivkaws/SlivkaJPredServiceInstance.java | 37 +++- 4 files changed, 182 insertions(+), 74 deletions(-) rename src/jalview/ws/api/{JPredMutlipleAlignmentServiceI.java => JPredServiceI.java} (66%) diff --git a/src/jalview/ws/api/JPredMutlipleAlignmentServiceI.java b/src/jalview/ws/api/JPredServiceI.java similarity index 66% rename from src/jalview/ws/api/JPredMutlipleAlignmentServiceI.java rename to src/jalview/ws/api/JPredServiceI.java index f897140..5ef159d 100644 --- a/src/jalview/ws/api/JPredMutlipleAlignmentServiceI.java +++ b/src/jalview/ws/api/JPredServiceI.java @@ -6,9 +6,9 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; import jalview.io.JPredFile; -public interface JPredMutlipleAlignmentServiceI extends JalviewWebServiceI +public interface JPredServiceI extends JalviewWebServiceI { - public JobId align(List sequences) throws Throwable; + public JobId predict(List sequences, boolean msa) throws Throwable; public AlignmentI getAlignment(JobId jobId) throws Exception; diff --git a/src/jalview/ws/jws2/JPredClient.java b/src/jalview/ws/jws2/JPredClient.java index 68bcb6c..1413dfe 100644 --- a/src/jalview/ws/jws2/JPredClient.java +++ b/src/jalview/ws/jws2/JPredClient.java @@ -9,17 +9,17 @@ import jalview.datamodel.SequenceI; import jalview.gui.AlignFrame; import jalview.ws.WSClient; import jalview.ws.WSMenuEntryProviderI; -import jalview.ws.api.JPredMutlipleAlignmentServiceI; +import jalview.ws.api.JPredServiceI; import jalview.ws.api.ServiceWithParameters; public class JPredClient extends WSClient { - JPredMutlipleAlignmentServiceI server; + JPredServiceI server; public JPredClient(ServiceWithParameters sh, String title, AlignmentView alView, AlignFrame alFrame, boolean viewOnly) { - server = (JPredMutlipleAlignmentServiceI) sh.getEndpoint(); + server = (JPredServiceI) sh.getEndpoint(); wsInfo = setWebService(sh, false); startClient(title, alView, alFrame, viewOnly); } @@ -29,16 +29,23 @@ public class JPredClient extends WSClient { var msf = view.getSequences(); var seq = msf[0].getSeq('-'); - if (msf.length <= 1) - throw new RuntimeException("You need more than one sequence."); + int[] delMap = null; + if (viewOnly) + delMap = view.getVisibleContigMapFor(seq.gapMap()); var aln = new SequenceI[msf.length]; for (int i = 0; i < msf.length; i++) { aln[i] = msf[i].getSeq('-'); } - int[] delMap = viewOnly ? - view.getVisibleContigMapFor(seq.gapMap()) : null; - var sequenceInfo = SeqsetUtils.uniquify(aln, true); + var sequenceInfo = msf.length > 1 ? SeqsetUtils.uniquify(aln, true) + : SeqsetUtils.SeqCharacterHash(seq); + if (viewOnly) + { + String seqs[] = view.getSequenceStrings('-'); + for (int i = 0; i < msf.length; i++) + aln[i].setSequence(seqs[i]); + seq.setSequence(seqs[0]); + } var thread = new JPredThread(wsInfo, title, server, sequenceInfo, aln, delMap, view, frame, WsURL); wsInfo.setthisService(thread); @@ -54,14 +61,7 @@ public class JPredClient extends WSClient mi.setToolTipText(sh.getHostURL()); mi.addActionListener((event) -> { var view = frame.gatherSeqOrMsaForSecStrPrediction(); - if (view.getSequences().length > 1) - { - new JPredClient(sh, frame.getTitle(), view, frame, true); - } - else - { - Cache.log.error("Single sequence not supported"); - } + new JPredClient(sh, frame.getTitle(), view, frame, true); }); menu.add(mi); }; diff --git a/src/jalview/ws/jws2/JPredThread.java b/src/jalview/ws/jws2/JPredThread.java index 087e9d4..d10ca8a 100644 --- a/src/jalview/ws/jws2/JPredThread.java +++ b/src/jalview/ws/jws2/JPredThread.java @@ -7,6 +7,7 @@ import java.util.List; import jalview.analysis.SeqsetUtils; import jalview.bin.Cache; +import jalview.commands.RemoveGapsCommand; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; @@ -24,51 +25,56 @@ import jalview.ws.AWsJob; import jalview.ws.JobStateSummary; import jalview.ws.WSClientI; import jalview.ws.api.CancellableI; -import jalview.ws.api.JPredMutlipleAlignmentServiceI; +import jalview.ws.api.JPredServiceI; import jalview.ws.gui.WsJob; import jalview.ws.gui.WsJob.JobState; -class JPredJob extends WsJob +public class JPredThread extends AWSThread implements WSClientI { - Hashtable sequenceInfo; - List msf; - int[] delMap; - AlignmentI alignment = null; - HiddenColumns hiddenCols = null; - public JPredJob(Hashtable sequenceInfo, SequenceI[] msf, int[] delMap) + private static class JPredJob extends WsJob { - this.sequenceInfo = sequenceInfo; - this.msf = List.of(msf); - this.delMap = delMap; - } + private final Hashtable sequenceInfo; + private final List msf; + private final int[] delMap; + private AlignmentI alignment = null; + private HiddenColumns hiddenCols = null; - @Override - public boolean hasValidInput() - { - return true; - } + private JPredJob(Hashtable sequenceInfo, SequenceI[] msf, int[] delMap) + { + this.sequenceInfo = sequenceInfo; + this.msf = List.of(msf); + this.delMap = delMap; + } - @Override - public boolean hasResults() - { - return (isSubjobComplete() && alignment != null); - } -} + @Override + public boolean hasValidInput() + { + return true; + } + @Override + public boolean hasResults() + { + return (isSubjobComplete() && alignment != null); + } + + public boolean isMSA() + { + return msf.size() > 1; + } + } -public class JPredThread extends AWSThread implements WSClientI -{ - private JPredMutlipleAlignmentServiceI server; + private JPredServiceI server; private String title; private Hashtable sequenceInfo; private SequenceI[] msf; private int[] delMap; public JPredThread(WebserviceInfo wsInfo, String title, - JPredMutlipleAlignmentServiceI server, Hashtable sequenceInfo, + JPredServiceI server, Hashtable sequenceInfo, SequenceI[] msf, int[] delMap, AlignmentView view, AlignFrame frame, String wsURL) { @@ -122,7 +128,7 @@ public class JPredThread extends AWSThread implements WSClientI try { try { - var jobHandle = server.align(job.msf); + var jobHandle = server.predict(job.msf, job.isMSA()); if (jobHandle != null) job.setJobHandle(jobHandle); } @@ -229,6 +235,8 @@ public class JPredThread extends AWSThread implements WSClientI wsInfo.removeProgressBar(progbar); } + static final int msaIndex = 0; + private void prepareJobResult(JPredJob job) throws Exception { HiddenColumns hiddenCols = null; @@ -237,38 +245,70 @@ public class JPredThread extends AWSThread implements WSClientI var prediction = server.getPrediction(job.getJobHandle()); var preds = prediction.getSeqsAsArray(); - if (job.delMap != null) + if (job.msf.size() > 1) { - Object[] alandcolsel = input - .getAlignmentAndHiddenColumns(getGapChar()); - alignment = new Alignment((SequenceI[]) alandcolsel[0]); - hiddenCols = (HiddenColumns) alandcolsel[1]; + if (job.delMap != null) + { + Object[] alandcolsel = input + .getAlignmentAndHiddenColumns(getGapChar()); + alignment = new Alignment((SequenceI[]) alandcolsel[0]); + hiddenCols = (HiddenColumns) alandcolsel[1]; + } + else + { + alignment = server.getAlignment(job.getJobHandle()); + var seqs = new SequenceI[alignment.getHeight()]; + for (int i = 0; i < alignment.getHeight(); i++) + { + seqs[i] = alignment.getSequenceAt(i); + } + if (!SeqsetUtils.deuniquify(sequenceInfo, seqs)) + { + throw (new Exception(MessageManager.getString( + "exception.couldnt_recover_sequence_properties_for_alignment"))); + } + } + firstSeq = 0; + if (currentView.getDataset() != null) + { + alignment.setDataset(currentView.getDataset()); + } + else + { + alignment.setDataset(null); + } + JnetAnnotationMaker.add_annotation(prediction, alignment, firstSeq, false, + job.delMap); } else { - alignment = server.getAlignment(job.getJobHandle()); - var seqs = new SequenceI[alignment.getHeight()]; - for (int i = 0; i < alignment.getHeight(); i++) + alignment = new Alignment(preds); + firstSeq = prediction.getQuerySeqPosition(); + if (job.delMap != null) { - seqs[i] = alignment.getSequenceAt(i); + Object[] alanndcolsel = input.getAlignmentAndHiddenColumns(getGapChar()); + SequenceI[] seqs = (SequenceI[]) alanndcolsel[0]; + new RemoveGapsCommand(MessageManager.getString("label.remove_gaps"), + new SequenceI[] {seqs[msaIndex]}, currentView); + SequenceI profileSeq = alignment.getSequenceAt(firstSeq); + profileSeq.setSequence(seqs[msaIndex].getSequenceAsString()); } - if (!SeqsetUtils.deuniquify(sequenceInfo, seqs)) + if (!SeqsetUtils.SeqCharacterUnhash( + alignment.getSequenceAt(firstSeq), sequenceInfo)) { - throw (new Exception(MessageManager.getString( - "exception.couldnt_recover_sequence_properties_for_alignment"))); + throw new Exception(MessageManager.getString( + "exception.couldnt_recover_sequence_props_for_jnet_query")); } - } - firstSeq = 0; - if (currentView.getDataset() != null) - { alignment.setDataset(currentView.getDataset()); + JnetAnnotationMaker.add_annotation(prediction, alignment, firstSeq, true, + job.delMap); + SequenceI profileSeq = alignment.getSequenceAt(0); + alignToProfileSeq(alignment, profileSeq); + if (job.delMap != null) + { + hiddenCols = alignment.propagateInsertions(profileSeq, input); + } } - else - { - alignment.setDataset(null); - } - JnetAnnotationMaker.add_annotation(prediction, alignment, firstSeq, false, - job.delMap); for (var annot : alignment.getAlignmentAnnotation()) { @@ -306,6 +346,46 @@ public class JPredThread extends AWSThread implements WSClientI dssan.adjustForAlignment(); } + private static void alignToProfileSeq(AlignmentI al, SequenceI profileseq) + { + char gc = al.getGapCharacter(); + int[] gapMap = profileseq.gapMap(); + // insert gaps into profile + for (int lp = 0, r = 0; r < gapMap.length; r++) + { + if (gapMap[r] - lp > 1) + { + StringBuffer sb = new StringBuffer(); + for (int s = 0, ns = gapMap[r] - lp; s < ns; s++) + { + sb.append(gc); + } + for (int s = 1, ns = al.getHeight(); s < ns; s++) + { + String sq = al.getSequenceAt(s).getSequenceAsString(); + int diff = gapMap[r] - sq.length(); + if (diff > 0) + { + // pad gaps + sq = sq + sb; + while ((diff = gapMap[r] - sq.length()) > 0) + { + sq = sq + ((diff >= sb.length()) ? sb.toString() + : sb.substring(0, diff)); + } + al.getSequenceAt(s).setSequence(sq); + } + else + { + al.getSequenceAt(s).setSequence(sq.substring(0, gapMap[r]) + + sb.toString() + sq.substring(gapMap[r])); + } + } + } + lp = gapMap[r]; + } + } + private void displayResults(boolean newWindow) { if (jobs == null || jobs.length == 0) @@ -315,9 +395,8 @@ public class JPredThread extends AWSThread implements WSClientI var job = (JPredJob) jobs[0]; if (job.hasResults() && newWindow) { - AlignFrame frame; job.alignment.setSeqrep(job.alignment.getSequenceAt(0)); - frame = new AlignFrame(job.alignment, job.hiddenCols, + AlignFrame frame = new AlignFrame(job.alignment, job.hiddenCols, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); Desktop.addInternalFrame(frame, title, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); diff --git a/src/jalview/ws/slivkaws/SlivkaJPredServiceInstance.java b/src/jalview/ws/slivkaws/SlivkaJPredServiceInstance.java index b8e4d2f..462be28 100644 --- a/src/jalview/ws/slivkaws/SlivkaJPredServiceInstance.java +++ b/src/jalview/ws/slivkaws/SlivkaJPredServiceInstance.java @@ -8,16 +8,41 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; import jalview.io.DataSourceType; import jalview.io.JPredFile; -import jalview.ws.api.JPredMutlipleAlignmentServiceI; +import jalview.ws.api.JPredServiceI; import jalview.ws.api.JobId; +import jalview.ws.params.ArgumentI; import uk.ac.dundee.compbio.slivkaclient.RemoteFile; import uk.ac.dundee.compbio.slivkaclient.SlivkaClient; import uk.ac.dundee.compbio.slivkaclient.SlivkaService; public class SlivkaJPredServiceInstance extends SlivkaWSInstance - implements JPredMutlipleAlignmentServiceI + implements JPredServiceI { + private class InputFormatParameter implements ArgumentI + { + String value = ""; + + @Override + public String getName() + { + return "format"; + } + + @Override + public String getValue() + { + return value; + } + + @Override + public void setValue(String selectedItem) + { + value = selectedItem; + } + } + + public SlivkaJPredServiceInstance(SlivkaClient client, SlivkaService service, String action) { @@ -26,9 +51,13 @@ public class SlivkaJPredServiceInstance extends SlivkaWSInstance } @Override - public JobId align(List sequences) throws Throwable + public JobId predict(List sequences, boolean msa) throws Throwable { - return super.submit(sequences, null, null); + // Hack allowing to send both single and msa jobs + // until msa and single sequence services are separated. + var arg = new InputFormatParameter(); + arg.setValue(msa ? "fasta" : "seq"); + return super.submit(sequences, null, List.of(arg)); } @Override -- 1.7.10.2