JAL-4012 - JAL-4020 release notes and a bit of ‘troubleshooting’ documentation
[jalview.git] / src / jalview / ws / dbsources / EBIAlfaFold.java
1
2 /*
3  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
4  * Copyright (C) $$Year-Rel$$ The Jalview Authors
5  * 
6  * This file is part of Jalview.
7  * 
8  * Jalview is free software: you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License 
10  * as published by the Free Software Foundation, either version 3
11  * of the License, or (at your option) any later version.
12  *  
13  * Jalview is distributed in the hope that it will be useful, but 
14  * WITHOUT ANY WARRANTY; without even the implied warranty 
15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
16  * PURPOSE.  See the GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
20  * The Jalview Authors are detailed in the 'AUTHORS' file.
21  */
22 package jalview.ws.dbsources;
23
24 import jalview.api.FeatureSettingsModelI;
25 import jalview.datamodel.AlignmentAnnotation;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.DBRefEntry;
28 import jalview.datamodel.DBRefSource;
29 import jalview.datamodel.PDBEntry;
30 import jalview.datamodel.PDBEntry.Type;
31 import jalview.datamodel.SequenceFeature;
32 import jalview.datamodel.SequenceI;
33 import jalview.datamodel.features.SequenceFeaturesI;
34 import jalview.io.DataSourceType;
35 import jalview.io.FileFormat;
36 import jalview.io.FileFormatI;
37 import jalview.io.FormatAdapter;
38 import jalview.io.PDBFeatureSettings;
39 import jalview.structure.StructureImportSettings;
40 import jalview.util.HttpUtils;
41 import jalview.util.MessageManager;
42 import jalview.ws.ebi.EBIFetchClient;
43 import jalview.ws.utils.UrlDownloadClient;
44
45 import java.io.File;
46 import java.util.ArrayList;
47 import java.util.List;
48
49 import com.stevesoft.pat.Regex;
50
51 /**
52  * @author JimP
53  * 
54  */
55 public class EBIAlfaFold extends EbiFileRetrievedProxy
56 {
57   private static final String SEPARATOR = "|";
58
59   private static final String COLON = ":";
60
61   private static final int PDB_ID_LENGTH = 4;
62
63   public EBIAlfaFold()
64   {
65     super();
66   }
67
68   /*
69    * (non-Javadoc)
70    * 
71    * @see jalview.ws.DbSourceProxy#getAccessionSeparator()
72    */
73   @Override
74   public String getAccessionSeparator()
75   {
76     return null;
77   }
78
79   /*
80    * (non-Javadoc)
81    * 
82    * @see jalview.ws.DbSourceProxy#getAccessionValidator()
83    */
84   @Override
85   public Regex getAccessionValidator()
86   {
87     Regex validator = new Regex("(AF-[A-Z]+[0-9]+[A-Z0-9]+-F1)");
88     validator.setIgnoreCase(true);
89     return validator;
90   }
91
92   /*
93    * (non-Javadoc)
94    * 
95    * @see jalview.ws.DbSourceProxy#getDbSource()
96    */
97   @Override
98   public String getDbSource()
99   {
100     return "ALPHAFOLD";
101   }
102
103   /*
104    * (non-Javadoc)
105    * 
106    * @see jalview.ws.DbSourceProxy#getDbVersion()
107    */
108   @Override
109   public String getDbVersion()
110   {
111     return "1";
112   }
113
114   public static String getAlphaFoldCifDownloadUrl(String id)
115   {
116     return "https://alphafold.ebi.ac.uk/files/" + id + "-model_v1.cif";
117   }
118
119   /*
120    * (non-Javadoc)
121    * 
122    * @see jalview.ws.DbSourceProxy#getSequenceRecords(java.lang.String[])
123    */
124   @Override
125   public AlignmentI getSequenceRecords(String queries) throws Exception
126   {
127     return getSequenceRecords(queries, null);
128   }
129
130   public AlignmentI getSequenceRecords(String queries, String retrievalUrl)
131           throws Exception
132   {
133     AlignmentI pdbAlignment = null;
134     String chain = null;
135     String id = null;
136     if (queries.indexOf(COLON) > -1)
137     {
138       chain = queries.substring(queries.indexOf(COLON) + 1);
139       id = queries.substring(0, queries.indexOf(COLON));
140     }
141     else
142     {
143       id = queries;
144     }
145
146     if (!isValidReference(id))
147     {
148       System.err.println(
149               "(AFClient) Ignoring invalid pdb query: '" + id + "'");
150       stopQuery();
151       return null;
152     }
153     String alphaFoldCif = getAlphaFoldCifDownloadUrl(id);
154     if (retrievalUrl != null)
155     {
156       alphaFoldCif = retrievalUrl;
157     }
158
159     try
160     {
161       File tmpFile = File.createTempFile(id, ".cif");
162       UrlDownloadClient.download(alphaFoldCif, tmpFile);
163
164       // may not need this check ?
165       file = tmpFile.getAbsolutePath();
166       if (file == null)
167       {
168         return null;
169       }
170
171       pdbAlignment = importDownloadedStructureFromUrl(alphaFoldCif, tmpFile,
172               id, chain, getDbSource(), getDbVersion());
173
174       if (pdbAlignment == null || pdbAlignment.getHeight() < 1)
175       {
176         throw new Exception(MessageManager.formatMessage(
177                 "exception.no_pdb_records_for_chain", new String[]
178                 { id, ((chain == null) ? "' '" : chain) }));
179       }
180
181     } catch (Exception ex) // Problem parsing PDB file
182     {
183       stopQuery();
184       throw (ex);
185     }
186     return pdbAlignment;
187   }
188
189   /**
190    * general purpose structure importer - designed to yield alignment useful for
191    * transfer of annotation to associated sequences
192    * 
193    * @param alphaFoldCif
194    * @param tmpFile
195    * @param id
196    * @param chain
197    * @param dbSource
198    * @param dbVersion
199    * @return
200    * @throws Exception
201    */
202   public static AlignmentI importDownloadedStructureFromUrl(
203           String alphaFoldCif, File tmpFile, String id, String chain,
204           String dbSource, String dbVersion) throws Exception
205   {
206     String file = tmpFile.getAbsolutePath();
207     // todo get rid of Type and use FileFormatI instead?
208     FileFormatI fileFormat = FileFormat.MMCif;
209     AlignmentI pdbAlignment = new FormatAdapter().readFile(tmpFile,
210             DataSourceType.FILE, fileFormat);
211     if (pdbAlignment != null)
212     {
213       List<SequenceI> toremove = new ArrayList<SequenceI>();
214       for (SequenceI pdbcs : pdbAlignment.getSequences())
215       {
216         String chid = null;
217         // Mapping map=null;
218         for (PDBEntry pid : pdbcs.getAllPDBEntries())
219         {
220           if (pid.getFile() == file)
221           {
222             chid = pid.getChainCode();
223
224           }
225         }
226         if (chain == null || (chid != null && (chid.equals(chain)
227                 || chid.trim().equals(chain.trim())
228                 || (chain.trim().length() == 0 && chid.equals("_")))))
229         {
230           // FIXME seems to result in 'PDB|1QIP|1qip|A' - 1QIP is redundant.
231           // TODO: suggest simplify naming to 1qip|A as default name defined
232           pdbcs.setName(id + SEPARATOR + pdbcs.getName());
233           // Might need to add more metadata to the PDBEntry object
234           // like below
235           /*
236            * PDBEntry entry = new PDBEntry(); // Construct the PDBEntry
237            * entry.setId(id); if (entry.getProperty() == null)
238            * entry.setProperty(new Hashtable());
239            * entry.getProperty().put("chains", pdbchain.id + "=" +
240            * sq.getStart() + "-" + sq.getEnd());
241            * sq.getDatasetSequence().addPDBId(entry);
242            */
243           // Add PDB DB Refs
244           // We make a DBRefEtntry because we have obtained the PDB file from
245           // a
246           // verifiable source
247           // JBPNote - PDB DBRefEntry should also carry the chain and mapping
248           // information
249           if (dbSource != null)
250           {
251             DBRefEntry dbentry = new DBRefEntry(dbSource,
252
253                     dbVersion, (chid == null ? id : id + chid));
254             // dbentry.setMap()
255             pdbcs.addDBRef(dbentry);
256             // update any feature groups
257             List<SequenceFeature> allsf = pdbcs.getFeatures()
258                     .getAllFeatures();
259             List<SequenceFeature> newsf = new ArrayList<SequenceFeature>();
260             if (allsf != null && allsf.size() > 0)
261             {
262               for (SequenceFeature f : allsf)
263               {
264                 if (file.equals(f.getFeatureGroup()))
265                 {
266                   f = new SequenceFeature(f, f.type, f.begin, f.end, id,
267                           f.score);
268                 }
269                 newsf.add(f);
270               }
271               pdbcs.setSequenceFeatures(newsf);
272             }
273           }
274         }
275         else
276         {
277           // mark this sequence to be removed from the alignment
278           // - since it's not from the right chain
279           toremove.add(pdbcs);
280         }
281       }
282       // now remove marked sequences
283       for (SequenceI pdbcs : toremove)
284       {
285         pdbAlignment.deleteSequence(pdbcs);
286         if (pdbcs.getAnnotation() != null)
287         {
288           for (AlignmentAnnotation aa : pdbcs.getAnnotation())
289           {
290             pdbAlignment.deleteAnnotation(aa);
291           }
292         }
293       }
294     }
295     return pdbAlignment;
296   }
297
298   /*
299    * (non-Javadoc)
300    * 
301    * @see jalview.ws.DbSourceProxy#isValidReference(java.lang.String)
302    */
303   @Override
304   public boolean isValidReference(String accession)
305   {
306     Regex r = getAccessionValidator();
307     return r.search(accession.trim());
308   }
309
310   /**
311    * human glyoxalase
312    */
313   @Override
314   public String getTestQuery()
315   {
316     return "1QIP";
317   }
318
319   @Override
320   public String getDbName()
321   {
322     return "PDB"; // getDbSource();
323   }
324
325   @Override
326   public int getTier()
327   {
328     return 0;
329   }
330
331   /**
332    * Returns a descriptor for suitable feature display settings with
333    * <ul>
334    * <li>ResNums or insertions features visible</li>
335    * <li>insertions features coloured red</li>
336    * <li>ResNum features coloured by label</li>
337    * <li>Insertions displayed above (on top of) ResNums</li>
338    * </ul>
339    */
340   @Override
341   public FeatureSettingsModelI getFeatureColourScheme()
342   {
343     return new PDBFeatureSettings();
344   }
345
346 }