X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fws%2Fdbsources%2FEBIAlfaFold.java;h=5ab05e09598353709bdb7a0888044931719a7ae3;hb=a06dd69fae641b3f5a8f202b439796df8469abb6;hp=bbb1f8b04680e13e481e87036649b3e62a2be6f2;hpb=8286731258d314669ee6facb627947fe159ac1eb;p=jalview.git diff --git a/src/jalview/ws/dbsources/EBIAlfaFold.java b/src/jalview/ws/dbsources/EBIAlfaFold.java index bbb1f8b..5ab05e0 100644 --- a/src/jalview/ws/dbsources/EBIAlfaFold.java +++ b/src/jalview/ws/dbsources/EBIAlfaFold.java @@ -21,45 +21,47 @@ */ package jalview.ws.dbsources; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.ParseException; + +import com.stevesoft.pat.Regex; + import jalview.api.FeatureSettingsModelI; -import jalview.bin.Cache; import jalview.bin.Console; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; -import jalview.datamodel.ContactMatrix; import jalview.datamodel.ContactMatrixI; import jalview.datamodel.DBRefEntry; -import jalview.datamodel.DBRefSource; +import jalview.datamodel.GroupSet; import jalview.datamodel.PDBEntry; -import jalview.datamodel.PDBEntry.Type; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; -import jalview.datamodel.features.SequenceFeaturesI; +import jalview.gui.Desktop; import jalview.io.DataSourceType; import jalview.io.FileFormat; import jalview.io.FileFormatI; import jalview.io.FormatAdapter; import jalview.io.PDBFeatureSettings; -import jalview.javascript.json.JSON; -import jalview.structure.StructureImportSettings; -import jalview.util.HttpUtils; +import jalview.structure.StructureImportSettings.TFType; +import jalview.structure.StructureMapping; +import jalview.structure.StructureSelectionManager; import jalview.util.MessageManager; import jalview.util.Platform; import jalview.ws.datamodel.alphafold.PAEContactMatrix; -import jalview.ws.ebi.EBIFetchClient; import jalview.ws.utils.UrlDownloadClient; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.jmol.adapter.readers.simple.JSONReader; - -import com.stevesoft.pat.Regex; - /** * @author JimP * @@ -72,6 +74,8 @@ public class EBIAlfaFold extends EbiFileRetrievedProxy private static final int PDB_ID_LENGTH = 4; + private static String AF_VERSION = "3"; + public EBIAlfaFold() { super(); @@ -96,7 +100,7 @@ public class EBIAlfaFold extends EbiFileRetrievedProxy @Override public Regex getAccessionValidator() { - Regex validator = new Regex("(AF-[A-Z]+[0-9]+[A-Z0-9]+-F1)"); + Regex validator = new Regex("(AF-[A-Z]+[0-9]+[A-Z0-9]+-F1)"); validator.setIgnoreCase(true); return validator; } @@ -123,15 +127,24 @@ public class EBIAlfaFold extends EbiFileRetrievedProxy return "1"; } - public static String getAlphaFoldCifDownloadUrl(String id) + public static String getAlphaFoldCifDownloadUrl(String id, String vnum) { - return "https://alphafold.ebi.ac.uk/files/" + id + "-model_v1.cif"; + if (vnum == null || vnum.length() == 0) + { + vnum = AF_VERSION; + } + return "https://alphafold.ebi.ac.uk/files/" + id + "-model_v" + vnum + + ".cif"; } - public static String getAlphaFoldPaeDownloadUrl(String id) + public static String getAlphaFoldPaeDownloadUrl(String id, String vnum) { + if (vnum == null || vnum.length() == 0) + { + vnum = AF_VERSION; + } return "https://alphafold.ebi.ac.uk/files/" + id - + "-predicted_aligned_error_v1.json"; + + "-predicted_aligned_error_v" + vnum + ".json"; } /* @@ -144,7 +157,9 @@ public class EBIAlfaFold extends EbiFileRetrievedProxy { return getSequenceRecords(queries, null); } - public AlignmentI getSequenceRecords(String queries, String retrievalUrl) throws Exception + + public AlignmentI getSequenceRecords(String queries, String retrievalUrl) + throws Exception { AlignmentI pdbAlignment = null; String chain = null; @@ -166,7 +181,7 @@ public class EBIAlfaFold extends EbiFileRetrievedProxy stopQuery(); return null; } - String alphaFoldCif = getAlphaFoldCifDownloadUrl(id); + String alphaFoldCif = getAlphaFoldCifDownloadUrl(id, AF_VERSION); if (retrievalUrl != null) { alphaFoldCif = retrievalUrl; @@ -175,18 +190,20 @@ public class EBIAlfaFold extends EbiFileRetrievedProxy try { File tmpFile = File.createTempFile(id, ".cif"); - Console.debug("Retrieving structure file for "+id+" from "+alphaFoldCif); + Console.debug("Retrieving structure file for " + id + " from " + + alphaFoldCif); UrlDownloadClient.download(alphaFoldCif, tmpFile); - + // may not need this check ? file = tmpFile.getAbsolutePath(); if (file == null) { return null; } + // TODO Get the PAE file somewhere around here and remove from JmolParser - pdbAlignment = importDownloadedStructureFromUrl(alphaFoldCif, tmpFile, id, chain, getDbSource(),getDbVersion()); - + pdbAlignment = importDownloadedStructureFromUrl(alphaFoldCif, tmpFile, + id, chain, getDbSource(), getDbVersion()); if (pdbAlignment == null || pdbAlignment.getHeight() < 1) { @@ -194,58 +211,311 @@ public class EBIAlfaFold extends EbiFileRetrievedProxy "exception.no_pdb_records_for_chain", new String[] { id, ((chain == null) ? "' '" : chain) })); } + // done during structure retrieval + // retrieve_AlphaFold_pAE(id, pdbAlignment, retrievalUrl); + + } catch (Exception ex) // Problem parsing PDB file + { + stopQuery(); + throw (ex); + } + return pdbAlignment; + } + + /** + * get an alphafold pAE for the given id and return the File object of the + * downloaded (temp) file + * + * @param id + * @param pdbAlignment + * @param retrievalUrl + * - URL of .mmcif from EBI-AlphaFold - will be used to generate the + * pAE URL automatically + * @throws IOException + * @throws Exception + */ + public static File fetchAlphaFoldPAE(String id, String retrievalUrl) + throws IOException + { + // import PAE as contact matrix - assume this will work if there was a + // model + String paeURL = getAlphaFoldPaeDownloadUrl(id, AF_VERSION); + + if (retrievalUrl != null) + { + // manufacture the PAE url from a url like ...-model-vN.cif + paeURL = retrievalUrl.replace("model", "predicted_aligned_error") + .replace(".cif", ".json"); + } + + // check the cache + File pae = paeDownloadCache.get(paeURL); + if (pae != null && pae.exists() && (new Date().getTime() + - pae.lastModified()) < PAE_CACHE_STALE_TIME) + { + Console.debug( + "Using existing file in PAE cache for '" + paeURL + "'"); + return pae; + } + + try + { + pae = File.createTempFile(id == null ? "af_pae" : id, "pae_json"); + } catch (IOException e) + { + e.printStackTrace(); + } + Console.debug("Downloading pae from " + paeURL + " to " + pae.toString() + + ""); + try + { + UrlDownloadClient.download(paeURL, pae); + } catch (IOException e) + { + throw e; + } + // cache and it if successful + paeDownloadCache.put(paeURL, pae); + return pae; + } + + /** + * get an alphafold pAE for the given id, and add it to sequence 0 in + * pdbAlignment (assuming it came from structurefile parser). + * + * @param id + * @param pdbAlignment + * @param retrievalUrl + * - URL of .mmcif from EBI-AlphaFold - will be used to generate the + * pAE URL automatically + * @throws IOException + * @throws Exception + */ + public static void retrieve_AlphaFold_pAE(String id, + AlignmentI pdbAlignment, String retrievalUrl) throws IOException + { + File pae = fetchAlphaFoldPAE(id, retrievalUrl); + addAlphaFoldPAE(pdbAlignment, pae, 0, null, false, false, null); + } + + public static void addAlphaFoldPAE(AlignmentI pdbAlignment, File pae, + int index, String id, boolean isStruct, boolean isStructId, + String label) + { + FileInputStream paeInput = null; + try + { + paeInput = new FileInputStream(pae); + } catch (FileNotFoundException e) + { + Console.error( + "Could not find pAE file '" + pae.getAbsolutePath() + "'", e); + return; + } + + if (isStruct) + { + // ###### WRITE A TEST for this bit of the logic addAlphaFoldPAE with + // different params. + StructureSelectionManager ssm = StructureSelectionManager + .getStructureSelectionManager(Desktop.instance); + if (ssm != null) + { + String structFilename = isStructId ? ssm.findFileForPDBId(id) : id; + addPAEToStructure(ssm, structFilename, pae, label); + } + + } + else + { + // attach to sequence?! + try + { + if (!importPaeJSONAsContactMatrixToSequence(pdbAlignment, paeInput, + index, id, label)) + { + Console.warn("Could not import contact matrix from '" + + pae.getAbsolutePath() + "' to sequence."); + } + } catch (IOException e1) + { + Console.error("Error when importing pAE file '" + + pae.getAbsolutePath() + "'", e1); + } catch (ParseException e2) + { + Console.error("Error when parsing pAE file '" + + pae.getAbsolutePath() + "'", e2); + } + } + + } + + public static void addPAEToStructure(StructureSelectionManager ssm, + String structFilename, File pae, String label) + { + FileInputStream paeInput = null; + try + { + paeInput = new FileInputStream(pae); + } catch (FileNotFoundException e) + { + Console.error( + "Could not find pAE file '" + pae.getAbsolutePath() + "'", e); + return; + } + if (ssm == null) + { + ssm = StructureSelectionManager + .getStructureSelectionManager(Desktop.instance); + } + if (ssm != null) + { + StructureMapping[] smArray = ssm.getMapping(structFilename); - // import PAE as contact matrix - assume this will work if there was a - // model - File pae = File.createTempFile(id, "pae_json"); - String paeURL = getAlphaFoldPaeDownloadUrl(id); - - if (retrievalUrl!=null) { - // manufacture the PAE url from a url like ...-model-vN.cif - paeURL = retrievalUrl.replace("model","predicted_aligned_error").replace(".cif",".json"); + try + { + if (!importPaeJSONAsContactMatrixToStructure(smArray, paeInput, + label)) + { + Console.warn("Could not import contact matrix from '" + + pae.getAbsolutePath() + "' to structure."); + } + } catch (IOException e1) + { + Console.error("Error when importing pAE file '" + + pae.getAbsolutePath() + "'", e1); + } catch (ParseException e2) + { + Console.error("Error when parsing pAE file '" + + pae.getAbsolutePath() + "'", e2); } - Console.debug("Downloading pae from " + paeURL - + " to " + pae.toString() + ""); + } + } - try { - UrlDownloadClient.download(paeURL, pae); - if (!importPaeJSONAsContactMatrix(pdbAlignment, pae)) + /** + * parses the given pAE matrix and adds it to sequence 0 in the given + * alignment + * + * @param pdbAlignment + * @param pae_input + * @return true if there was a pAE matrix added + * @throws ParseException + * @throws IOException + * @throws Exception + */ + public static boolean importPaeJSONAsContactMatrixToSequence( + AlignmentI pdbAlignment, InputStream pae_input, int index, + String seqId, String label) throws IOException, ParseException + { + SequenceI sequence = null; + if (seqId == null) + { + int seqToGet = index > 0 ? index : 0; + sequence = pdbAlignment.getSequenceAt(seqToGet); + } + if (sequence == null) + { + SequenceI[] sequences = pdbAlignment.findSequenceMatch(seqId); + if (sequences == null || sequences.length < 1) { - Console.warn("Couln't import contact matrix from " + paeURL - + " (stored in " + pae.toString() + ")"); + Console.warn("Could not find sequence with id '" + seqId + + "' to attach pAE matrix to. Ignoring matrix."); + return false; } - } catch (Exception pae_ex) { - Console.debug("Couldn't download PAE",pae_ex); + else + { + sequence = sequences[0]; // just use the first sequence with this seqId } + } + if (sequence == null) + { + return false; + } + return importPaeJSONAsContactMatrixToSequence(pdbAlignment, pae_input, + sequence, label); + } - } catch (Exception ex) // Problem parsing PDB file + public static boolean importPaeJSONAsContactMatrixToSequence( + AlignmentI pdbAlignment, InputStream pae_input, + SequenceI sequence, String label) + throws IOException, ParseException + { + JSONObject paeDict = parseJSONtoPAEContactMatrix(pae_input); + if (paeDict == null) { - stopQuery(); - throw (ex); + Console.debug("JSON file did not parse properly."); + return false; } - return pdbAlignment; + ContactMatrixI matrix = new PAEContactMatrix(sequence, + (Map) paeDict); + + AlignmentAnnotation cmannot = sequence.addContactList(matrix); + if (label != null) + cmannot.label = label; + pdbAlignment.addAnnotation(cmannot); + + return true; + } + + public static JSONObject parseJSONtoPAEContactMatrix( + InputStream pae_input) throws IOException, ParseException + { + Object paeJson = Platform.parseJSON(pae_input); + JSONObject paeDict = null; + if (paeJson instanceof JSONObject) + { + paeDict = (JSONObject) paeJson; + } + else if (paeJson instanceof JSONArray) + { + JSONArray jsonArray = (JSONArray) paeJson; + if (jsonArray.size() > 0) + paeDict = (JSONObject) jsonArray.get(0); + } + + return paeDict; } - private boolean importPaeJSONAsContactMatrix(AlignmentI pdbAlignment, - File pae) throws Exception + // ###### TEST THIS + public static boolean importPaeJSONAsContactMatrixToStructure( + StructureMapping[] smArray, InputStream paeInput, String label) + throws IOException, ParseException { - FileInputStream pae_input = new FileInputStream(pae); + boolean someDone = false; + for (StructureMapping sm : smArray) + { + boolean thisDone = importPaeJSONAsContactMatrixToStructure(sm, + paeInput, label); + someDone |= thisDone; + } + return someDone; + } - List pae_obj = (List) Platform - .parseJSON(pae_input); + public static boolean importPaeJSONAsContactMatrixToStructure( + StructureMapping sm, InputStream paeInput, String label) + throws IOException, ParseException + { + JSONObject pae_obj = parseJSONtoPAEContactMatrix(paeInput); if (pae_obj == null) { + Console.debug("JSON file did not parse properly."); return false; } - ContactMatrixI matrix = new PAEContactMatrix( - pdbAlignment.getSequenceAt(0), (Map)pae_obj.get(0)); - pdbAlignment.getSequenceAt(0).addAlignmentAnnotation(pdbAlignment.addContactList(matrix)); + SequenceI seq = sm.getSequence(); + ContactMatrixI matrix = new PAEContactMatrix(seq, + (Map) pae_obj); + AlignmentAnnotation cmannot = sm.getSequence().addContactList(matrix); + /* this already happens in Sequence.addContactList() + seq.addAlignmentAnnotation(cmannot); + */ return true; } /** - * general purpose structure importer - designed to yield alignment useful for transfer of annotation to associated sequences + * general purpose structure importer - designed to yield alignment useful for + * transfer of annotation to associated sequences + * * @param alphaFoldCif * @param tmpFile * @param id @@ -255,14 +525,17 @@ public class EBIAlfaFold extends EbiFileRetrievedProxy * @return * @throws Exception */ - public static AlignmentI importDownloadedStructureFromUrl(String alphaFoldCif, - File tmpFile, String id, String chain, String dbSource, String dbVersion) throws Exception + public static AlignmentI importDownloadedStructureFromUrl( + String alphaFoldCif, File tmpFile, String id, String chain, + String dbSource, String dbVersion) throws Exception { String file = tmpFile.getAbsolutePath(); // todo get rid of Type and use FileFormatI instead? FileFormatI fileFormat = FileFormat.MMCif; - AlignmentI pdbAlignment = new FormatAdapter().readFile(tmpFile, - DataSourceType.FILE, fileFormat); + TFType tempfacType = TFType.PLDDT; + AlignmentI pdbAlignment = new FormatAdapter().readFile(tmpFile, file, + DataSourceType.FILE, fileFormat, tempfacType); + if (pdbAlignment != null) { List toremove = new ArrayList(); @@ -275,7 +548,6 @@ public class EBIAlfaFold extends EbiFileRetrievedProxy if (pid.getFile() == file) { chid = pid.getChainCode(); - } } if (chain == null || (chid != null && (chid.equals(chain) @@ -309,15 +581,17 @@ public class EBIAlfaFold extends EbiFileRetrievedProxy // dbentry.setMap() pdbcs.addDBRef(dbentry); // update any feature groups - List allsf = pdbcs.getFeatures().getAllFeatures(); + List allsf = pdbcs.getFeatures() + .getAllFeatures(); List newsf = new ArrayList(); - if (allsf!=null && allsf.size()>0) + if (allsf != null && allsf.size() > 0) { - for (SequenceFeature f:allsf) + for (SequenceFeature f : allsf) { if (file.equals(f.getFeatureGroup())) { - f = new SequenceFeature(f, f.type, f.begin, f.end, id, f.score); + f = new SequenceFeature(f, f.type, f.begin, f.end, id, + f.score); } newsf.add(f); } @@ -396,4 +670,9 @@ public class EBIAlfaFold extends EbiFileRetrievedProxy return new PDBFeatureSettings(); } + // days * 86400000 + private static final long PAE_CACHE_STALE_TIME = 1 * 86400000; + + private static Map paeDownloadCache = new HashMap<>(); + }