X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=inline;f=src%2Fjalview%2Fws2%2Fclient%2Fslivka%2FSlivkaWSClient.java;fp=src%2Fjalview%2Fws2%2Fclient%2Fslivka%2FSlivkaWSClient.java;h=d7841af5056ca7842cdb4bc9fe81eb3cf26446cd;hb=bea1d9b563d2fea018de3dbde9112dd59149126e;hp=0000000000000000000000000000000000000000;hpb=49ab19e8189569edf0bc1f4ba8dac14e67f4ca36;p=jalview.git diff --git a/src/jalview/ws2/client/slivka/SlivkaWSClient.java b/src/jalview/ws2/client/slivka/SlivkaWSClient.java new file mode 100644 index 0000000..d7841af --- /dev/null +++ b/src/jalview/ws2/client/slivka/SlivkaWSClient.java @@ -0,0 +1,291 @@ +package jalview.ws2.client.slivka; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import jalview.api.FeatureColourI; +import jalview.bin.Cache; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceI; +import jalview.datamodel.features.FeatureMatcherSetI; +import jalview.io.AnnotationFile; +import jalview.io.DataSourceType; +import jalview.io.FeaturesFile; +import jalview.io.FileFormat; +import jalview.io.FormatAdapter; +import jalview.ws.params.ArgumentI; +import jalview.ws2.api.Credentials; +import jalview.ws2.api.JobStatus; +import jalview.ws2.api.WebServiceJobHandle; +import jalview.ws2.client.api.AlignmentWebServiceClientI; +import jalview.ws2.client.api.AnnotationWebServiceClientI; +import jalview.ws2.client.api.WebServiceClientI; +import uk.ac.dundee.compbio.slivkaclient.Job; +import uk.ac.dundee.compbio.slivkaclient.Parameter; +import uk.ac.dundee.compbio.slivkaclient.SlivkaClient; +import uk.ac.dundee.compbio.slivkaclient.SlivkaService; + +import static java.lang.String.format; + +public class SlivkaWSClient implements WebServiceClientI +{ + final SlivkaService service; + + final SlivkaClient client; + + SlivkaWSClient(SlivkaService service) + { + this.service = service; + this.client = service.getClient(); + } + + @Override + public String getUrl() + { + return client.getUrl().toString(); + } + + @Override + public String getClientName() + { + return "slivka"; + } + + // pattern for matching media types + static final Pattern mediaTypePattern = Pattern.compile( + "(?:text|application)\\/(?:x-)?([\\w-]+)"); + + @Override + public WebServiceJobHandle submit(List sequences, + List args, Credentials credentials) throws IOException + { + var request = new uk.ac.dundee.compbio.slivkaclient.JobRequest(); + for (Parameter param : service.getParameters()) + { + // TODO: restrict input sequences parameter name to "sequences" + if (param instanceof Parameter.FileParameter) + { + Parameter.FileParameter fileParam = (Parameter.FileParameter) param; + FileFormat format = null; + var match = mediaTypePattern.matcher(fileParam.getMediaType()); + if (match.find()) + { + String fmt = match.group(1); + if (fmt.equalsIgnoreCase("pfam")) + format = FileFormat.Pfam; + else if (fmt.equalsIgnoreCase("stockholm")) + format = FileFormat.Stockholm; + else if (fmt.equalsIgnoreCase("clustal")) + format = FileFormat.Clustal; + else if (fmt.equalsIgnoreCase("fasta")) + format = FileFormat.Fasta; + } + if (format == null) + { + Cache.log.warn(String.format( + "Unknown input format %s, assuming fasta.", + fileParam.getMediaType())); + format = FileFormat.Fasta; + } + InputStream stream = new ByteArrayInputStream(format.getWriter(null) + .print(sequences.toArray(new SequenceI[0]), false) + .getBytes()); + request.addFile(param.getId(), stream); + } + } + if (args != null) + { + for (ArgumentI arg : args) + { + // multiple choice field names are name$number to avoid duplications + // the number is stripped here + String paramId = arg.getName().split("\\$", 2)[0]; + Parameter param = service.getParameter(paramId); + if (param instanceof Parameter.FlagParameter) + { + if (arg.getValue() != null && !arg.getValue().isBlank()) + request.addData(paramId, true); + else + request.addData(paramId, false); + } + else if (param instanceof Parameter.FileParameter) + { + request.addFile(paramId, new File(arg.getValue())); + } + else + { + request.addData(paramId, arg.getValue()); + } + } + } + var job = service.submitJob(request); + return createJobHandle(job.getId()); + } + + protected WebServiceJobHandle createJobHandle(String jobId) + { + return new WebServiceJobHandle( + getClientName(), service.getName(), client.getUrl().toString(), + jobId); + } + + @Override + public JobStatus getStatus(WebServiceJobHandle job) throws IOException + { + var slivkaJob = client.getJob(job.getJobId()); + return statusMap.getOrDefault(slivkaJob.getStatus(), JobStatus.UNKNOWN); + } + + protected static final EnumMap statusMap = new EnumMap<>(Job.Status.class); + static + { + statusMap.put(Job.Status.PENDING, JobStatus.SUBMITTED); + statusMap.put(Job.Status.REJECTED, JobStatus.INVALID); + statusMap.put(Job.Status.ACCEPTED, JobStatus.SUBMITTED); + statusMap.put(Job.Status.QUEUED, JobStatus.QUEUED); + statusMap.put(Job.Status.RUNNING, JobStatus.RUNNING); + statusMap.put(Job.Status.COMPLETED, JobStatus.COMPLETED); + statusMap.put(Job.Status.INTERRUPTED, JobStatus.CANCELLED); + statusMap.put(Job.Status.DELETED, JobStatus.CANCELLED); + statusMap.put(Job.Status.FAILED, JobStatus.FAILED); + statusMap.put(Job.Status.ERROR, JobStatus.SERVER_ERROR); + statusMap.put(Job.Status.UNKNOWN, JobStatus.UNKNOWN); + } + + @Override + public String getLog(WebServiceJobHandle job) throws IOException + { + var slivkaJob = client.getJob(job.getJobId()); + for (var f : slivkaJob.getResults()) + { + if (f.getLabel().equals("log")) + { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + f.writeTo(stream); + return stream.toString(StandardCharsets.UTF_8); + } + } + return ""; + } + + @Override + public String getErrorLog(WebServiceJobHandle job) throws IOException + { + var slivkaJob = client.getJob(job.getJobId()); + for (var f : slivkaJob.getResults()) + { + if (f.getLabel().equals("error-log")) + { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + f.writeTo(stream); + return stream.toString(StandardCharsets.UTF_8); + } + } + return ""; + } + + @Override + public void cancel(WebServiceJobHandle job) + throws IOException, UnsupportedOperationException + { + Cache.log.warn( + "slivka client does not support job cancellation"); + } +} + +class SlivkaAlignmentWSClient extends SlivkaWSClient + implements AlignmentWebServiceClientI +{ + + SlivkaAlignmentWSClient(SlivkaService service) + { + super(service); + } + + @Override + public AlignmentI getAlignment(WebServiceJobHandle job) throws IOException + { + var slivkaJob = client.getJob(job.getJobId()); + for (var f : slivkaJob.getResults()) + { + // TODO: restrict result file label to "alignment" + FileFormat format; + var match = mediaTypePattern.matcher(f.getMediaType()); + if (!match.find()) + continue; + String fmt = match.group(1); + if (fmt.equalsIgnoreCase("clustal")) + format = FileFormat.Clustal; + else if (fmt.equalsIgnoreCase("fasta")) + format = FileFormat.Fasta; + else + continue; + return new FormatAdapter().readFile(f.getContentUrl().toString(), + DataSourceType.URL, format); + } + Cache.log.warn("No alignment found on the server"); + throw new IOException("no alignment found"); + } + +} + +class SlivkaAnnotationWSClient extends SlivkaWSClient + implements AnnotationWebServiceClientI +{ + SlivkaAnnotationWSClient(SlivkaService service) + { + super(service); + } + + @Override + public List attachAnnotations(WebServiceJobHandle job, + List sequences, Map colours, + Map filters) throws IOException + { + var slivkaJob = client.getJob(job.getJobId()); + var aln = new Alignment(sequences.toArray(new SequenceI[sequences.size()])); + boolean featPresent = false, annotPresent = false; + for (var f : slivkaJob.getResults()) + { + // TODO: restrict file label to "annotations" or "features" + var match = mediaTypePattern.matcher(f.getMediaType()); + if (!match.find()) + continue; + String fmt = match.group(1); + if (fmt.equalsIgnoreCase("jalview-annotations")) + { + annotPresent = new AnnotationFile().readAnnotationFileWithCalcId( + aln, service.getId(), f.getContentUrl().toString(), + DataSourceType.URL); + if (annotPresent) + Cache.log.debug(format("loaded annotations for %s", service.getId())); + } + else if (fmt.equalsIgnoreCase("jalview-features")) + { + FeaturesFile ff = new FeaturesFile(f.getContentUrl().toString(), + DataSourceType.URL); + featPresent = ff.parse(aln, colours, true); + if (featPresent) + Cache.log.debug(format("loaded features for %s", service.getId())); + } + } + if (!annotPresent) + Cache.log.debug(format("no annotations found for %s", service.getId())); + if (!featPresent) + Cache.log.debug(format("no features found for %s", service.getId())); + return aln.getAlignmentAnnotation() != null ? Arrays.asList(aln.getAlignmentAnnotation()) + : Collections.emptyList(); + } +}