From 56158d2e9374e750d6886a7cbc3abd4caf7991f5 Mon Sep 17 00:00:00 2001 From: Bill Date: Tue, 5 Jun 2018 16:05:06 -0700 Subject: [PATCH 1/5] Built Graph Widget, working with basic sample --- bargraph/widget.go | 95 ++++++++++++++++++++++++ prettyweather/widget.go | 7 +- wtf.go | 2 + wtf/graph_widget.go | 160 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 bargraph/widget.go create mode 100644 wtf/graph_widget.go diff --git a/bargraph/widget.go b/bargraph/widget.go new file mode 100644 index 00000000..51194638 --- /dev/null +++ b/bargraph/widget.go @@ -0,0 +1,95 @@ +package bargraph + +import ( + "fmt" + "math/rand" + "time" + + "github.com/olebedev/config" + "github.com/senorprogrammer/wtf/wtf" +) + +// Config is a pointer to the global config object +var Config *config.Config + +var started = false +var ok = true + +// Widget define wtf widget to register widget later +type Widget struct { + wtf.GraphWidget + + // time interval for send http request + updateInterval int +} + +// NewWidget Make new instance of widget +func NewWidget() *Widget { + widget := Widget{ + GraphWidget: wtf.NewGraphWidget(" Bar Graph", "bargraph", false), + } + + widget.View.SetWrap(true) + widget.View.SetWordWrap(true) + + return &widget +} + +/* -------------------- Exported Functions -------------------- */ + +// MakeGraph - Load the dead drop stats +func MakeGraph(widget *Widget) { + + //this could come from config + const lineCount = 20 + var stats [lineCount][2]int64 + + for i := lineCount - 1; i >= 0; i-- { + + stats[i][1] = time.Now().AddDate(0, 0, i*-1).Unix() * 1000 + stats[i][0] = int64(rand.Intn(120-5) + 5) + + } + + widget.GraphWidget.BuildBars(20, "🌟", stats[:]) + +} + +// Refresh & update after interval time +func (widget *Widget) Refresh() { + + if widget.Disabled() { + return + } + + if started == false { + // this code should run once + go func() { + for { + time.Sleep(time.Duration(widget.updateInterval) * time.Second) + } + }() + + } + + started = true + + widget.UpdateRefreshedAt() + widget.View.Clear() + + if !ok { + widget.View.SetText( + fmt.Sprint("Error!"), + ) + return + } + + display(widget) + +} + +/* -------------------- Unexported Functions -------------------- */ + +func display(widget *Widget) { + MakeGraph(widget) +} diff --git a/prettyweather/widget.go b/prettyweather/widget.go index 6b838c69..5cb61446 100644 --- a/prettyweather/widget.go +++ b/prettyweather/widget.go @@ -18,6 +18,7 @@ type Widget struct { result string unit string city string + view string } func NewWidget() *Widget { @@ -42,8 +43,10 @@ func (widget *Widget) Refresh() { //this method reads the config and calls wttr.in for pretty weather func (widget *Widget) prettyWeather() { client := &http.Client{} - widget.unit, widget.city = Config.UString("wtf.mods.prettyweather.unit", "m"), Config.UString("wtf.mods.prettyweather.city", "") - req, err := http.NewRequest("GET", "https://wttr.in/"+widget.city+"?0"+"?"+widget.unit, nil) + widget.unit = Config.UString("wtf.mods.prettyweather.unit", "m") + widget.city = Config.UString("wtf.mods.prettyweather.city", "") + widget.view = Config.UString("wtf.mods.prettyweather.view", "0") + req, err := http.NewRequest("GET", "https://wttr.in/"+widget.city+"?"+widget.view+"?"+widget.unit, nil) if err != nil { widget.result = fmt.Sprintf("%s", err.Error()) return diff --git a/wtf.go b/wtf.go index 660e458c..a855089a 100644 --- a/wtf.go +++ b/wtf.go @@ -6,6 +6,7 @@ import ( "os" "time" + "github.com/BillKeenan/wtf/bargraph" "github.com/gdamore/tcell" "github.com/olebedev/config" "github.com/radovskyb/watcher" @@ -186,6 +187,7 @@ func makeWidgets(app *tview.Application, pages *tview.Pages) { clocks.NewWidget(), cmdrunner.NewWidget(), cryptolive.NewWidget(), + bargraph.NewWidget(), gcal.NewWidget(), git.NewWidget(app, pages), github.NewWidget(app, pages), diff --git a/wtf/graph_widget.go b/wtf/graph_widget.go new file mode 100644 index 00000000..84b5a4a7 --- /dev/null +++ b/wtf/graph_widget.go @@ -0,0 +1,160 @@ +package wtf + +import ( + "bytes" + "fmt" + "strings" + "time" + + "github.com/rivo/tview" +) + +type GraphWidget struct { + enabled bool + focusable bool + + Name string + RefreshedAt time.Time + RefreshInt int + View *tview.TextView + + Position + + Data [][2]int64 +} + +// NewGraphWidget initialize your fancy new graph +func NewGraphWidget(name string, configKey string, focusable bool) GraphWidget { + widget := GraphWidget{ + enabled: Config.UBool(fmt.Sprintf("wtf.mods.%s.enabled", configKey), false), + focusable: focusable, + + Name: Config.UString(fmt.Sprintf("wtf.mods.%s.title", configKey), name), + RefreshInt: Config.UInt(fmt.Sprintf("wtf.mods.%s.refreshInterval", configKey)), + } + + widget.Position = NewPosition( + Config.UInt(fmt.Sprintf("wtf.mods.%s.position.top", configKey)), + Config.UInt(fmt.Sprintf("wtf.mods.%s.position.left", configKey)), + Config.UInt(fmt.Sprintf("wtf.mods.%s.position.width", configKey)), + Config.UInt(fmt.Sprintf("wtf.mods.%s.position.height", configKey)), + ) + + widget.addView() + + return widget +} + +func (widget *GraphWidget) BorderColor() string { + if widget.Focusable() { + return Config.UString("wtf.colors.border.focusable", "red") + } + + return Config.UString("wtf.colors.border.normal", "gray") +} + +func (widget *GraphWidget) Disabled() bool { + return !widget.Enabled() +} + +func (widget *GraphWidget) Enabled() bool { + return widget.enabled +} + +func (widget *GraphWidget) Focusable() bool { + return widget.enabled && widget.focusable +} + +func (widget *GraphWidget) RefreshInterval() int { + return widget.RefreshInt +} + +func (widget *GraphWidget) TextView() *tview.TextView { + return widget.View +} + +/* -------------------- Unexported Functions -------------------- */ + +func (widget *GraphWidget) UpdateRefreshedAt() { + widget.RefreshedAt = time.Now() +} + +func (widget *GraphWidget) addView() { + view := tview.NewTextView() + + view.SetBackgroundColor(ColorFor(Config.UString("wtf.colors.background", "black"))) + view.SetBorder(true) + view.SetBorderColor(ColorFor(widget.BorderColor())) + view.SetDynamicColors(true) + view.SetTitle(widget.Name) + view.SetWrap(false) + + widget.View = view +} + +// BuildBars will build a string of * to represent your data of [time][value] +// time should be passed as a int64 +func (widget *GraphWidget) BuildBars(maxStars int, starChar string, data [][2]int64) { + + var buffer bytes.Buffer + + //counter to inintialize min value + var count int + + //store the max value from the array + var maxValue int + + //store the min value from the array + var minValue int + + //just getting min and max values + for i := range data { + + var val = int(data[i][0]) + + //initialize the min value + if count == 0 { + minValue = val + } + count++ + + //update max value + if val > maxValue { + maxValue = val + } + + //update minValue + if val < minValue { + minValue = val + } + + } + + // each number = how many stars? + var starRatio = float64(maxStars) / float64((maxValue - minValue)) + + //build the stars + for i := range data { + var val = int(data[i][0]) + + //how many stars for this one? + var starCount = int(float64((val - minValue)) * starRatio) + + if starCount == 0 { + starCount = 1 + } + //build the actual string + var stars = strings.Repeat(starChar, starCount) + + //parse the time + var t = time.Unix(int64(data[i][1]/1000), 0) + + //write the line + buffer.WriteString(fmt.Sprintf("%s -\t [red]%s[white] - (%d)\n", t.Format("Jan 02, 2006"), stars, val)) + } + + widget.View.SetText(buffer.String()) + +} + +/* -------------------- Exported Functions -------------------- */ From 1075096f2fb33fc6a19149d60da489bc1af7a0b6 Mon Sep 17 00:00:00 2001 From: Bill Keenan Date: Wed, 6 Jun 2018 11:05:47 -0700 Subject: [PATCH 2/5] removed unused sleep code --- bargraph/widget.go | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/bargraph/widget.go b/bargraph/widget.go index 51194638..aaea80d1 100644 --- a/bargraph/widget.go +++ b/bargraph/widget.go @@ -62,28 +62,9 @@ func (widget *Widget) Refresh() { return } - if started == false { - // this code should run once - go func() { - for { - time.Sleep(time.Duration(widget.updateInterval) * time.Second) - } - }() - - } - - started = true - widget.UpdateRefreshedAt() widget.View.Clear() - if !ok { - widget.View.SetText( - fmt.Sprint("Error!"), - ) - return - } - display(widget) } From 540dee04d7d0acea0efa9b5d7e71fc14f5e9343d Mon Sep 17 00:00:00 2001 From: Bill Date: Wed, 6 Jun 2018 11:31:52 -0700 Subject: [PATCH 3/5] updated with suggestions from @senorprogrammer --- bargraph/widget.go | 6 +++--- wtf.go | 2 +- wtf/{graph_widget.go => bargraph.go} | 27 ++++++++++++++------------- 3 files changed, 18 insertions(+), 17 deletions(-) rename wtf/{graph_widget.go => bargraph.go} (81%) diff --git a/bargraph/widget.go b/bargraph/widget.go index 51194638..d661a658 100644 --- a/bargraph/widget.go +++ b/bargraph/widget.go @@ -17,7 +17,7 @@ var ok = true // Widget define wtf widget to register widget later type Widget struct { - wtf.GraphWidget + wtf.BarGraph // time interval for send http request updateInterval int @@ -26,7 +26,7 @@ type Widget struct { // NewWidget Make new instance of widget func NewWidget() *Widget { widget := Widget{ - GraphWidget: wtf.NewGraphWidget(" Bar Graph", "bargraph", false), + BarGraph: wtf.NewBarGraph(" Bar Graph", "bargraph", false), } widget.View.SetWrap(true) @@ -51,7 +51,7 @@ func MakeGraph(widget *Widget) { } - widget.GraphWidget.BuildBars(20, "🌟", stats[:]) + widget.BarGraph.BuildBars(20, "🌟", stats[:]) } diff --git a/wtf.go b/wtf.go index a855089a..387c709b 100644 --- a/wtf.go +++ b/wtf.go @@ -6,12 +6,12 @@ import ( "os" "time" - "github.com/BillKeenan/wtf/bargraph" "github.com/gdamore/tcell" "github.com/olebedev/config" "github.com/radovskyb/watcher" "github.com/rivo/tview" "github.com/senorprogrammer/wtf/bamboohr" + "github.com/senorprogrammer/wtf/bargraph" "github.com/senorprogrammer/wtf/clocks" "github.com/senorprogrammer/wtf/cmdrunner" "github.com/senorprogrammer/wtf/cryptoexchanges/bittrex" diff --git a/wtf/graph_widget.go b/wtf/bargraph.go similarity index 81% rename from wtf/graph_widget.go rename to wtf/bargraph.go index 84b5a4a7..7c7da2ac 100644 --- a/wtf/graph_widget.go +++ b/wtf/bargraph.go @@ -9,7 +9,8 @@ import ( "github.com/rivo/tview" ) -type GraphWidget struct { +//BarGraph lets make graphs +type BarGraph struct { enabled bool focusable bool @@ -23,9 +24,9 @@ type GraphWidget struct { Data [][2]int64 } -// NewGraphWidget initialize your fancy new graph -func NewGraphWidget(name string, configKey string, focusable bool) GraphWidget { - widget := GraphWidget{ +// NewBarGraph initialize your fancy new graph +func NewBarGraph(name string, configKey string, focusable bool) BarGraph { + widget := BarGraph{ enabled: Config.UBool(fmt.Sprintf("wtf.mods.%s.enabled", configKey), false), focusable: focusable, @@ -45,7 +46,7 @@ func NewGraphWidget(name string, configKey string, focusable bool) GraphWidget { return widget } -func (widget *GraphWidget) BorderColor() string { +func (widget *BarGraph) BorderColor() string { if widget.Focusable() { return Config.UString("wtf.colors.border.focusable", "red") } @@ -53,33 +54,33 @@ func (widget *GraphWidget) BorderColor() string { return Config.UString("wtf.colors.border.normal", "gray") } -func (widget *GraphWidget) Disabled() bool { +func (widget *BarGraph) Disabled() bool { return !widget.Enabled() } -func (widget *GraphWidget) Enabled() bool { +func (widget *BarGraph) Enabled() bool { return widget.enabled } -func (widget *GraphWidget) Focusable() bool { +func (widget *BarGraph) Focusable() bool { return widget.enabled && widget.focusable } -func (widget *GraphWidget) RefreshInterval() int { +func (widget *BarGraph) RefreshInterval() int { return widget.RefreshInt } -func (widget *GraphWidget) TextView() *tview.TextView { +func (widget *BarGraph) TextView() *tview.TextView { return widget.View } /* -------------------- Unexported Functions -------------------- */ -func (widget *GraphWidget) UpdateRefreshedAt() { +func (widget *BarGraph) UpdateRefreshedAt() { widget.RefreshedAt = time.Now() } -func (widget *GraphWidget) addView() { +func (widget *BarGraph) addView() { view := tview.NewTextView() view.SetBackgroundColor(ColorFor(Config.UString("wtf.colors.background", "black"))) @@ -94,7 +95,7 @@ func (widget *GraphWidget) addView() { // BuildBars will build a string of * to represent your data of [time][value] // time should be passed as a int64 -func (widget *GraphWidget) BuildBars(maxStars int, starChar string, data [][2]int64) { +func (widget *BarGraph) BuildBars(maxStars int, starChar string, data [][2]int64) { var buffer bytes.Buffer From 4b12c73b11de8dc7f49e622fe069f8b75d854284 Mon Sep 17 00:00:00 2001 From: Bill Date: Wed, 6 Jun 2018 11:34:32 -0700 Subject: [PATCH 4/5] added sample code comment --- bargraph/widget.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bargraph/widget.go b/bargraph/widget.go index 3b57bdcd..0e798952 100644 --- a/bargraph/widget.go +++ b/bargraph/widget.go @@ -1,7 +1,10 @@ package bargraph +/************** +This is a demo bargraph that just populates some random date/val data +*/ + import ( - "fmt" "math/rand" "time" @@ -26,7 +29,7 @@ type Widget struct { // NewWidget Make new instance of widget func NewWidget() *Widget { widget := Widget{ - BarGraph: wtf.NewBarGraph(" Bar Graph", "bargraph", false), + BarGraph: wtf.NewBarGraph(" Sample Bar Graph", "bargraph", false), } widget.View.SetWrap(true) From 34a4061f5ded4d85eba932dc5265dd10bc072896 Mon Sep 17 00:00:00 2001 From: Bill Date: Wed, 6 Jun 2018 11:50:44 -0700 Subject: [PATCH 5/5] sample bargraph config added --- _sample_configs/bargraph_config.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 _sample_configs/bargraph_config.yml diff --git a/_sample_configs/bargraph_config.yml b/_sample_configs/bargraph_config.yml new file mode 100644 index 00000000..577771ee --- /dev/null +++ b/_sample_configs/bargraph_config.yml @@ -0,0 +1,27 @@ +wtf: + colors: + border: + focusable: darkslateblue + focused: orange + normal: gray + grid: + columns: [40, 40] + rows: [13, 13, 4] + refreshInterval: 1 + mods: + bargraph: + colors: + from: + name: coral + displayName: grey + to: + name: white + price: green + enabled: true + position: + top: 2 + left: 0 + height: 2 + width: 2 + refreshInterval: 30 + updateInterval: 15 \ No newline at end of file