Skip to content

Commit

Permalink
Refactor stats output. Add new stats page to web app.
Browse files Browse the repository at this point in the history
  • Loading branch information
aebruno committed Nov 4, 2015
1 parent aa46611 commit 8803617
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 47 deletions.
60 changes: 57 additions & 3 deletions cmd/treat/handlers.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
// Copyright 2015 TREAT Authors. All rights reserved.
//
// This file is part of TREAT.
//
//
// TREAT is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// TREAT 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 General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with TREAT. If not, see <http://www.gnu.org/licenses/>.

Expand Down Expand Up @@ -770,3 +770,57 @@ func DbHandler(app *Application) http.Handler {
http.Redirect(w, r, "/", 302)
})
}

func StatsHandler(app *Application) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
db := context.Get(r, "db").(*Database)
if db == nil {
logrus.Error("stats handler: database not found in request context")
errorHandler(app, w, http.StatusInternalServerError)
return
}

countByString := r.FormValue("countby")
countBy := COUNT_FRAG
if "Norm" == countByString {
countBy = COUNT_NORM
} else if "Unique" == countByString {
countBy = COUNT_UNIQUE
} else {
countByString = "Fragments"
}

fields, err := app.NewSearchFields(r.URL, db)
if err != nil {
logrus.Printf("Error parsing get request: %s", err)
errorHandler(app, w, http.StatusInternalServerError)
return
}

tmpl, ok := db.geneTemplates[fields.Gene]
if !ok {
logrus.Printf("Error fetching template for gene: %s", fields.Gene)
errorHandler(app, w, http.StatusInternalServerError)
return
}

stats, err := geneStats(db.storage, fields.Gene, countBy)
if err != nil {
logrus.Printf("Failed to compute stats for gene %s: %s", fields.Gene, err)
errorHandler(app, w, http.StatusInternalServerError)
return
}

vars := map[string]interface{}{
"dbs": app.dbs,
"curdb": db.name,
"stats": stats,
"Fields": fields,
"Template": tmpl,
"Counts": []string{"Fragments", "Norm", "Unique"},
"Countby": countByString,
"Genes": db.genes}

renderTemplate(app, "stats.html", w, vars)
})
}
18 changes: 10 additions & 8 deletions cmd/treat/main.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
// Copyright 2015 TREAT Authors. All rights reserved.
//
// This file is part of TREAT.
//
//
// TREAT is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// TREAT 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 General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with TREAT. If not, see <http://www.gnu.org/licenses/>.

Expand All @@ -28,7 +28,7 @@ import (
func main() {
app := cli.NewApp()
app.Name = "treat"
app.Copyright = `Copyright 2015 TREAT Authors.
app.Copyright = `Copyright 2015 TREAT Authors.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand All @@ -43,9 +43,9 @@ func main() {
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.`
app.Authors = []cli.Author{
{Name: "Andrew E. Bruno", Email: "[email protected]"},
{Name: "Rachel Simpson", Email: "[email protected]"},
{Name: "Laurie Read", Email: "[email protected]"}}
{Name: "Andrew E. Bruno", Email: "[email protected]"},
{Name: "Rachel Simpson", Email: "[email protected]"},
{Name: "Laurie Read", Email: "[email protected]"}}
app.Usage = "Trypanosome RNA Editing Alignment Tool"
app.Version = "0.0.1"
app.Flags = []cli.Flag{
Expand Down Expand Up @@ -163,9 +163,11 @@ func main() {
Usage: "Print database stats",
Flags: []cli.Flag{
&cli.StringFlag{Name: "gene, g", Usage: "Filter by gene"},
&cli.BoolFlag{Name: "unique, u", Usage: "Use unique fragment counts only"},
&cli.BoolFlag{Name: "norm, n", Usage: "Use normalized fragment counts only"},
},
Action: func(c *cli.Context) {
Stats(c.GlobalString("db"), c.String("gene"))
ShowStats(c.GlobalString("db"), c.String("gene"), c.Bool("unique"), c.Bool("norm"))
},
},
{
Expand Down
10 changes: 6 additions & 4 deletions cmd/treat/server.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
// Copyright 2015 TREAT Authors. All rights reserved.
//
// This file is part of TREAT.
//
//
// TREAT is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// TREAT 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 General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with TREAT. If not, see <http://www.gnu.org/licenses/>.

Expand Down Expand Up @@ -129,6 +129,7 @@ func NewApplication(dbpath, tmpldir string, enableCache bool) (*Application, err
funcMap := template.FuncMap{
"increment": incrementFunc,
"decrement": decrementFunc,
"percent": percent,
"round": roundFunc,
"juncseq": juncseqFunc,
"pctSearch": pctSearchFunc,
Expand Down Expand Up @@ -291,6 +292,7 @@ func (a *Application) router() *mux.Router {
router.Path("/heat").Handler(HeatHandler(a)).Methods("GET")
router.Path("/search").Handler(SearchHandler(a)).Methods("GET")
router.Path("/show").Handler(ShowHandler(a)).Methods("GET")
router.Path("/stats").Handler(StatsHandler(a)).Methods("GET")
router.Path("/db").Handler(DbHandler(a)).Methods("GET")
router.Path("/tmpl-report").Handler(TemplateSummaryHandler(a)).Methods("GET")

Expand Down Expand Up @@ -471,7 +473,7 @@ func decrementFunc(x int) int {
}

func roundFunc(val float64) string {
return fmt.Sprintf("%.4f", val)
return fmt.Sprintf("%.2f", val)
}

func pctSearchFunc(a *treat.Alignment, totals map[string]float64) string {
Expand Down
146 changes: 120 additions & 26 deletions cmd/treat/stats.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
// Copyright 2015 TREAT Authors. All rights reserved.
//
// This file is part of TREAT.
//
//
// TREAT is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// TREAT 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 General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with TREAT. If not, see <http://www.gnu.org/licenses/>.

Expand All @@ -25,15 +25,53 @@ import (
"github.com/ubccr/treat"
)

const (
COUNT_UNIQUE = iota
COUNT_FRAG
COUNT_NORM
)

type Stats struct {
Total int
Std int
NonStd int
Indels int
Snps int
SingleMismatch int
DoubleMismatch int
}

type SampleStats struct {
Stats
}

type GeneStats struct {
Stats
Name string
SampleMap map[string]*SampleStats
}

func percent(x, y int) float64 {
if y == 0 {
return float64(0)
}

return (float64(x) / float64(y)) * float64(100)
}

func Stats(dbpath, gene string) {
func ShowStats(dbpath, gene string, unique bool, norm bool) {
s, err := NewStorage(dbpath)
if err != nil {
logrus.Fatal(err)
}

countby := COUNT_FRAG
if unique {
countby = COUNT_UNIQUE
} else if norm {
countby = COUNT_NORM
}

fmt.Printf("db path: %s\n", dbpath)
fmt.Printf("version: %.1f\n\n", s.version)

Expand All @@ -47,40 +85,96 @@ func Stats(dbpath, gene string) {
continue
}

grandTotal := 0
nonMutant := 0
countMap := make(map[string]int)
totalMap := make(map[string]int)
err = s.Search(&SearchFields{Gene: g, All: true, EditStop: -1, JuncLen: -1, JuncEnd: -1}, func(key *treat.AlignmentKey, a *treat.Alignment) {
if a.HasMutation == uint8(0) {
countMap[key.Sample]++
nonMutant++
}

totalMap[key.Sample]++
grandTotal++
})

stats, err := geneStats(s, g, countby)
if err != nil {
logrus.Fatal(err)
}

fmt.Println(strings.Repeat("=", 80))
fmt.Println(g)
fmt.Println(stats.Name)
fmt.Println(strings.Repeat("=", 80))
fmt.Printf("%20s%11d\n", "Total Alignments:", grandTotal)
fmt.Printf("%20s%11d\n", "Non-Mutant:", nonMutant)
fmt.Printf("%20s%11d\n", "Mutant:", grandTotal-nonMutant)
fmt.Printf("%20s%11d\n", "Edit Stop:", tmpl.EditStop)
fmt.Printf("%20s%11d\n", "Total Alignments:", stats.Total)
fmt.Printf("%20s%11d\n", "Standard:", stats.Std)
fmt.Printf("%20s%11d\n", "Non-Standard:", stats.NonStd)
fmt.Printf("%20s%11d\n", "1-Mismatch:", stats.SingleMismatch)
fmt.Printf("%20s%11d\n", "2-Mismatch:", stats.DoubleMismatch)
fmt.Printf("%20s%11d\n", ">3-Mismatch:", stats.Snps)
fmt.Printf("%20s%11d\n", "Indels:", stats.Indels)
fmt.Printf("%20s%11d\n", "Template Edit Stop:", tmpl.EditStop)
fmt.Printf("%20s%11s\n", "Edit Base:", string(tmpl.EditBase))
fmt.Printf("%20s%11d\n", "Alt Templates:", len(tmpl.AltRegion))
fmt.Println(strings.Repeat("-", 80))
fmt.Printf("%-25s%11s%15s%8s%11s%8s\n", "Sample", "Total", "Non-Mutant", "%", "Mutant", "%")
fmt.Printf("%-15s%9s%9s%5s%9s%5s%9s%5s%9s%5s\n", "Sample", "Total", "Std", "%", "Non-Std", "%", "1MM", "%", "2MM", "%")
fmt.Println(strings.Repeat("-", 80))
for sample, total := range totalMap {
fmt.Printf("%-25s%11d%15d%8.2f%11d%8.2f\n", sample, total, countMap[sample], percent(countMap[sample], total), total-countMap[sample], percent(total-countMap[sample], total))
for sample, rec := range stats.SampleMap {
name := sample
if len(sample) > 12 {
name = name[0:12] + ".."
}
fmt.Printf("%-15s%9d%9d%5.1f%9d%5.1f%9d%5.1f%9d%5.1f\n",
name,
rec.Total,
rec.Std,
percent(rec.Std, rec.Total),
rec.NonStd,
percent(rec.NonStd, rec.Total),
rec.SingleMismatch,
percent(rec.SingleMismatch, rec.Total),
rec.DoubleMismatch,
percent(rec.DoubleMismatch, rec.Total))
}

fmt.Println()
}
}

func geneStats(s *Storage, gene string, countby int) (*GeneStats, error) {
gstat := &GeneStats{Name: gene}
gstat.SampleMap = make(map[string]*SampleStats)

err := s.Search(&SearchFields{Gene: gene, All: true, EditStop: -1, JuncLen: -1, JuncEnd: -1}, func(key *treat.AlignmentKey, a *treat.Alignment) {
if _, ok := gstat.SampleMap[key.Sample]; !ok {
gstat.SampleMap[key.Sample] = &SampleStats{}
}

var readCount int
if countby == COUNT_NORM {
readCount = int(a.Norm)
} else if countby == COUNT_FRAG {
readCount = int(a.ReadCount)
} else {
readCount = 1
}

if a.HasMutation == uint8(0) {
gstat.SampleMap[key.Sample].Std += readCount
gstat.Std += readCount
} else {
gstat.SampleMap[key.Sample].NonStd += readCount
gstat.NonStd += readCount
}

if a.Indel == uint8(1) {
gstat.SampleMap[key.Sample].Indels += readCount
gstat.Indels += readCount
} else if a.Mismatches == uint8(1) {
gstat.SampleMap[key.Sample].SingleMismatch += readCount
gstat.SingleMismatch += readCount
} else if a.Mismatches == uint8(2) {
gstat.SampleMap[key.Sample].DoubleMismatch += readCount
gstat.DoubleMismatch += readCount
} else if a.Mismatches > uint8(2) {
gstat.SampleMap[key.Sample].Snps += readCount
gstat.Snps += readCount
}

gstat.SampleMap[key.Sample].Total += readCount
gstat.Total += readCount
})

if err != nil {
return nil, err
}

return gstat, nil
}
Loading

0 comments on commit 8803617

Please sign in to comment.