Skip to content
This repository was archived by the owner on May 3, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 89 additions & 5 deletions cmd/thyme/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"runtime"
"time"

"github.com/jessevdk/go-flags"
"github.com/sourcegraph/thyme"
Expand All @@ -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"`
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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)
}
}
Expand Down
9 changes: 6 additions & 3 deletions list.go
Original file line number Diff line number Diff line change
@@ -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())
}
6 changes: 3 additions & 3 deletions show.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package thyme

import (
"fmt"
"os"
"io"
"sort"
"strconv"
"text/template"
Expand All @@ -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,
Expand Down