diff --git a/download/download.go b/download/download.go index ad1bade..379f148 100644 --- a/download/download.go +++ b/download/download.go @@ -4,10 +4,12 @@ import ( "github.com/evolbioinfo/gotree/tree" ) +// ImageDownloader defines function to download an image type ImageDownloader interface { Download(id string, format int) ([]byte, error) // Downdload a tree image from a server } +// TreeDownloader defines function to download an Tree type TreeDownloader interface { Download(id string) (*tree.Tree, error) // Download a tree from a server } diff --git a/download/panther.go b/download/panther.go new file mode 100644 index 0000000..75044f9 --- /dev/null +++ b/download/panther.go @@ -0,0 +1,166 @@ +package download + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + + "github.com/evolbioinfo/gotree/tree" +) + +// PantherTreeDownloader allows to download trees from Panther +// using a family ID +type PantherTreeDownloader struct { + server string + path string +} + +// NewPantherTreeDownloader initializes a new Panther tree downloader +func NewPantherTreeDownloader() *PantherTreeDownloader { + return &PantherTreeDownloader{ + server: "http://pantherdb.org/", + path: "services/oai/pantherdb/treeinfo", + } +} + +// Download a tree from Panther +func (p *PantherTreeDownloader) Download(id string) (t *tree.Tree, err error) { + geturl := fmt.Sprintf("%s/%s?family=%s", p.server, p.path, id) + var getresponse *http.Response + var responsebody []byte + var answer *PantherAnswer + + if getresponse, err = http.Get(geturl); err != nil { + return + } + defer getresponse.Body.Close() + + if responsebody, err = ioutil.ReadAll(getresponse.Body); err != nil { + return + } + + if err = json.Unmarshal(responsebody, answer); err != nil { + err = fmt.Errorf("%s (%s)", err.Error(), string(responsebody)) + return + } + + if answer.Error != "" { + err = errors.New(string(answer.Error)) + return + } + + if t, err = p.treeFromPantherAnswer(answer); err != nil { + return + } + + return +} + +// PantherAnswer is the root of Panther JSON answer +type PantherAnswer struct { + Search PantherAnswerSearch `json:"search"` + TreeTopology PantherAnswerTreeTopology `json:"tree_topology"` + Error string `json:"error"` + SearchType string `json:"search_type"` +} + +// PantherAnswerSearch defines information on answer search +type PantherAnswerSearch struct { + Product PantherAnswerProduct `json:"product"` + SearchType string `json:"search_type"` + Parameters PantherAnswerParameters `json:"parameters"` +} + +// PantherAnswerProduct defines information version and source of the answer +type PantherAnswerProduct struct { + Source string `json:"source"` + Version float64 `json:"version"` +} + +// PantherAnswerParameters defines information about family +type PantherAnswerParameters struct { + Family string `json:"family"` +} + +// PantherAnswerTreeTopology defines information on node +type PantherAnswerTreeTopology struct { + AnnotationNode PantherAnswerAnnotationNode `json:"annotation_node"` +} + +// PantherAnswerAnnotationNode defines information about the node +type PantherAnswerAnnotationNode struct { + SfID string `json:"sf_id"` + PersistentID string `json:"persistent_id"` + BranchLength float64 `json:"branch_length"` + PropSfID string `json:"prop_sf_id"` + EventType string `json:"event_type"` + Species string `json:"species"` + TreeNodeType string `json:"tree_node_type"` + TaxonomicRange string `json:"taxonomic_range"` + SfName string `json:"sf_name"` + GeneSymbol string `json:"gene_symbol"` + NodeName string `json:"node_name"` + Organism string `json:"organism"` + Children PantherAnswerChildren `json:"children"` +} + +// PantherAnswerChildren defines the children type +type PantherAnswerChildren struct { + TreeNodeType string `json:"tree_node_type"` + TaxonomicRange string `json:"taxonomic_range"` + SfName string `json:"sf_name"` + AnnotationNode []PantherAnswerAnnotationNode `json:"annotation_node"` +} + +func (p *PantherTreeDownloader) treeFromPantherAnswer(answer *PantherAnswer) (t *tree.Tree, err error) { + var nedges, nnodes int = 0, 0 + + t = tree.NewTree() + err = p.annotationNodeToTree(&answer.TreeTopology.AnnotationNode, t, nil, &nedges, &nnodes) + return +} + +func (p *PantherTreeDownloader) annotationNodeToTree(an *PantherAnswerAnnotationNode, t *tree.Tree, parent *tree.Node, nedges, nnodes *int) (err error) { + newNode := t.NewNode() + newNode.SetId(*nnodes) + (*nnodes)++ + if parent == nil { + t.SetRoot(newNode) + } else { + e := t.ConnectNodes(parent, newNode) + e.SetId(*nedges) + (*nedges)++ + if an.BranchLength != 0 { + e.SetLength(an.BranchLength) + } + } + if an.Species != "" { + newNode.SetName(an.Species) + } + + if an.EventType != "" { + newNode.AddComment(an.EventType) + } + + if an.Organism != "" { + newNode.AddComment(an.Organism) + } + + if an.PersistentID != "" { + newNode.AddComment(an.PersistentID) + } + + for _, cl := range an.Children.AnnotationNode { + if err = p.annotationNodeToTree(&cl, t, newNode, nedges, nnodes); err != nil { + return + } + } + + if len(an.Children.AnnotationNode) == 0 && an.GeneSymbol == "" { + err = fmt.Errorf("One tip has no gene symbol") + } + + return +}