1 package jalview.ws2.slivka;
3 import static java.lang.String.format;
5 import java.io.ByteArrayInputStream;
6 import java.io.ByteArrayOutputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.util.Arrays;
10 import java.util.Collection;
11 import java.util.EnumMap;
12 import java.util.List;
15 import jalview.api.FeatureColourI;
16 import jalview.bin.Cache;
17 import jalview.datamodel.Alignment;
18 import jalview.datamodel.AlignmentAnnotation;
19 import jalview.datamodel.AlignmentI;
20 import jalview.datamodel.SequenceI;
21 import jalview.datamodel.features.FeatureMatcherSetI;
22 import jalview.io.AnnotationFile;
23 import jalview.io.DataSourceType;
24 import jalview.io.FeaturesFile;
25 import jalview.io.FileFormat;
26 import jalview.io.FormatAdapter;
27 import jalview.io.JPredFile;
28 import jalview.ws.params.ArgumentI;
29 import jalview.ws.params.ParamDatastoreI;
30 import jalview.ws.slivkaws.SlivkaDatastore;
31 import jalview.ws2.WSJob;
32 import jalview.ws2.WSJobStatus;
33 import jalview.ws2.WebServiceI;
34 import javajs.http.ClientProtocolException;
35 import uk.ac.dundee.compbio.slivkaclient.Job;
36 import uk.ac.dundee.compbio.slivkaclient.Parameter;
37 import uk.ac.dundee.compbio.slivkaclient.RemoteFile;
38 import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
39 import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
41 public class SlivkaWebService implements WebServiceI
43 protected final SlivkaClient client;
45 protected final SlivkaService service;
47 protected ParamDatastoreI store;
49 protected static final EnumMap<Job.Status, WSJobStatus> statusMap = new EnumMap<>(
52 statusMap.put(Job.Status.PENDING, WSJobStatus.SUBMITTED);
53 statusMap.put(Job.Status.REJECTED, WSJobStatus.INVALID);
54 statusMap.put(Job.Status.ACCEPTED, WSJobStatus.QUEUED);
55 statusMap.put(Job.Status.QUEUED, WSJobStatus.QUEUED);
56 statusMap.put(Job.Status.RUNNING, WSJobStatus.RUNNING);
57 statusMap.put(Job.Status.COMPLETED, WSJobStatus.FINISHED);
58 statusMap.put(Job.Status.INTERRUPTED, WSJobStatus.CANCELLED);
59 statusMap.put(Job.Status.DELETED, WSJobStatus.CANCELLED);
60 statusMap.put(Job.Status.FAILED, WSJobStatus.FAILED);
61 statusMap.put(Job.Status.ERROR, WSJobStatus.SERVER_ERROR);
62 statusMap.put(Job.Status.UNKNOWN, WSJobStatus.UNKNOWN);
65 public SlivkaWebService(SlivkaClient client, SlivkaService service)
68 this.service = service;
72 public String getHostName()
74 return client.getUrl().toString();
78 public String getProviderName()
84 public String getName()
86 return service.getName();
90 public String getDescription()
92 return service.getDescription();
96 public boolean hasParameters()
98 return getParamStore().getServiceParameters().size() > 0;
102 public ParamDatastoreI getParamStore()
106 store = new SlivkaDatastore(service);
112 public String submit(List<SequenceI> sequences, List<ArgumentI> args)
115 var request = new uk.ac.dundee.compbio.slivkaclient.JobRequest();
116 for (Parameter param : service.getParameters())
118 if (param instanceof Parameter.FileParameter)
120 // if finds a file input, gives it sequences stream
121 Parameter.FileParameter fileParam = (Parameter.FileParameter) param;
123 switch (fileParam.getMediaType())
125 case "application/pfam":
126 format = FileFormat.Pfam;
128 case "application/stockholm":
129 format = FileFormat.Stockholm;
131 case "application/clustal":
132 format = FileFormat.Clustal;
134 case "application/fasta":
136 format = FileFormat.Fasta;
139 InputStream stream = new ByteArrayInputStream(format.getWriter(null)
140 .print(sequences.toArray(new SequenceI[0]), false)
142 request.addFile(param.getId(), stream);
147 for (ArgumentI arg : args)
149 // multiple choice field names are name$number to avoid duplications
150 // the number is stripped here
151 String paramId = arg.getName().split("\\$", 2)[0];
152 Parameter param = service.getParameter(paramId);
153 if (param instanceof Parameter.FlagParameter)
155 if (arg.getValue() != null && !arg.getValue().isBlank())
156 request.addData(paramId, true);
158 request.addData(paramId, false);
162 request.addData(paramId, arg.getValue());
166 var job = service.submitJob(request);
171 public void updateProgress(WSJob job) throws IOException
173 var slivkaJob = client.getJob(job.getJobId());
174 job.setStatus(statusMap.get(slivkaJob.getStatus()));
175 Collection<RemoteFile> files = slivkaJob.getResults();
176 for (RemoteFile f : files)
178 if (f.getLabel().equals("log"))
180 ByteArrayOutputStream stream = new ByteArrayOutputStream();
182 job.setLog(stream.toString("UTF-8"));
184 else if (f.getLabel().equals("error-log"))
186 ByteArrayOutputStream stream = new ByteArrayOutputStream();
188 job.setErrorLog(stream.toString("UTF-8"));
194 public void cancel(WSJob job) throws IOException
196 job.setStatus(WSJobStatus.CANCELLED);
197 Cache.log.warn("Slivka does not support job cancellation yet.");
201 public boolean handleSubmissionError(WSJob job, Exception ex)
203 if (ex instanceof ClientProtocolException)
205 Cache.log.error("Job submission failed due to exception.", ex);
212 public boolean handleCollectionError(WSJob job, Exception ex)
214 // TODO Auto-generated method stub
218 public AlignmentI getAlignment(WSJob job) throws IOException
220 Collection<RemoteFile> files;
221 var slivkaJob = client.getJob(job.getJobId());
222 files = slivkaJob.getResults();
223 for (RemoteFile f : files)
225 if (f.getMediaType().equals("application/clustal"))
227 return new FormatAdapter().readFile(f.getContentUrl().toString(),
228 DataSourceType.URL, FileFormat.Clustal);
230 else if (f.getMediaType().equals("application/fasta"))
232 return new FormatAdapter().readFile(f.getContentUrl().toString(),
233 DataSourceType.URL, FileFormat.Fasta);
239 public List<AlignmentAnnotation> attachAnnotations(WSJob job,
240 List<SequenceI> dataset, Map<String, FeatureColourI> featureColours,
241 Map<String, FeatureMatcherSetI> featureFilters) throws IOException
243 RemoteFile annotFile = null;
244 RemoteFile featFile = null;
246 var slivkaJob = client.getJob(job.getJobId());
247 Collection<RemoteFile> files = slivkaJob.getResults();
248 for (RemoteFile f : files)
250 if (f.getMediaType().equals("application/jalview-annotations"))
252 else if (f.getMediaType().equals("application/jalview-features"))
255 Alignment aln = new Alignment(dataset.toArray(new SequenceI[0]));
257 boolean annotPresent = annotFile != null;
258 if (annotFile != null)
260 AnnotationFile af = new AnnotationFile();
261 annotPresent = af.readAnnotationFileWithCalcId(
262 aln, service.getId(), annotFile.getContentUrl().toString(),
266 Cache.log.debug(format("Annotation file loaded %s", annotFile));
268 Cache.log.debug(format("No annotations loaded from %s", annotFile));
270 boolean featPresent = featFile != null;
271 if (featFile != null)
273 FeaturesFile ff = new FeaturesFile(featFile.getContentUrl().toString(),
275 featPresent = ff.parse(aln, featureColours, true);
278 Cache.log.debug(format("Features file loaded %s", featFile));
280 Cache.log.debug(format("No features loaded from %s", annotFile));
281 return Arrays.asList(aln.getAlignmentAnnotation());
284 public JPredFile getPrediction(WSJob job) throws IOException
286 Collection<RemoteFile> files = client.getJob(job.getJobId()).getResults();
287 for (RemoteFile f : files)
289 if (f.getLabel().equals("concise"))
291 return new JPredFile(f.getContentUrl(), DataSourceType.URL);
298 public String toString()
300 return String.format("SlivkaWebService[%s]", getName());