JAL-2295 don't exclude RESNUM from transfer to Chimera
[jalview.git] / test / jalview / ext / rbvi / chimera / JalviewChimeraView.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.ext.rbvi.chimera;
22
23 import static org.testng.Assert.assertEquals;
24 import static org.testng.Assert.assertFalse;
25 import static org.testng.Assert.assertNotNull;
26 import static org.testng.Assert.assertTrue;
27
28 import jalview.api.FeatureRenderer;
29 import jalview.api.structures.JalviewStructureDisplayI;
30 import jalview.bin.Cache;
31 import jalview.bin.Jalview;
32 import jalview.datamodel.DBRefEntry;
33 import jalview.datamodel.PDBEntry;
34 import jalview.datamodel.SequenceFeature;
35 import jalview.datamodel.SequenceI;
36 import jalview.gui.AlignFrame;
37 import jalview.gui.Desktop;
38 import jalview.gui.Preferences;
39 import jalview.gui.StructureViewer;
40 import jalview.gui.StructureViewer.ViewerType;
41 import jalview.io.FileLoader;
42 import jalview.io.FormatAdapter;
43 import jalview.structure.StructureMapping;
44 import jalview.structure.StructureSelectionManager;
45 import jalview.ws.sifts.SiftsClient;
46 import jalview.ws.sifts.SiftsException;
47 import jalview.ws.sifts.SiftsSettings;
48
49 import java.io.File;
50 import java.io.IOException;
51 import java.util.List;
52 import java.util.Vector;
53
54 import org.testng.annotations.AfterClass;
55 import org.testng.annotations.AfterMethod;
56 import org.testng.annotations.BeforeClass;
57 import org.testng.annotations.Test;
58
59 @Test(singleThreaded = true)
60 public class JalviewChimeraView
61 {
62
63   private JalviewStructureDisplayI chimeraViewer;
64
65   /**
66    * @throws java.lang.Exception
67    */
68   @BeforeClass(alwaysRun = true)
69   public static void setUpBeforeClass() throws Exception
70   {
71     Jalview.main(new String[] { "-noquestionnaire", "-nonews", "-props",
72         "test/jalview/ext/rbvi/chimera/testProps.jvprops" });
73     Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
74             ViewerType.CHIMERA.name());
75     Cache.setProperty("SHOW_ANNOTATIONS", "false");
76     Cache.setProperty(Preferences.STRUCT_FROM_PDB, "false");
77     Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
78             ViewerType.CHIMERA.name());
79     Cache.setProperty("MAP_WITH_SIFTS", "true");
80     // TODO this should not be necessary!
81     SiftsSettings.setMapWithSifts(true);
82   }
83
84   /**
85    * @throws java.lang.Exception
86    */
87   @AfterClass(alwaysRun = true)
88   public static void tearDownAfterClass() throws Exception
89   {
90     Desktop.instance.closeAll_actionPerformed(null);
91   }
92
93   @AfterMethod(alwaysRun = true)
94   public void tearDownAfterTest() throws Exception
95   {
96     SiftsClient.setMockSiftsFile(null);
97     if (chimeraViewer != null)
98     {
99       chimeraViewer.closeViewer(true);
100     }
101   }
102
103   /**
104    * Load 1GAQ and view the first structure for which a PDB id is found. Note no
105    * network connection is needed - PDB file is read locally, SIFTS fetch fails
106    * so mapping falls back to Needleman-Wunsch - ok for this test.
107    */
108   // External as local install of Chimera required
109   @Test(groups = { "External" })
110   public void testSingleSeqViewChimera()
111   {
112     String inFile = "examples/1gaq.txt";
113     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
114             FormatAdapter.FILE);
115     assertNotNull(af, "Failed to create AlignFrame");
116     SequenceI sq = af.getViewport().getAlignment().getSequenceAt(0);
117     assertEquals(sq.getName(), "1GAQ|A");
118     SequenceI dsq = sq.getDatasetSequence();
119     Vector<PDBEntry> pdbIds = dsq.getAllPDBEntries();
120     assertEquals(pdbIds.size(), 1);
121     PDBEntry pdbEntry = pdbIds.get(0);
122     assertEquals(pdbEntry.getId(), "1GAQ");
123     StructureViewer structureViewer = new StructureViewer(af.getViewport()
124             .getStructureSelectionManager());
125     chimeraViewer = structureViewer.viewStructures(pdbEntry,
126             new SequenceI[] { sq }, af.getCurrentView().getAlignPanel());
127     JalviewChimeraBinding binding = (JalviewChimeraBinding) chimeraViewer
128             .getBinding();
129
130     /*
131      * Wait for viewer load thread to complete
132      */
133     while (!binding.isFinishedInit())
134     {
135       try
136       {
137         Thread.sleep(500);
138       } catch (InterruptedException e)
139       {
140       }
141     }
142
143     assertTrue(binding.isChimeraRunning(), "Failed to start Chimera");
144
145     assertEquals(chimeraViewer.getBinding().getPdbCount(), 1);
146     chimeraViewer.closeViewer(true);
147     chimeraViewer = null;
148     return;
149   }
150
151   /**
152    * Test for writing Jalview features as attributes on mapped residues in
153    * Chimera. Note this uses local copies of PDB and SIFTS file, no network
154    * connection required.
155    * 
156    * @throws IOException
157    * @throws SiftsException
158    */
159   // External as this requires a local install of Chimera
160   @Test(groups = { "External" })
161   public void testTransferFeatures() throws IOException, SiftsException
162   {
163     String inFile = "examples/uniref50.fa";
164     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
165             FormatAdapter.FILE);
166     assertNotNull(af, "Failed to create AlignFrame");
167     SequenceI sq = af.getViewport().getAlignment().findName("FER2_ARATH");
168     assertNotNull(sq, "Didn't find FER2_ARATH");
169
170     /*
171      * need a Uniprot dbref for SIFTS mapping to work!!
172      */
173     sq.addDBRef(new DBRefEntry("UNIPROT", "0", "P16972", null));
174
175     /*
176      * use local test PDB and SIFTS files
177      */
178     String pdbFilePath = new File(
179             "test/jalview/ext/rbvi/chimera/4zho.pdb").getPath();
180     PDBEntry pdbEntry = new PDBEntry("4ZHO", null, null, pdbFilePath);
181     String siftsFilePath = new File(
182             "test/jalview/ext/rbvi/chimera/4zho.xml.gz")
183             .getPath();
184     SiftsClient.setMockSiftsFile(new File(siftsFilePath));
185
186     StructureViewer structureViewer = new StructureViewer(af.getViewport()
187             .getStructureSelectionManager());
188     chimeraViewer = structureViewer.viewStructures(pdbEntry,
189             new SequenceI[] { sq }, af.getCurrentView().getAlignPanel());
190
191     JalviewChimeraBinding binding = (JalviewChimeraBinding) chimeraViewer
192             .getBinding();
193     do
194     {
195       try
196       {
197         Thread.sleep(500);
198       } catch (InterruptedException e)
199       {
200       }
201     } while (!binding.isFinishedInit());
202
203     assertTrue(binding.isChimeraRunning(), "Failed to launch Chimera");
204
205     assertEquals(binding.getPdbCount(), 1);
206
207     /*
208      * check mapping is (sequence) 53-145 to (structure) 2-94 A/B
209      * (or possibly 52-145 to 1-94 - see JAL-2319)
210      */
211     StructureSelectionManager ssm = binding.getSsm();
212     String pdbFile = binding.getPdbFile()[0];
213     StructureMapping[] mappings = ssm.getMapping(pdbFile);
214     assertTrue(mappings[0].getMappingDetailsOutput().contains("SIFTS"),
215             "Failed to perform SIFTS mapping");
216     assertEquals(mappings.length, 2);
217     assertEquals(mappings[0].getChain(), "A");
218     assertEquals(mappings[0].getPDBResNum(53), 2);
219     assertEquals(mappings[0].getPDBResNum(145), 94);
220     assertEquals(mappings[1].getChain(), "B");
221     assertEquals(mappings[1].getPDBResNum(53), 2);
222     assertEquals(mappings[1].getPDBResNum(145), 94);
223
224     /*
225      * now add some features to FER2_ARATH 
226      */
227     // feature on a sequence region not mapped to structure:
228     sq.addSequenceFeature(new SequenceFeature("transit peptide",
229             "chloroplast", 1, 51, Float.NaN, null));
230     // feature on a region mapped to structure:
231     sq.addSequenceFeature(new SequenceFeature("domain",
232             "2Fe-2S ferredoxin-type", 55, 145, Float.NaN, null));
233     // on sparse positions of the sequence
234     sq.addSequenceFeature(new SequenceFeature("metal ion-binding site",
235             "Iron-Sulfur (2Fe-2S)", 91, 91, Float.NaN, null));
236     sq.addSequenceFeature(new SequenceFeature("metal ion-binding site",
237             "Iron-Sulfur (2Fe-2S)", 96, 96, Float.NaN, null));
238     // on a sequence region that is partially mapped to structure:
239     sq.addSequenceFeature(new SequenceFeature("helix", null, 50, 60,
240             Float.NaN, null));
241     // and again:
242     sq.addSequenceFeature(new SequenceFeature("chain", null, 50, 70,
243             Float.NaN, null));
244     // add numeric valued features - score is set as attribute value
245     sq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 62,
246             62, -2.1f, null));
247     sq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 65,
248             65, 3.6f, null));
249     sq.addSequenceFeature(new SequenceFeature("RESNUM", "ALA:   2  4zhoA",
250             53, 53, Float.NaN, null));
251
252     /*
253      * set all features visible except for chain
254      */
255     af.setShowSeqFeatures(true);
256     FeatureRenderer fr = af.getFeatureRenderer();
257     fr.setVisible("transit peptide");
258     fr.setVisible("domain");
259     fr.setVisible("metal ion-binding site");
260     fr.setVisible("helix");
261     fr.setVisible("kd");
262     fr.setVisible("RESNUM");
263
264     /*
265      * 'perform' menu action to copy visible features to
266      * attributes in Chimera
267      */
268     // TODO rename and pull up method to binding interface
269     // once functionality is added for Jmol as well
270     binding.sendFeaturesToViewer(af.getViewport().getAlignPanel());
271
272     /*
273      * give Chimera time to open the commands file and execute it
274      */
275     try
276     {
277       Thread.sleep(1000);
278     } catch (InterruptedException e)
279     {
280     }
281
282     /*
283      * ask Chimera for its residue attribute names
284      */
285     List<String> reply = binding.sendChimeraCommand("list resattr", true);
286     // prefixed and sanitised attribute names for Jalview features:
287     assertTrue(reply.contains("resattr jv_domain"));
288     assertTrue(reply.contains("resattr jv_metal_ion_binding_site"));
289     assertTrue(reply.contains("resattr jv_helix"));
290     assertTrue(reply.contains("resattr jv_kd"));
291     assertTrue(reply.contains("resattr jv_RESNUM"));
292     // feature is not on a mapped region - no attribute created
293     assertFalse(reply.contains("resattr jv_transit_peptide"));
294     // feature is not visible - no attribute created
295     assertFalse(reply.contains("resattr jv_chain"));
296
297     /*
298      * ask Chimera for residues with an attribute
299      * 91 and 96 on sequence --> residues 40 and 45 on chains A and B
300      */
301     reply = binding.sendChimeraCommand(
302             "list resi att jv_metal_ion_binding_site", true);
303     assertEquals(reply.size(), 4);
304     assertTrue(reply
305             .contains("residue id #0:40.A jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 40"));
306     assertTrue(reply
307             .contains("residue id #0:45.A jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 45"));
308     assertTrue(reply
309             .contains("residue id #0:40.B jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 40"));
310     assertTrue(reply
311             .contains("residue id #0:45.B jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 45"));
312
313     /*
314      * check attributes with score values
315      * sequence positions 62 and 65 --> residues 11 and 14 on chains A and B
316      */
317     reply = binding.sendChimeraCommand("list resi att jv_kd", true);
318     assertEquals(reply.size(), 4);
319     assertTrue(reply.contains("residue id #0:11.A jv_kd -2.1 index 11"));
320     assertTrue(reply.contains("residue id #0:14.A jv_kd 3.6 index 14"));
321     assertTrue(reply.contains("residue id #0:11.B jv_kd -2.1 index 11"));
322     assertTrue(reply.contains("residue id #0:14.B jv_kd 3.6 index 14"));
323
324     /*
325      * list residues with positive kd score 
326      */
327     reply = binding.sendChimeraCommand(
328             "list resi spec :*/jv_kd>0 attr jv_kd", true);
329     assertEquals(reply.size(), 2);
330     assertTrue(reply.contains("residue id #0:14.A jv_kd 3.6 index 14"));
331     assertTrue(reply.contains("residue id #0:14.B jv_kd 3.6 index 14"));
332
333     SiftsClient.setMockSiftsFile(null);
334     chimeraViewer.closeViewer(true);
335     chimeraViewer = null;
336   }
337 }