Merge branch 'mmw/JAL-4199-task-execution-update' into development/Release_2_12_Branch
[jalview.git] / src / jalview / ws2 / client / ebi / PhmmerWSClient.java
1 package jalview.ws2.client.ebi;
2
3 import java.io.BufferedReader;
4 import java.io.ByteArrayOutputStream;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.InputStreamReader;
8 import java.io.StringReader;
9 import java.net.URI;
10 import java.util.List;
11
12 import jalview.bin.Console;
13 import jalview.datamodel.Alignment;
14 import jalview.datamodel.AlignmentI;
15 import jalview.datamodel.SequenceI;
16 import jalview.io.DataSourceType;
17 import jalview.io.FileFormat;
18 import jalview.io.FileParse;
19 import jalview.io.FormatAdapter;
20 import jalview.io.StockholmFile;
21 import jalview.ws.params.ArgumentI;
22 import jalview.ws.params.simple.BooleanOption;
23 import jalview.ws.params.simple.DoubleParameter;
24 import jalview.ws.params.simple.IntegerParameter;
25 import jalview.ws2.api.Credentials;
26 import jalview.ws2.api.JobStatus;
27 import jalview.ws2.api.WebServiceJobHandle;
28 import jalview.ws2.client.api.AlignmentWebServiceClientI;
29 import jalview.ws2.client.api.WebServiceClientI;
30 import uk.ac.dundee.compbio.hmmerclient.PhmmerClient;
31 import uk.ac.dundee.compbio.hmmerclient.PhmmerRequest;
32 import uk.ac.dundee.compbio.hmmerclient.PhmmerRequest.SequenceDatabase;
33 import uk.ac.dundee.compbio.hmmerclient.PhmmerRequest.SubstitutionMatrix;
34
35 public class PhmmerWSClient implements AlignmentWebServiceClientI
36 {
37
38   final PhmmerClient client;
39
40   PhmmerWSClient(PhmmerClient client)
41   {
42     this.client = client;
43   }
44
45   @Override
46   public String getUrl()
47   {
48     return client.getURL().toString();
49   }
50
51   @Override
52   public String getClientName()
53   {
54     return "ebi-job-dispatcher";
55   }
56
57   @Override
58   public WebServiceJobHandle submit(List<SequenceI> sequences,
59           List<ArgumentI> args, Credentials credentials) throws IOException
60   {
61     var request = PhmmerRequest.newBuilder();
62     String sequence = FileFormat.Fasta.getWriter(null)
63             .print(new SequenceI[]{ sequences.get(0) }, false);
64     request.sequence(new StringReader(sequence));
65     populateRequestArguments(request, args);
66     var email = credentials.getEmail() != null ? credentials.getEmail() :
67       "nouser@jalview.org";
68     var jobId = client.submit(request.build(), email);
69     Console.debug("Phmmer client submitted new job with id " + jobId);
70     return new WebServiceJobHandle(
71             getClientName(), "phmmer", getUrl(), jobId);
72   }
73   
74   private static void populateRequestArguments(PhmmerRequest.Builder request, List<ArgumentI> args)
75   {
76     boolean useBitScore = false;
77     boolean useEValue = false;
78     for (var arg : args)
79     {
80       if (arg.getName().equals("cut-offs"))
81         if (arg.getValue().equals("E"))
82           useEValue = true;
83         else if (arg.getValue().equals("T"))
84           useBitScore = true;
85         else
86           throw new IllegalArgumentException(
87                   "cut-offs argument contains value other than \"E\" or \"T\": "
88                           + arg.getValue());
89     }
90     assert (useBitScore || useEValue) && !(useBitScore && useEValue);
91     for (var arg : args)
92     {
93       switch (arg.getName())
94       {
95       case "incE":
96         request.incE(useEValue ? DoubleParameter.parseFloat(arg) : null);
97         break;
98       case "incdomE":
99         request.incdomE(useEValue ? DoubleParameter.parseFloat(arg) : null);
100         break;
101       case "E":
102         request.E(useEValue ? DoubleParameter.parseFloat(arg) : null);
103         break;
104       case "domE":
105         request.domE(useEValue ? DoubleParameter.parseFloat(arg) : null);
106         break;
107       case "incT":
108         request.incT(useBitScore ? DoubleParameter.parseFloat(arg) : null);
109         break;
110       case "incdomT":
111         request.incdomT(useBitScore ? DoubleParameter.parseFloat(arg) : null);
112         break;
113       case "T":
114         request.T(useBitScore ? DoubleParameter.parseFloat(arg) : null);
115         break;
116       case "domT":
117         request.domT(useBitScore ? DoubleParameter.parseFloat(arg) : null);
118         break;
119       case "popen":
120         request.popen(DoubleParameter.parseFloat(arg));
121         break;
122       case "pextend":
123         request.pextend(DoubleParameter.parseFloat(arg));
124         break;
125       case "mx":
126         request.mx(parseSubstitutionMatrix(arg));
127         break;
128       case "nobias":
129         request.noBias(BooleanOption.parseBoolean(arg));
130         break;
131       case "compressedout":
132         request.compressedOut(BooleanOption.parseBoolean(arg));
133         break;
134       case "alignView":
135         request.compressedOut(BooleanOption.parseBoolean(arg));
136         break;
137       case "database":
138         request.database(parseSequenceDatabase(arg));
139         break;
140       case "evalue":
141         request.evalue(DoubleParameter.parseFloat(arg));
142         break;
143       case "nhits":
144         request.nhits(IntegerParameter.parseInt(arg));
145         break;
146       }
147     }
148   }
149
150   private static SubstitutionMatrix parseSubstitutionMatrix(ArgumentI arg)
151   {
152     if (arg.getValue() == null)
153       return null;
154     switch (arg.getValue())
155     {
156     case "BLOSUM45":
157       return SubstitutionMatrix.BLOSUM45;
158     case "BLOSUM62":
159       return SubstitutionMatrix.BLOSUM62;
160     case "BLOSUM90":
161       return SubstitutionMatrix.BLOSUM90;
162     case "PAM30":
163       return SubstitutionMatrix.PAM30;
164     case "PAM70":
165       return SubstitutionMatrix.PAM70;
166     default:
167       throw new IllegalArgumentException(
168               "invalid matrix " + arg.getValue());
169     }
170   }
171
172   private static SequenceDatabase parseSequenceDatabase(ArgumentI arg)
173   {
174     if (arg.getValue() == null)
175       return null;
176     switch (arg.getValue())
177     {
178     case "swissprot":
179       return SequenceDatabase.SWISS_PROT;
180     case "uniprotrefprot":
181       return SequenceDatabase.REFERENCE_PROTEOMES;
182     case "uniprotkb":
183       return SequenceDatabase.UNIPROTKB;
184     case "pdb":
185       return SequenceDatabase.PDB;
186     case "rp75":
187       return SequenceDatabase.RP75;
188     case "rp55":
189       return SequenceDatabase.RP55;
190     case "rp35":
191       return SequenceDatabase.RP35;
192     case "rp15":
193       return SequenceDatabase.RP15;
194     case "ensembl":
195       return SequenceDatabase.ENSEMBL;
196     case "merops":
197       return SequenceDatabase.MEROPS;
198     case "qfo":
199       return SequenceDatabase.QUEST_FOR_ORTHOLOGS;
200     case "chembl":
201       return SequenceDatabase.CHEMBL;
202     default:
203       throw new IllegalArgumentException(
204               "invalid database " + arg.getValue());
205     }
206   }
207
208   @Override
209   public JobStatus getStatus(WebServiceJobHandle job) throws IOException
210   {
211     var status = client.getStatus(job.getJobId());
212     switch (status)
213     {
214     case PENDING: return JobStatus.SUBMITTED;
215     case QUEUED: return JobStatus.QUEUED;
216     case RUNNING: return JobStatus.RUNNING;
217     case FINISHED: return JobStatus.COMPLETED;
218     case FAILURE: return JobStatus.FAILED;
219     case ERROR: return JobStatus.SERVER_ERROR;
220     case NOT_FOUND: return JobStatus.SERVER_ERROR;
221     case UNDEFINED: return JobStatus.UNKNOWN;
222     }
223     return JobStatus.UNKNOWN;
224   }
225
226   @Override
227   public String getLog(WebServiceJobHandle job) throws IOException
228   {
229     return "";
230   }
231
232   @Override
233   public String getErrorLog(WebServiceJobHandle job) throws IOException
234   {
235     if (getStatus(job) != JobStatus.FAILED)
236       return "";
237     try(InputStream stream = client.getResultStream(job.getJobId(), "error"))
238     {
239       ByteArrayOutputStream out = new ByteArrayOutputStream();
240       stream.transferTo(out);
241       return out.toString();
242     }
243   }
244
245   @Override
246   public void cancel(WebServiceJobHandle job)
247           throws IOException, UnsupportedOperationException
248   {
249     throw new UnsupportedOperationException(
250             "ebi job dispatcher does not support job cancellation");
251   }
252
253   /**
254    * FIXME: Temporary hack
255    */
256   @Override
257   public AlignmentI getAlignment(WebServiceJobHandle job) throws IOException
258   {
259     URI url = client.getResultURL(job.getJobId(), "sto");
260     try(InputStream stream = client.getResultStream(job.getJobId(), "sto"))
261     {
262       StockholmFile file = new StockholmFile(new FileParse(
263               new BufferedReader(new InputStreamReader(stream)),
264               url.toString(), DataSourceType.URL));
265       var aln = new Alignment(file.getSeqsAsArray());
266       for (var annotation : file.getAnnotations())
267         aln.addAnnotation(annotation);
268       return aln;
269     }
270   }
271 }