/* * This is eMonocot, a global online biodiversity information resource. * * Copyright © 2011–2015 The Board of Trustees of the Royal Botanic Gardens, Kew and The University of Oxford * * eMonocot is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * eMonocot is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * The complete text of the GNU Affero General Public License is in the source repository as the file * ‘COPYING’. It is also available from <http://www.gnu.org/licenses/>. */ package org.emonocot.job.phylo; import java.io.File; import java.util.ArrayList; import java.util.List; import org.emonocot.api.PhylogeneticTreeService; import org.emonocot.api.TaxonService; import org.emonocot.api.match.Match; import org.emonocot.api.match.taxon.TaxonMatcher; import org.emonocot.harvest.common.AbstractRecordAnnotator; import org.emonocot.model.PhylogeneticTree; import org.emonocot.model.Taxon; import org.emonocot.model.constants.AnnotationCode; import org.emonocot.model.constants.AnnotationType; import org.emonocot.model.constants.RecordType; import org.emonocot.persistence.hibernate.SolrIndexingListener; import org.forester.io.parsers.PhylogenyParser; import org.forester.io.parsers.util.ParserUtils; import org.forester.io.writers.PhylogenyWriter; import org.forester.phylogeny.Phylogeny; import org.forester.phylogeny.PhylogenyMethods; import org.forester.phylogeny.PhylogenyNode; import org.forester.phylogeny.data.Annotation; import org.forester.phylogeny.data.PhylogenyDataUtil; import org.forester.phylogeny.data.Uri; import org.gbif.ecat.parser.UnparsableException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; /** * * @author ben * */ public class PhylogeneticTreeTransformingTasklet extends AbstractRecordAnnotator implements Tasklet { Logger logger = LoggerFactory.getLogger(PhylogeneticTreeTransformingTasklet.class); private String treeIdentifier; private Resource inputFile; private String baseUri; private TaxonMatcher taxonMatcher; private String rootTaxonIdentifier; private String phylogenyTitle; private String phylogenyCreator; private String phylogenyDescription; private String phylogenyRights; private String phylogenyRightsHolder; private String phylogenyLicense; private PhylogeneticTreeService phylogeneticTreeService; private TaxonService taxonService; private SolrIndexingListener solrIndexingListener; public void setTreeIdentifier(String treeIdentifier) { this.treeIdentifier = treeIdentifier; } public void setRootTaxonIdentifier(String rootTaxonIdentifier) { this.rootTaxonIdentifier = rootTaxonIdentifier; } public void setPhylogenyCreator(String phylogenyCreator) { this.phylogenyCreator = phylogenyCreator; } public void setPhylogenyDescription(String phylogenyDescription) { this.phylogenyDescription = phylogenyDescription; } public void setPhylogenyRights(String phylogenyRights) { this.phylogenyRights = phylogenyRights; } public void setPhylogenyRightsHolder(String phylogenyRightsHolder) { this.phylogenyRightsHolder = phylogenyRightsHolder; } public void setPhylogenyLicense(String phylogenyLicense) { this.phylogenyLicense = phylogenyLicense; } public void setInputFile(Resource inputFile) { this.inputFile = inputFile; } public void setBaseUri(String baseUri) { this.baseUri = baseUri; } public void setPhylogenyTitle(String phylogenyTitle) { this.phylogenyTitle = phylogenyTitle; } @Autowired public void setTaxonMatcher(TaxonMatcher taxonMatcher) { this.taxonMatcher = taxonMatcher; } @Autowired public void setPhylogeneticTreeService(PhylogeneticTreeService phylogeneticTreeService) { this.phylogeneticTreeService = phylogeneticTreeService; } @Autowired public void setTaxonService(TaxonService taxonService) { this.taxonService = taxonService; } @Autowired public void setSolrIndexingListener(SolrIndexingListener solrIndexingListener) { this.solrIndexingListener = solrIndexingListener; } @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { File treefile = inputFile.getFile(); PhylogenyParser parser = ParserUtils.createParserDependingOnFileType(treefile, true); Phylogeny[] phylogenies = PhylogenyMethods.readPhylogenies(parser, treefile); Phylogeny phylogeny = phylogenies[0]; PhylogenyWriter phylogenyWriter = PhylogenyWriter.createPhylogenyWriter(); phylogenyWriter.setIndentPhyloxml(false); PhylogenyNode node = phylogeny.getRoot(); PhylogeneticTree phylogeneticTree = phylogeneticTreeService.find(treeIdentifier); if(phylogeneticTree == null) { phylogeneticTree = new PhylogeneticTree(); phylogeneticTree.setAuthority(getSource()); phylogeneticTree.setIdentifier(treeIdentifier); org.emonocot.model.Annotation annotation = new org.emonocot.model.Annotation(); annotation.setJobId(stepExecution.getJobExecutionId()); annotation.setAnnotatedObj(phylogeneticTree); annotation.setRecordType(RecordType.PhylogeneticTree); annotation.setCode(AnnotationCode.Create); annotation.setAuthority(getSource()); annotation.setType(AnnotationType.Info); phylogeneticTree.getAnnotations().add(annotation); } else { org.emonocot.model.Annotation annotation = new org.emonocot.model.Annotation(); annotation.setJobId(stepExecution.getJobExecutionId()); annotation.setAnnotatedObj(phylogeneticTree); annotation.setRecordType(RecordType.PhylogeneticTree); annotation.setCode(AnnotationCode.Update); annotation.setType(AnnotationType.Info); annotation.setAuthority(getSource()); phylogeneticTree.getAnnotations().add(annotation); phylogeneticTree.getLeaves().clear(); } addTaxonLinks(node,phylogeneticTree); boolean hasBranchLengths = addBranchLengths(node); StringBuffer stringBuffer = phylogenyWriter.toPhyloXML(phylogeny, 1); phylogeneticTree.setNumberOfExternalNodes(new Long(phylogeny.getNumberOfExternalNodes())); phylogeneticTree.setHasBranchLengths(hasBranchLengths); if(phylogeneticTree.getTitle() == null || phylogeneticTree.getTitle().isEmpty()) { phylogeneticTree.setTitle(phylogeny.getName()); } if(phylogeneticTree.getDescription() == null || phylogeneticTree.getDescription().isEmpty()) { phylogeneticTree.setDescription(phylogeny.getDescription()); } if(phylogenyTitle != null && !phylogenyTitle.isEmpty()) { phylogeneticTree.setTitle(phylogenyTitle); } if(phylogenyDescription != null && !phylogenyDescription.isEmpty()) { phylogeneticTree.setDescription(phylogenyDescription); } if(phylogenyCreator != null && !phylogenyCreator.isEmpty()) { phylogeneticTree.setCreator(phylogenyCreator); } if(phylogenyRights != null && !phylogenyRights.isEmpty()) { phylogeneticTree.setRights(phylogenyRights); } if(phylogenyLicense != null && !phylogenyLicense.isEmpty()) { phylogeneticTree.setLicense(phylogenyLicense); } if(phylogenyRightsHolder != null && !phylogenyRightsHolder.isEmpty()) { phylogeneticTree.setRightsHolder(phylogenyRightsHolder); } if(rootTaxonIdentifier != null && !rootTaxonIdentifier.isEmpty()) { if(rootTaxonIdentifier.indexOf(",") == -1) { Taxon rootTaxon = taxonService.find(rootTaxonIdentifier); if(rootTaxon != null) { phylogeneticTree.getTaxa().add(rootTaxon); } } else { for(String identifier : rootTaxonIdentifier.split(",")) { Taxon rootTaxon = taxonService.find(identifier); if(rootTaxon != null) { phylogeneticTree.getTaxa().add(rootTaxon); } } } } phylogeneticTree.setPhylogeny(stringBuffer.toString().replaceAll("\n", "")); phylogeneticTreeService.saveOrUpdate(phylogeneticTree); solrIndexingListener.indexObject(phylogeneticTree); logger.info(stringBuffer.toString()); return RepeatStatus.FINISHED; } private boolean addBranchLengths(PhylogenyNode node) { boolean hasBranchLengths = true; if(node.isRoot() || node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT) { // do nothing } else { node.setDistanceToParent(1.0D); hasBranchLengths = false; } for(PhylogenyNode descendant : node.getDescendants()) { if(!addBranchLengths(descendant)) { hasBranchLengths = false; } } return hasBranchLengths; } private void addTaxonLinks(PhylogenyNode node, PhylogeneticTree phylogeneticTree) { Taxon taxon = matchTaxonName(node.getName()); if (taxon != null) { Annotation annotation = new Annotation(); annotation.setDesc(taxon.getScientificName()); List<Uri> uris = new ArrayList<Uri>(); uris.add(new Uri(baseUri + "/taxon/" + taxon.getIdentifier(), null, null)); annotation.setUris(uris); node.getNodeData().addAnnotation(annotation); if(node.isExternal()) { phylogeneticTree.getLeaves().add(taxon); } } for(PhylogenyNode descendant : node.getDescendants()) { addTaxonLinks(descendant,phylogeneticTree); } } private Taxon matchTaxonName(String taxonName) { if (taxonName == null || taxonName.isEmpty()) { return null; } else { try { List<Match<Taxon>> matches = taxonMatcher.match(taxonName); Taxon object = null; AnnotationType annotationType = null; AnnotationCode code = null; String text = null; if (matches.size() == 0) { annotationType = AnnotationType.Error; code = AnnotationCode.Absent; text = "No matches found for taxonomic name " + taxonName; } else if (matches.size() > 1) { annotationType = AnnotationType.Error; code = AnnotationCode.BadRecord; text = matches.size() + " matches found for taxonomic name " + taxonName; } else { annotationType = AnnotationType.Info; code = AnnotationCode.Present; object = matches.get(0).getInternal(); text = object.getIdentifier() + " matches taxonomic name " + taxonName; } org.emonocot.model.Annotation annotation = new org.emonocot.model.Annotation(); annotation.setJobId(stepExecution.getJobExecutionId()); annotation.setAnnotatedObj(object); annotation.setRecordType(RecordType.Taxon); annotation.setCode(code); annotation.setType(annotationType); annotation.setValue(taxonName); annotation.setText(text); super.annotate(annotation); return object; } catch (UnparsableException e) { org.emonocot.model.Annotation annotation = new org.emonocot.model.Annotation(); annotation.setJobId(stepExecution.getJobExecutionId()); annotation.setRecordType(RecordType.Taxon); annotation.setCode(AnnotationCode.BadField); annotation.setType(AnnotationType.Error); annotation.setValue(taxonName); annotation.setText("Could not parse taxon name " + taxonName); super.annotate(annotation); return null; } } } }