diff --git a/cmd/thyme/main.go b/cmd/thyme/main.go index 9ea4c9d..9a8c19b 100644 --- a/cmd/thyme/main.go +++ b/cmd/thyme/main.go @@ -4,8 +4,10 @@ import ( "encoding/json" "fmt" "log" + "net/http" "os" "runtime" + "time" "github.com/jessevdk/go-flags" "github.com/sourcegraph/thyme" @@ -23,8 +25,22 @@ func init() { if _, err := CLI.AddCommand("dep", "", "external dependencies that need to be installed", &depCmd); err != nil { log.Fatal(err) } + if _, err := CLI.AddCommand("watch", "", "record current windows on an interval", &watchCmd); err != nil { + log.Fatal(err) + } + if _, err := CLI.AddGroup("shortcuts", "shortcuts for frequently used commands", &shortcutOptions); err != nil { + log.Fatal(err) + } + CLI.SubcommandsOptional = true } +type ShortcutOptions struct { + Watch bool `long:"watch" short:"w" description:"sets up default watch"` + Show bool `long:"show" short:"s" description:"sets up default show"` +} + +var shortcutOptions ShortcutOptions + // TrackCmd is the subcommand that tracks application usage. type TrackCmd struct { Out string `long:"out" short:"o" description:"output file"` @@ -80,16 +96,74 @@ func (c *TrackCmd) Execute(args []string) error { return nil } +// WatchCmd is the subcommand that tracks application usage on an interval +type WatchCmd struct { + Out string `long:"out" short:"o" description:"output file" default:"thyme.json"` + Interval uint32 `long:"interval" short:"n" description:"seconds between records" default:"30"` +} + +var watchCmd WatchCmd + +func (c *WatchCmd) Execute(args []string) error { + trackCmd.Out = c.Out + err := trackCmd.Execute(args) + if err != nil { + return err + } + for range time.Tick(time.Duration(c.Interval) * time.Second) { + err := trackCmd.Execute(args) + if err != nil { + return err + } + } + return nil +} + // ShowCmd is the subcommand that reads the data emitted by the track // subcommand and displays the data to the user. type ShowCmd struct { - In string `long:"in" short:"i" description:"input file"` - What string `long:"what" short:"w" description:"what to show {list,stats}" default:"list"` + In string `long:"in" short:"i" description:"input file"` + What string `long:"what" short:"w" description:"what to show {list,stats}" default:"list"` + Serve bool `long:"serve" short:"s" description:"serves content via http"` } var showCmd ShowCmd func (c *ShowCmd) Execute(args []string) error { + if c.Serve { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + f, err := os.Open(c.In) + if err != nil { + fmt.Fprint(w, err) + return + } + defer f.Close() + + var stream thyme.Stream + if err := json.NewDecoder(f).Decode(&stream); err != nil { + fmt.Fprint(w, err) + return + } + thyme.Stats(w, &stream) + }) + http.HandleFunc("/list", func(w http.ResponseWriter, r *http.Request) { + f, err := os.Open(c.In) + if err != nil { + fmt.Fprint(w, err) + return + } + defer f.Close() + + var stream thyme.Stream + if err := json.NewDecoder(f).Decode(&stream); err != nil { + fmt.Fprint(w, err) + return + } + thyme.List(w, &stream) + }) + fmt.Println("Starting server...") + http.ListenAndServe(":6090", nil) + } if c.In == "" { var snap thyme.Snapshot if err := json.NewDecoder(os.Stdin).Decode(&snap); err != nil { @@ -111,13 +185,13 @@ func (c *ShowCmd) Execute(args []string) error { } switch c.What { case "stats": - if err := thyme.Stats(&stream); err != nil { + if err := thyme.Stats(os.Stdout, &stream); err != nil { return err } case "list": fallthrough default: - thyme.List(&stream) + thyme.List(os.Stdout, &stream) } } return nil @@ -142,11 +216,21 @@ func main() { if err != nil { return err } + showOpt := CLI.FindOptionByLongName("show") + if showOpt != nil { + showCmd.In = watchCmd.Out + showCmd.What = "show" + showCmd.Serve = true + go showCmd.Execute([]string{}) + } + opt := CLI.FindOptionByLongName("watch") + if opt != nil { + watchCmd.Execute([]string{}) + } return nil } if err := run(); err != nil { - log.Print(err) os.Exit(1) } } diff --git a/list.go b/list.go index b2eae5c..e10268d 100644 --- a/list.go +++ b/list.go @@ -1,7 +1,10 @@ package thyme -import "fmt" +import ( + "fmt" + "io" +) -func List(stream *Stream) { - fmt.Printf("%s", stream.Print()) +func List(w io.Writer, stream *Stream) { + fmt.Fprintf(w, "%s", stream.Print()) } diff --git a/show.go b/show.go index eacb513..a31f603 100644 --- a/show.go +++ b/show.go @@ -2,7 +2,7 @@ package thyme import ( "fmt" - "os" + "io" "sort" "strconv" "text/template" @@ -16,12 +16,12 @@ const maxNumberOfBars = 30 // 1. A timeline of applications active, visible, and open // 2. A timeline of windows active, visible, and open // 3. A barchart of applications most often active, visible, and open -func Stats(stream *Stream) error { +func Stats(w io.Writer, stream *Stream) error { tlFine := NewTimeline(stream, func(w *Window) string { return w.Name }) tlCoarse := NewTimeline(stream, appID) agg := NewAggTime(stream, appID) - if err := statsTmpl.Execute(os.Stdout, &statsPage{ + if err := statsTmpl.Execute(w, &statsPage{ Fine: tlFine, Coarse: tlCoarse, Agg: agg,