From b057ffe8c7366ec417d419317313a00750b65391 Mon Sep 17 00:00:00 2001 From: Cory Mainwaring Date: Fri, 19 Aug 2016 11:34:18 -1000 Subject: [PATCH 1/3] Add watch command watch command runs the track command once per interval. Current defaults are 30 seconds and output file of "thyme.json". --- cmd/thyme/main.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/cmd/thyme/main.go b/cmd/thyme/main.go index 9ea4c9d..3a99427 100644 --- a/cmd/thyme/main.go +++ b/cmd/thyme/main.go @@ -6,6 +6,7 @@ import ( "log" "os" "runtime" + "time" "github.com/jessevdk/go-flags" "github.com/sourcegraph/thyme" @@ -23,6 +24,9 @@ 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) + } } // TrackCmd is the subcommand that tracks application usage. @@ -80,6 +84,25 @@ 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"` + Seconds uint32 `long:"seconds" short:"s" description:"seconds between records" default:"30"` +} + +var watchCmd WatchCmd + +func (c *WatchCmd) Execute(args []string) error { + trackCmd.Out = c.Out + for range time.Tick(time.Duration(c.Seconds) * 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 { From 88d0aee230ae48c439e51ea038729d0bc7b91e87 Mon Sep 17 00:00:00 2001 From: Cory Mainwaring Date: Fri, 19 Aug 2016 11:45:41 -1000 Subject: [PATCH 2/3] Sync up terminology with Linux watch command --- cmd/thyme/main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/thyme/main.go b/cmd/thyme/main.go index 3a99427..b1a4d94 100644 --- a/cmd/thyme/main.go +++ b/cmd/thyme/main.go @@ -86,15 +86,15 @@ func (c *TrackCmd) Execute(args []string) error { // 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"` - Seconds uint32 `long:"seconds" short:"s" description:"seconds between records" default:"30"` + 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 - for range time.Tick(time.Duration(c.Seconds) * time.Second) { + for range time.Tick(time.Duration(c.Interval) * time.Second) { err := trackCmd.Execute(args) if err != nil { return err From 0f522385fe82703a581727f9fb76573a94762633 Mon Sep 17 00:00:00 2001 From: Cory Mainwaring Date: Fri, 19 Aug 2016 12:59:36 -1000 Subject: [PATCH 3/3] Add extra functionality for easy-to-use interface Presents option to serve to a local http server for the show command, and allows users to use top-level switches to set up an automatic 30s track with a local server. --- cmd/thyme/main.go | 71 +++++++++++++++++++++++++++++++++++++++++++---- list.go | 9 ++++-- show.go | 6 ++-- 3 files changed, 75 insertions(+), 11 deletions(-) diff --git a/cmd/thyme/main.go b/cmd/thyme/main.go index b1a4d94..9a8c19b 100644 --- a/cmd/thyme/main.go +++ b/cmd/thyme/main.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "log" + "net/http" "os" "runtime" "time" @@ -27,8 +28,19 @@ func init() { 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"` @@ -94,6 +106,10 @@ 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 { @@ -106,13 +122,48 @@ func (c *WatchCmd) Execute(args []string) error { // 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 { @@ -134,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 @@ -165,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,