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