JAL-3829 JAL-3855 ignore case when detecting alphafold ID
[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.SequenceI;
32 import jalview.io.DataSourceType;
33 import jalview.io.FileFormat;
34 import jalview.io.FileFormatI;
35 import jalview.io.FormatAdapter;
36 import jalview.io.PDBFeatureSettings;
37 import jalview.structure.StructureImportSettings;
38 import jalview.util.HttpUtils;
39 import jalview.util.MessageManager;
40 import jalview.ws.ebi.EBIFetchClient;
41 import jalview.ws.utils.UrlDownloadClient;
42
43 import java.io.File;
44 import java.util.ArrayList;
45 import java.util.List;
46
47 import com.stevesoft.pat.Regex;
48
49 /**
50  * @author JimP
51  * 
52  */
53 public class EBIAlfaFold extends EbiFileRetrievedProxy
54 {
55   private static final String SEPARATOR = "|";
56
57   private static final String COLON = ":";
58
59   private static final int PDB_ID_LENGTH = 4;
60
61   public EBIAlfaFold()
62   {
63     super();
64   }
65
66   /*
67    * (non-Javadoc)
68    * 
69    * @see jalview.ws.DbSourceProxy#getAccessionSeparator()
70    */
71   @Override
72   public String getAccessionSeparator()
73   {
74     return null;
75   }
76
77   /*
78    * (non-Javadoc)
79    * 
80    * @see jalview.ws.DbSourceProxy#getAccessionValidator()
81    */
82   @Override
83   public Regex getAccessionValidator()
84   {
85     Regex validator =  new Regex("(AF-[A-Z]+[0-9]+[A-Z0-9]+-F1)");
86     validator.setIgnoreCase(true);
87     return validator;
88   }
89
90   /*
91    * (non-Javadoc)
92    * 
93    * @see jalview.ws.DbSourceProxy#getDbSource()
94    */
95   @Override
96   public String getDbSource()
97   {
98     return "ALPHAFOLD";
99   }
100
101   /*
102    * (non-Javadoc)
103    * 
104    * @see jalview.ws.DbSourceProxy#getDbVersion()
105    */
106   @Override
107   public String getDbVersion()
108   {
109     return "1";
110   }
111
112   public static String getAlphaFoldCifDownloadUrl(String id)
113   {
114     return "https://alphafold.ebi.ac.uk/files/" + id + "-model_v1.cif";
115   }
116
117   /*
118    * (non-Javadoc)
119    * 
120    * @see jalview.ws.DbSourceProxy#getSequenceRecords(java.lang.String[])
121    */
122   @Override
123   public AlignmentI getSequenceRecords(String queries) throws Exception
124   {
125     AlignmentI pdbAlignment = null;
126     String chain = null;
127     String id = null;
128     if (queries.indexOf(COLON) > -1)
129     {
130       chain = queries.substring(queries.indexOf(COLON) + 1);
131       id = queries.substring(0, queries.indexOf(COLON));
132     }
133     else
134     {
135       id = queries;
136     }
137
138     if (!isValidReference(id))
139     {
140       System.err.println(
141               "(AFClient) Ignoring invalid pdb query: '" + id + "'");
142       stopQuery();
143       return null;
144     }
145     String alphaFoldCif = getAlphaFoldCifDownloadUrl(id);
146
147     try
148     {
149       File tmpFile = File.createTempFile(id, ".cif");
150       UrlDownloadClient.download(alphaFoldCif, tmpFile);
151       
152       // may not need this check ?
153       file = tmpFile.getAbsolutePath();
154       if (file == null)
155       {
156         return null;
157       }
158
159       pdbAlignment = importDownloadedStructureFromUrl(alphaFoldCif, tmpFile, id, chain, getDbSource(),getDbVersion());
160       
161
162       if (pdbAlignment == null || pdbAlignment.getHeight() < 1)
163       {
164         throw new Exception(MessageManager.formatMessage(
165                 "exception.no_pdb_records_for_chain", new String[]
166                 { id, ((chain == null) ? "' '" : chain) }));
167       }
168
169     } catch (Exception ex) // Problem parsing PDB file
170     {
171       stopQuery();
172       throw (ex);
173     }
174     return pdbAlignment;
175   }
176
177   /**
178    * general purpose structure importer - designed to yield alignment useful for transfer of annotation to associated sequences
179    * @param alphaFoldCif
180    * @param tmpFile
181    * @param id
182    * @param chain
183    * @param dbSource
184    * @param dbVersion
185    * @return
186    * @throws Exception
187    */
188   public static AlignmentI importDownloadedStructureFromUrl(String alphaFoldCif,
189           File tmpFile, String id, String chain, String dbSource, String dbVersion) throws Exception
190   {
191     String file = tmpFile.getAbsolutePath();
192     // todo get rid of Type and use FileFormatI instead?
193     FileFormatI fileFormat = FileFormat.MMCif;
194     AlignmentI pdbAlignment = new FormatAdapter().readFile(tmpFile,
195             DataSourceType.FILE, fileFormat);
196     if (pdbAlignment != null)
197     {
198       List<SequenceI> toremove = new ArrayList<SequenceI>();
199       for (SequenceI pdbcs : pdbAlignment.getSequences())
200       {
201         String chid = null;
202         // Mapping map=null;
203         for (PDBEntry pid : pdbcs.getAllPDBEntries())
204         {
205           if (pid.getFile() == file)
206           {
207             chid = pid.getChainCode();
208
209           }
210         }
211         if (chain == null || (chid != null && (chid.equals(chain)
212                 || chid.trim().equals(chain.trim())
213                 || (chain.trim().length() == 0 && chid.equals("_")))))
214         {
215           // FIXME seems to result in 'PDB|1QIP|1qip|A' - 1QIP is redundant.
216           // TODO: suggest simplify naming to 1qip|A as default name defined
217           pdbcs.setName(id + SEPARATOR + pdbcs.getName());
218           // Might need to add more metadata to the PDBEntry object
219           // like below
220           /*
221            * PDBEntry entry = new PDBEntry(); // Construct the PDBEntry
222            * entry.setId(id); if (entry.getProperty() == null)
223            * entry.setProperty(new Hashtable());
224            * entry.getProperty().put("chains", pdbchain.id + "=" +
225            * sq.getStart() + "-" + sq.getEnd());
226            * sq.getDatasetSequence().addPDBId(entry);
227            */
228           // Add PDB DB Refs
229           // We make a DBRefEtntry because we have obtained the PDB file from
230           // a
231           // verifiable source
232           // JBPNote - PDB DBRefEntry should also carry the chain and mapping
233           // information
234           if (dbSource != null)
235           {
236             DBRefEntry dbentry = new DBRefEntry(dbSource,
237
238                     dbVersion, (chid == null ? id : id + chid));
239             // dbentry.setMap()
240             pdbcs.addDBRef(dbentry);
241           }
242         }
243         else
244         {
245           // mark this sequence to be removed from the alignment
246           // - since it's not from the right chain
247           toremove.add(pdbcs);
248         }
249       }
250       // now remove marked sequences
251       for (SequenceI pdbcs : toremove)
252       {
253         pdbAlignment.deleteSequence(pdbcs);
254         if (pdbcs.getAnnotation() != null)
255         {
256           for (AlignmentAnnotation aa : pdbcs.getAnnotation())
257           {
258             pdbAlignment.deleteAnnotation(aa);
259           }
260         }
261       }
262     }
263     return pdbAlignment;
264   }
265
266   /*
267    * (non-Javadoc)
268    * 
269    * @see jalview.ws.DbSourceProxy#isValidReference(java.lang.String)
270    */
271   @Override
272   public boolean isValidReference(String accession)
273   {
274     Regex r = getAccessionValidator();
275     return r.search(accession.trim());
276   }
277
278   /**
279    * human glyoxalase
280    */
281   @Override
282   public String getTestQuery()
283   {
284     return "1QIP";
285   }
286
287   @Override
288   public String getDbName()
289   {
290     return "PDB"; // getDbSource();
291   }
292
293   @Override
294   public int getTier()
295   {
296     return 0;
297   }
298
299   /**
300    * Returns a descriptor for suitable feature display settings with
301    * <ul>
302    * <li>ResNums or insertions features visible</li>
303    * <li>insertions features coloured red</li>
304    * <li>ResNum features coloured by label</li>
305    * <li>Insertions displayed above (on top of) ResNums</li>
306    * </ul>
307    */
308   @Override
309   public FeatureSettingsModelI getFeatureColourScheme()
310   {
311     return new PDBFeatureSettings();
312   }
313 }