Code

Add a separate package for handling graphs and plots.
[sysdb/webui.git] / server / graph.go
index 9406f760158029519ecf32edb07f7993c6dfc632..9c679bc5a0386a9ed48a4900a8a5c7d9a7f7af27 100644 (file)
@@ -29,71 +29,57 @@ package server
 
 import (
        "bytes"
-       "errors"
        "fmt"
        "io"
        "net/http"
        "time"
 
-       "code.google.com/p/plotinum/plot"
-       "code.google.com/p/plotinum/plotter"
-       "code.google.com/p/plotinum/plotutil"
-       "code.google.com/p/plotinum/vg"
-       "code.google.com/p/plotinum/vg/vgsvg"
-       "github.com/sysdb/go/proto"
-       "github.com/sysdb/go/sysdb"
+       "github.com/gonum/plot/vg"
+       "github.com/sysdb/webui/graph"
 )
 
-func (s *Server) graph(w http.ResponseWriter, req request) {
-       if len(req.args) != 2 {
-               s.internal(w, fmt.Errorf("Missing host/metric information"))
-       }
+var urldate = "20060102150405"
 
-       host := proto.EscapeString(req.args[0])
-       metric := proto.EscapeString(req.args[1])
-       res, err := s.query(fmt.Sprintf("TIMESERIES %s.%s", host, metric))
-       if err != nil {
-               s.internal(w, fmt.Errorf("Failed to retrieve graph data: %v", err))
+func (s *Server) graph(w http.ResponseWriter, req request) {
+       if len(req.args) < 2 || 4 < len(req.args) {
+               s.badrequest(w, fmt.Errorf("Missing host/metric information"))
                return
        }
 
-       ts, ok := res.(sysdb.Timeseries)
-       if !ok {
-               s.internal(w, errors.New("TIMESERIES did not return a time-series"))
-               return
+       end := time.Now()
+       start := end.Add(-24 * time.Hour)
+       var err error
+       if len(req.args) > 2 {
+               if start, err = time.Parse(urldate, req.args[2]); err != nil {
+                       s.badrequest(w, fmt.Errorf("Invalid start time: %v", err))
+                       return
+               }
        }
-
-       p, err := plot.New()
+       if len(req.args) > 3 {
+               if end, err = time.Parse(urldate, req.args[3]); err != nil {
+                       s.badrequest(w, fmt.Errorf("Invalid start time: %v", err))
+                       return
+               }
+       }
+       g := &graph.Graph{
+               Start:   start,
+               End:     end,
+               Metrics: [][2]string{{req.args[0], req.args[1]}},
+       }
+       p, err := g.Plot(s.c)
        if err != nil {
-               s.internal(w, fmt.Errorf("Failed to create plot: %v", err))
+               s.internal(w, err)
                return
        }
-       p.Add(plotter.NewGrid())
-       p.X.Tick.Marker = dateTicks
 
-       var i int
-       for name, data := range ts.Data {
-               pts := make(plotter.XYs, len(data))
-               for i, p := range data {
-                       pts[i].X = float64(time.Time(p.Timestamp).UnixNano())
-                       pts[i].Y = p.Value
-               }
-               l, err := plotter.NewLine(pts)
-               if err != nil {
-                       s.internal(w, fmt.Errorf("Failed to create line plotter: %v", err))
-                       return
-               }
-               l.LineStyle.Color = plotutil.DarkColors[i%len(plotutil.DarkColors)]
-               p.Add(l)
-               p.Legend.Add(name, l)
-               i++
+       pw, err := p.WriterTo(vg.Length(500), vg.Length(200), "svg")
+       if err != nil {
+               s.internal(w, fmt.Errorf("Failed to write plot: %v", err))
+               return
        }
 
-       c := vgsvg.New(vg.Length(500), vg.Length(200))
-       p.Draw(plot.MakeDrawArea(c))
-
        var buf bytes.Buffer
-       if _, err := c.WriteTo(&buf); err != nil {
+       if _, err := pw.WriteTo(&buf); err != nil {
                s.internal(w, fmt.Errorf("Failed to write plot: %v", err))
                return
        }
@@ -102,18 +88,4 @@ func (s *Server) graph(w http.ResponseWriter, req request) {
        io.Copy(w, &buf)
 }
 
-func dateTicks(min, max float64) []plot.Tick {
-       // TODO: this is surely not the best we can do
-       // but it'll distribute ticks evenly.
-       ticks := plot.DefaultTicks(min, max)
-       for i, t := range ticks {
-               if t.Label == "" {
-                       // Skip minor ticks.
-                       continue
-               }
-               ticks[i].Label = time.Unix(0, int64(t.Value)).Format(time.RFC822)
-       }
-       return ticks
-}
-
 // vim: set tw=78 sw=4 sw=4 noexpandtab :