JAL-2375 JAL-2376 First commit of implementation for Phyre2 result browsing, template...
[jalview.git] / src / jalview / ws / phyre2 / Phyre2Client.java
1 package jalview.ws.phyre2;
2
3 import jalview.datamodel.AlignmentI;
4 import jalview.datamodel.SequenceI;
5 import jalview.fts.core.DecimalFormatTableCellRenderer;
6 import jalview.io.DataSourceType;
7 import jalview.io.FileFormat;
8 import jalview.io.FormatAdapter;
9 import jalview.io.StructureFile;
10 import jalview.schemes.ResidueProperties;
11 import jalview.structure.StructureMapping;
12 import jalview.structure.StructureMappingClient;
13 import jalview.structures.models.MappingOutputModel;
14 import jalview.util.Comparison;
15 import jalview.util.Format;
16
17 import java.io.File;
18 import java.io.IOException;
19 import java.io.PrintStream;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.List;
23
24 import javax.swing.JTable;
25 import javax.swing.table.DefaultTableModel;
26
27 import org.jsoup.Jsoup;
28 import org.jsoup.nodes.Document;
29 import org.jsoup.nodes.Element;
30 import org.jsoup.select.Elements;
31
32 public class Phyre2Client extends StructureMappingClient
33 {
34   private final static String NEWLINE = System.lineSeparator();
35
36   public static final int UNASSIGNED = -1;
37
38   private final static String PATH_SEPARATOR = File.separator;
39
40   public Phyre2Client(StructureFile structureFile)
41   {
42     this.structureFile = structureFile;
43   }
44
45   @Override
46   public StructureMapping getStructureMapping(SequenceI seq,
47           String pdbFile, String chain)
48   {
49     final StringBuilder mappingDetails = new StringBuilder(128);
50     PrintStream ps = new PrintStream(System.out)
51     {
52       @Override
53       public void print(String x)
54       {
55         mappingDetails.append(x);
56       }
57
58       @Override
59       public void println()
60       {
61         mappingDetails.append(NEWLINE);
62       }
63     };
64     HashMap<Integer, int[]> mapping = getPhyre2FastaMapping(seq, ps);
65
66     String mappingOutput = mappingDetails.toString();
67     StructureMapping phyre2ModelMapping = new StructureMapping(seq,
68             pdbFile, structureFile.getId(), chain, mapping, mappingOutput);
69     return phyre2ModelMapping;
70   }
71
72   public HashMap<Integer, int[]> getPhyre2FastaMapping(SequenceI inputSeq,
73           java.io.PrintStream os)
74   {
75     HashMap<Integer, int[]> mapping = new HashMap<Integer, int[]>();
76     AlignmentI seq2Phyre2ModelFastaMapping = null;
77     try
78     {
79       seq2Phyre2ModelFastaMapping = new FormatAdapter().readFile(
80               getFastaMappingFile(), DataSourceType.FILE, FileFormat.Fasta);
81     } catch (IOException e1)
82     {
83       e1.printStackTrace();
84     }
85     SequenceI[] seqs = seq2Phyre2ModelFastaMapping.getSequencesArray();
86     SequenceI tSequenceRes = seqs[0];
87     SequenceI tStructureRes = seqs[1];
88
89     // Essential to resolve fastaAlignment to input sequence and model sequence
90     // coordinates
91     tSequenceRes.setStart(inputSeq.getStart());
92     tSequenceRes.setEnd(inputSeq.getEnd());
93
94     tStructureRes.setStart(structureFile.getSeqsAsArray()[0].getStart());
95     tStructureRes.setEnd(structureFile.getSeqsAsArray()[0].getEnd());
96     try
97     {
98       int sequenceResLenght = tSequenceRes.getLength();
99       int structureResLenght = tStructureRes.getLength();
100       if (sequenceResLenght == structureResLenght)
101       {
102         int prevStructResNum = -1;
103         int alignmentLenght = sequenceResLenght + tSequenceRes.getStart();
104         for (int x = 0; x < alignmentLenght; x++)
105         {
106           int alignSeqResidueIndex = tSequenceRes.findIndex(x);
107           int structResNum = tStructureRes
108                   .findPosition(alignSeqResidueIndex);
109           int sequenceResNum = tSequenceRes
110                   .findPosition(alignSeqResidueIndex - 1);
111           boolean sameResNum = (structResNum == prevStructResNum);
112           // System.out.println(sequenceResNum + " : "
113           // + (sameResNum ? -1 : prevStructResNum));
114           mapping.put(sequenceResNum, new int[] {
115               sameResNum ? -1 : prevStructResNum, -1 });
116           prevStructResNum = structResNum;
117         }
118       }
119     } catch (Exception e)
120     {
121       e.printStackTrace();
122     }
123
124     try
125     {
126       populateAtomPositions(" ", mapping);
127     } catch (IllegalArgumentException e)
128     {
129       e.printStackTrace();
130     } catch (StructureMappingException e)
131     {
132       e.printStackTrace();
133     }
134
135     if (os != null)
136     {
137       MappingOutputModel mop = new MappingOutputModel();
138       mop.setSeqStart(tSequenceRes.getStart());
139       mop.setSeqEnd(tSequenceRes.getEnd());
140       mop.setSeqName(tSequenceRes.getName());
141       mop.setSeqResidue(tSequenceRes.getSequenceAsString());
142
143       mop.setStrStart(tStructureRes.getStart());
144       mop.setStrEnd(tStructureRes.getEnd());
145       mop.setStrName(tStructureRes.getName());
146       mop.setStrResidue(tStructureRes.getSequenceAsString());
147
148       mop.setType("pep");
149       try
150       {
151         os.print(getMappingOutput(mop).toString());
152       } catch (Exception e)
153       {
154         e.printStackTrace();
155       }
156       os.println();
157     }
158     return mapping;
159   }
160
161   private String getFastaMappingFile()
162   {
163     File phyre2ModelFile = new File(structureFile.getDataName());
164     String phyre2ModelResultDir = phyre2ModelFile.getParent();
165     String modelId = structureFile.getId().substring(0,
166             structureFile.getId().lastIndexOf(".pdb"));
167     return phyre2ModelResultDir + PATH_SEPARATOR + modelId + ".fasta";
168   }
169
170   @Override
171   public StringBuffer getMappingOutput(MappingOutputModel mp)
172           throws StructureMappingException
173   {
174     String seqRes = mp.getSeqResidue();
175     String seqName = mp.getSeqName();
176     int sStart = mp.getSeqStart();
177     int sEnd = mp.getSeqEnd();
178
179     String strRes = mp.getStrResidue();
180     String strName = mp.getStrName();
181     int pdbStart = mp.getStrStart();
182     int pdbEnd = mp.getStrEnd();
183
184     String type = mp.getType();
185
186     int maxid = (seqName.length() >= strName.length()) ? seqName.length()
187             : strName.length();
188     int len = 72 - maxid - 1;
189
190     int nochunks = ((seqRes.length()) / len)
191             + ((seqRes.length()) % len > 0 ? 1 : 0);
192     // output mappings
193     StringBuffer output = new StringBuffer();
194     output.append(NEWLINE);
195     output.append("Sequence \u27f7 Structure mapping details").append(
196             NEWLINE);
197     output.append("Method: Phyre2 Alignment");
198     output.append(NEWLINE).append(NEWLINE);
199
200     output.append(new Format("%" + maxid + "s").form(seqName));
201     output.append(" :  ");
202     output.append(String.valueOf(sStart));
203     output.append(" - ");
204     output.append(String.valueOf(sEnd));
205     output.append(" Maps to ");
206     output.append(NEWLINE);
207     output.append(new Format("%" + maxid + "s").form(strName));
208     output.append(" :  ");
209     output.append(String.valueOf(pdbStart));
210     output.append(" - ");
211     output.append(String.valueOf(pdbEnd));
212     output.append(NEWLINE).append(NEWLINE);
213
214     int matchedSeqCount = 0;
215     for (int j = 0; j < nochunks; j++)
216     {
217       // Print the first aligned sequence
218       output.append(new Format("%" + (maxid) + "s").form(seqName)).append(
219               " ");
220
221       for (int i = 0; i < len; i++)
222       {
223         if ((i + (j * len)) < seqRes.length())
224         {
225           output.append(seqRes.charAt(i + (j * len)));
226         }
227       }
228
229       output.append(NEWLINE);
230       output.append(new Format("%" + (maxid) + "s").form(" ")).append(" ");
231
232       // Print out the matching chars
233       for (int i = 0; i < len; i++)
234       {
235         try
236         {
237           if ((i + (j * len)) < seqRes.length())
238           {
239             boolean sameChar = Comparison.isSameResidue(
240                     seqRes.charAt(i + (j * len)),
241                     strRes.charAt(i + (j * len)), false);
242             if (sameChar
243                     && !jalview.util.Comparison.isGap(seqRes.charAt(i
244                             + (j * len))))
245             {
246               matchedSeqCount++;
247               output.append("|");
248             }
249             else if (type.equals("pep"))
250             {
251               if (ResidueProperties.getPAM250(seqRes.charAt(i + (j * len)),
252                       strRes.charAt(i + (j * len))) > 0)
253               {
254                 output.append(".");
255               }
256               else
257               {
258                 output.append(" ");
259               }
260             }
261             else
262             {
263               output.append(" ");
264             }
265           }
266         } catch (IndexOutOfBoundsException e)
267         {
268           continue;
269         }
270       }
271       // Now print the second aligned sequence
272       output = output.append(NEWLINE);
273       output = output.append(new Format("%" + (maxid) + "s").form(strName))
274               .append(" ");
275       for (int i = 0; i < len; i++)
276       {
277         if ((i + (j * len)) < strRes.length())
278         {
279           output.append(strRes.charAt(i + (j * len)));
280         }
281       }
282       output.append(NEWLINE).append(NEWLINE);
283     }
284     float pid = (float) matchedSeqCount / seqRes.length() * 100;
285     // if (pid < SiftsSettings.getFailSafePIDThreshold())
286     // {
287     // throw new Exception(">>> Low PID detected for Phyre2 mapping...");
288     // }
289     output.append("Length of alignment = " + seqRes.length()).append(
290             NEWLINE);
291     output.append(new Format("Percentage ID = %2.2f").form(pid));
292     return output;
293   }
294
295
296
297   public static List<Phyre2SummaryPojo> parsePhyre2ResultSummaryTable(
298           String html)
299   {
300     List<Phyre2SummaryPojo> phyre2Results = new ArrayList<Phyre2SummaryPojo>();
301     try
302     {
303       File in = new File(html);
304       Document doc = Jsoup.parse(in, null);
305       // Document doc = Jsoup.connect(html).get();
306       Elements tableElements = doc.select("table.midshade");
307       for (Element table : tableElements)
308       {
309         System.out.println();
310         Elements tableRowElements = table.select(":not(thead) tr");
311         for (int i = 0; i < tableRowElements.size(); i++)
312         {
313           Element row = tableRowElements.get(i);
314           Elements rowItems = row.select("td");
315           if (rowItems.size() > 11)
316           {
317             // for (int j = 0; j < rowItems.size(); j++)
318             // {
319             // System.out.println(">>> r:" + j + "  =  "
320             // + rowItems.get(j).text());
321             // }
322
323             String c = rowItems.get(6).select("input").attr("onmouseover");
324             String alignedRange = c.substring(c.indexOf("Residues ") + 9,
325                     c.indexOf(" of your sequence aligned "));
326             String coverage = c.substring(c.lastIndexOf(" (") + 2,
327                     c.lastIndexOf(" coverage). Click to view detailed"));
328             // System.out.println("coverage" + coverage);
329             try
330             {
331               Phyre2SummaryPojo psp = new Phyre2SummaryPojo();
332               String sn = rowItems.get(0).text();
333               psp.setSerialNo(Integer.valueOf(sn));
334               psp.setTemplateId(rowItems.get(1).text());
335               psp.setCoverage(coverage);
336               psp.setAlignedRange(alignedRange);
337               psp.setConfidence(Double.valueOf(rowItems.get(8).text()));
338               psp.setPid(Integer.valueOf(rowItems.get(9).text()));
339               psp.setTemplateSummary(rowItems.get(10).text());
340               // System.out.println("row  >>>> " + psp.toString());
341               // System.out.println();
342               phyre2Results.add(psp);
343             } catch (NumberFormatException e)
344             {
345               e.printStackTrace();
346             } catch (IndexOutOfBoundsException e)
347             {
348               e.printStackTrace();
349             } catch (Exception e)
350             {
351               e.printStackTrace();
352             }
353           }
354         }
355       }
356       return phyre2Results;
357
358     } catch (Exception e)
359     {
360       e.printStackTrace();
361       return null;
362     }
363   }
364
365   public static DefaultTableModel getTableModel(
366           List<Phyre2SummaryPojo> phyreResults)
367   {
368     if (phyreResults == null)
369     {
370       return null;
371     }
372     DefaultTableModel tableModel = new DefaultTableModel()
373     {
374       @Override
375       public boolean isCellEditable(int row, int column)
376       {
377         return false;
378       }
379
380       @Override
381       public Class<?> getColumnClass(int columnIndex)
382       {
383         switch (columnIndex)
384         {
385         case 0:
386           return Integer.class;
387         case 1:
388           return String.class;
389         case 2:
390           return String.class;
391         case 3:
392           return String.class;
393         case 4:
394           return Double.class;
395         case 5:
396           return Integer.class;
397         case 6:
398           return String.class;
399         default:
400           return String.class;
401         }
402       }
403
404     };
405
406     tableModel.addColumn("#");
407     tableModel.addColumn("Template");
408     tableModel.addColumn("Aligned Range");
409     tableModel.addColumn("Coverage");
410     tableModel.addColumn("Confidence");
411     tableModel.addColumn("%.i.d");
412     tableModel.addColumn("Template Information");
413
414     for (Phyre2SummaryPojo res : phyreResults)
415     {
416       tableModel.addRow(new Object[] { res.getSerialNo(),
417           res.getTemplateId(), res.getAlignedRange(), res.getCoverage(),
418           res.getConfidence(), res.getPid(), res.getTemplateSummary() }); 
419     }
420     return tableModel;
421   }
422
423   public static void configurePhyreResultTable(JTable phyreResultTable)
424   {
425
426     DecimalFormatTableCellRenderer idCellRender = new DecimalFormatTableCellRenderer(
427             true, 0);
428     DecimalFormatTableCellRenderer pidCellRender = new DecimalFormatTableCellRenderer(
429             true, 1);
430     DecimalFormatTableCellRenderer confidenceCellRender = new DecimalFormatTableCellRenderer(
431             true, 1);
432
433     phyreResultTable.getColumn("#").setMinWidth(20);
434     phyreResultTable.getColumn("#").setPreferredWidth(30);
435     phyreResultTable.getColumn("#").setMaxWidth(40);
436     phyreResultTable.getColumn("#").setCellRenderer(idCellRender);
437
438     phyreResultTable.getColumn("Template").setMinWidth(60);
439     phyreResultTable.getColumn("Template").setPreferredWidth(60);
440     phyreResultTable.getColumn("Template").setMaxWidth(90);
441
442     phyreResultTable.getColumn("Aligned Range").setMinWidth(80);
443     phyreResultTable.getColumn("Aligned Range").setPreferredWidth(80);
444     phyreResultTable.getColumn("Aligned Range").setMaxWidth(120);
445
446     phyreResultTable.getColumn("Coverage").setMinWidth(60);
447     phyreResultTable.getColumn("Coverage").setPreferredWidth(60);
448     phyreResultTable.getColumn("Coverage").setMaxWidth(90);
449
450     phyreResultTable.getColumn("Confidence").setMinWidth(60);
451     phyreResultTable.getColumn("Confidence").setPreferredWidth(60);
452     phyreResultTable.getColumn("Confidence").setMaxWidth(90);
453     phyreResultTable.getColumn("Confidence").setCellRenderer(
454             confidenceCellRender);
455
456     phyreResultTable.getColumn("%.i.d").setMinWidth(45);
457     phyreResultTable.getColumn("%.i.d").setPreferredWidth(450);
458     phyreResultTable.getColumn("%.i.d").setMaxWidth(65);
459     phyreResultTable.getColumn("%.i.d").setCellRenderer(pidCellRender);
460
461     phyreResultTable.getColumn("Template Information").setMinWidth(400);
462     phyreResultTable.getColumn("Template Information").setPreferredWidth(
463             600);
464     phyreResultTable.getColumn("Template Information").setMaxWidth(1500);
465   }
466 }