From f2b943e3d6178ab0d9613b226c6ef5363119b39a Mon Sep 17 00:00:00 2001 From: Andrew_Zol Date: Fri, 8 Jun 2018 20:27:39 +0300 Subject: [PATCH 01/11] Added gspreadsheets module --- .gitignore | 1 + gspreadsheets/client.go | 145 ++++++++++++++++++++++++++++++++++++++++ gspreadsheets/widget.go | 56 ++++++++++++++++ wtf.go | 2 + 4 files changed, 204 insertions(+) create mode 100644 gspreadsheets/client.go create mode 100644 gspreadsheets/widget.go diff --git a/.gitignore b/.gitignore index 7048d6ec..2d038a3c 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ # Misc .DS_Store gcal/client_secret.json +gspreadsheets/client_secret.json #intellij idea .idea/ diff --git a/gspreadsheets/client.go b/gspreadsheets/client.go new file mode 100644 index 00000000..1cf16321 --- /dev/null +++ b/gspreadsheets/client.go @@ -0,0 +1,145 @@ +/* +* This butt-ugly code is direct from Google itself +* https://developers.google.com/sheets/api/quickstart/go + */ + +package gspreadsheets + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" + "os" + "os/user" + "path/filepath" + "strings" + + "github.com/senorprogrammer/wtf/wtf" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + sheets "google.golang.org/api/sheets/v4" +) + +/* -------------------- Exported Functions -------------------- */ + +func Fetch() ([]*sheets.ValueRange, error) { + ctx := context.Background() + + secretPath, _ := wtf.ExpandHomeDir(Config.UString("wtf.mods.gspreadsheets.secretFile")) + + b, err := ioutil.ReadFile(secretPath) + if err != nil { + log.Fatalf("Unable to read secretPath. %v", err) + return nil, err + } + + config, err := google.ConfigFromJSON(b, "https://www.googleapis.com/auth/spreadsheets.readonly") + + if err != nil { + log.Fatalf("Unable to get config from JSON. %v", err) + return nil, err + } + client := getClient(ctx, config) + + srv, err := sheets.New(client) + if err != nil { + log.Fatalf("Unable to get create server. %v", err) + return nil, err + } + + cells := wtf.ToStrs(Config.UList("wtf.mods.gspreadsheets.cells.addresses")) + documentId := Config.UString("wtf.mods.gspreadsheets.sheetId") + addresses := strings.Join(cells[:], ";") + + responses := make([]*sheets.ValueRange, len(cells)) + + for i := 0; i < len(cells); i++ { + resp, err := srv.Spreadsheets.Values.Get(documentId, cells[i]).Do() + if err != nil { + log.Fatalf("Error fetching cells %s", addresses) + return nil, err + } + responses[i] = resp + } + + return responses, err +} + +/* -------------------- Unexported Functions -------------------- */ + +// getClient uses a Context and Config to retrieve a Token +// then generate a Client. It returns the generated Client. +func getClient(ctx context.Context, config *oauth2.Config) *http.Client { + cacheFile, err := tokenCacheFile() + if err != nil { + log.Fatalf("Unable to get path to cached credential file. %v", err) + } + tok, err := tokenFromFile(cacheFile) + if err != nil { + tok = getTokenFromWeb(config) + saveToken(cacheFile, tok) + } + return config.Client(ctx, tok) +} + +// getTokenFromWeb uses Config to request a Token. +// It returns the retrieved Token. +func getTokenFromWeb(config *oauth2.Config) *oauth2.Token { + authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline) + fmt.Printf("Go to the following link in your browser then type the "+ + "authorization code: \n%v\n", authURL) + + var code string + if _, err := fmt.Scan(&code); err != nil { + log.Fatalf("Unable to read authorization code %v", err) + } + + tok, err := config.Exchange(oauth2.NoContext, code) + if err != nil { + log.Fatalf("Unable to retrieve token from web %v", err) + } + return tok +} + +// tokenCacheFile generates credential file path/filename. +// It returns the generated credential path/filename. +func tokenCacheFile() (string, error) { + usr, err := user.Current() + if err != nil { + return "", err + } + tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials") + os.MkdirAll(tokenCacheDir, 0700) + return filepath.Join(tokenCacheDir, + url.QueryEscape("spreadsheets-go-quickstart.json")), err +} + +// tokenFromFile retrieves a Token from a given file path. +// It returns the retrieved Token and any read error encountered. +func tokenFromFile(file string) (*oauth2.Token, error) { + f, err := os.Open(file) + if err != nil { + return nil, err + } + t := &oauth2.Token{} + err = json.NewDecoder(f).Decode(t) + defer f.Close() + return t, err +} + +// saveToken uses a file path to create a file and store the +// token in it. +func saveToken(file string, token *oauth2.Token) { + fmt.Printf("Saving credential file to: %s\n", file) + f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + log.Fatalf("Unable to cache oauth token: %v", err) + } + defer f.Close() + + json.NewEncoder(f).Encode(token) +} diff --git a/gspreadsheets/widget.go b/gspreadsheets/widget.go new file mode 100644 index 00000000..e754ce74 --- /dev/null +++ b/gspreadsheets/widget.go @@ -0,0 +1,56 @@ +package gspreadsheets + +import ( + "fmt" + + "github.com/senorprogrammer/wtf/wtf" + "github.com/olebedev/config" + sheets "google.golang.org/api/sheets/v4" +) + +// Config is a pointer to the global config object +var Config *config.Config + +type Widget struct { + wtf.TextWidget +} + +func NewWidget() *Widget { + widget := Widget{ + TextWidget: wtf.NewTextWidget(" Google Spreadsheets ", "gspreadsheets", false), + } + + return &widget +} + +/* -------------------- Exported Functions -------------------- */ + +func (widget *Widget) Refresh() { + if widget.Disabled() { + return + } + + cells, _ := Fetch() + + widget.UpdateRefreshedAt() + + widget.View.SetText(fmt.Sprintf("%s", widget.contentFrom(cells))) +} + +/* -------------------- Unexported Functions -------------------- */ + +func (widget *Widget) contentFrom(valueRanges []*sheets.ValueRange) string { + if valueRanges == nil { + return "error 1" + } + + valuesColor := Config.UString("wtf.mods.gspreadsheets.colors.values", "green") + res := "" + + cells := wtf.ToStrs(Config.UList("wtf.mods.gspreadsheets.cells.names")) + for i := 0; i < len(valueRanges); i++ { + res = res + fmt.Sprintf("%s\t[%s]%s\n", cells[i], valuesColor, valueRanges[i].Values[0][0]) + } + + return res +} diff --git a/wtf.go b/wtf.go index b9627490..baa24779 100644 --- a/wtf.go +++ b/wtf.go @@ -16,6 +16,7 @@ import ( "github.com/senorprogrammer/wtf/cryptoexchanges/bittrex" "github.com/senorprogrammer/wtf/cryptoexchanges/cryptolive" "github.com/senorprogrammer/wtf/gcal" + "github.com/senorprogrammer/wtf/gspreadsheets" "github.com/senorprogrammer/wtf/git" "github.com/senorprogrammer/wtf/github" "github.com/senorprogrammer/wtf/help" @@ -220,6 +221,7 @@ func makeWidgets(app *tview.Application, pages *tview.Pages) { cmdrunner.Config = Config cryptolive.Config = Config gcal.Config = Config + gspreadsheets.Config = Config git.Config = Config github.Config = Config ipinfo.Config = Config From 0ae1061ced6682b84b7552c42a29d91eb3e7ee54 Mon Sep 17 00:00:00 2001 From: Andrew_Zol Date: Fri, 8 Jun 2018 20:31:44 +0300 Subject: [PATCH 02/11] Added updated config. --- _sample_configs/complex_config.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/_sample_configs/complex_config.yml b/_sample_configs/complex_config.yml index c21bdb72..e07aa209 100644 --- a/_sample_configs/complex_config.yml +++ b/_sample_configs/complex_config.yml @@ -79,6 +79,25 @@ wtf: refreshInterval: 300 secretFile: "~/.wtf/gcal/client_secret.json" withLocation: true + gspreadsheets: + enabled: true + secretFile: "~/.wtf/gspreadsheets/client_secret.json" + refreshInterval: "300" + sheetId: "id_of_google_spreadsheet" + colors: + values: "green" + cells: + names: + - "Cell 1 name" + - "Cell 2 name" + addresses: + - "A1" + - "A2" + position: + top: 0 + left: 0 + width: 1 + height: 1 git: commitCount: 5 enabled: true From a70a0cd41e6a283d283ee868875b6cdb56d56f92 Mon Sep 17 00:00:00 2001 From: Bryan Austin Date: Fri, 8 Jun 2018 12:18:27 -0700 Subject: [PATCH 03/11] Allow use of project list in Jira module config For my own use case (and anyone in a similar situation), the Jira module is more useful if I can specify a list of projects to display issues from, rather than no project (which selects all projects) or one specific project. New supported syntax: ` project: ["PROJA", "PROJB"]` If this is merged, documentation for the Jira module should be updated accordingly. (Sorry, I would have done this myself but I'm not sure what the right place is - when grepping I find multiple places in the repo containing documentation strings and I'm not sure what the "master" location is) Specifying a single project (or no project) is still supported - behavior shouldn't change for anyone who doesn't change their config. --- jira/client.go | 22 +++++++++++++++++++--- jira/widget.go | 20 +++++++++++++++++++- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/jira/client.go b/jira/client.go index b7903018..226dd14c 100644 --- a/jira/client.go +++ b/jira/client.go @@ -12,11 +12,12 @@ import ( "strings" ) -func IssuesFor(username string, project string, jql string) (*SearchResult, error) { +func IssuesFor(username string, projects []string, jql string) (*SearchResult, error) { query := []string{} - if project != "" { - query = append(query, buildJql("project", project)) + var projQuery = getProjectQuery(projects) + if projQuery != "" { + query = append(query, projQuery) } if username != "" { @@ -88,3 +89,18 @@ func parseJson(obj interface{}, text io.Reader) { } } } + +func getProjectQuery(projects []string) string { + singleEmptyProject := len(projects) == 1 && len(projects[0]) == 0 + if len(projects) == 0 || singleEmptyProject { + return "" + } else if len(projects) == 1 { + return buildJql("project", projects[0]) + } + + quoted := make([]string, len(projects)) + for i := range projects { + quoted[i] = fmt.Sprintf("\"%s\"", projects[i]) + } + return fmt.Sprintf("project in (%s)", strings.Join(quoted, ", ")) +} diff --git a/jira/widget.go b/jira/widget.go index 126c629d..6ca9670e 100644 --- a/jira/widget.go +++ b/jira/widget.go @@ -25,7 +25,7 @@ func NewWidget() *Widget { /* -------------------- Exported Functions -------------------- */ func (widget *Widget) Refresh() { - searchResult, err := IssuesFor(Config.UString("wtf.mods.jira.username"), Config.UString("wtf.mods.jira.project", ""), Config.UString("wtf.mods.jira.jql", "")) + searchResult, err := IssuesFor(Config.UString("wtf.mods.jira.username"), getProjects(), Config.UString("wtf.mods.jira.jql", "")) widget.UpdateRefreshedAt() @@ -81,3 +81,21 @@ func (widget *Widget) issueTypeColor(issue *Issue) string { return color } + +func getProjects() []string { + // see if project is set to a single string + configPath := "wtf.mods.jira.project" + singleProject, err := Config.String(configPath) + if err == nil { + return []string{singleProject} + } + // else, assume list + projList := Config.UList(configPath) + var ret []string + for _, proj := range projList { + if str, ok := proj.(string); ok { + ret = append(ret, str) + } + } + return ret +} From e2c1f793bfd6eb2e3b842a3cf182233435fcb487 Mon Sep 17 00:00:00 2001 From: Bryan Austin Date: Fri, 8 Jun 2018 14:44:45 -0700 Subject: [PATCH 04/11] Fix newline in git module repo names breaking display After setting up the git module with multiple repos and switching between them, I observed some graphical wonkiness in the display: https://i.imgur.com/R3e7eij.png After adding some log statements, I tracked it down to the `GitRepo.Repository` field having a newline in it after it's set from a command execution's stdout. This change strips the repository path of spaces when assigning to the `Repository` field, which fixes the display issues. --- git/git_repo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/git_repo.go b/git/git_repo.go index 3f657732..5d3ba090 100644 --- a/git/git_repo.go +++ b/git/git_repo.go @@ -22,7 +22,7 @@ func NewGitRepo(repoPath string) *GitRepo { repo.Branch = repo.branch() repo.ChangedFiles = repo.changedFiles() repo.Commits = repo.commits() - repo.Repository = repo.repository() + repo.Repository = strings.TrimSpace(repo.repository()) return &repo } From 23f53a06b9db9f2f4401d107bc2071f2e7e0092c Mon Sep 17 00:00:00 2001 From: Chris Cummer Date: Fri, 8 Jun 2018 15:34:31 -0700 Subject: [PATCH 05/11] Make Bargraph widget work with new module lazy-loading --- wtf.go | 4 +++- wtf/bargraph.go | 4 ++++ wtf/text_widget.go | 8 ++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/wtf.go b/wtf.go index 7ecc536d..9ff361a8 100644 --- a/wtf.go +++ b/wtf.go @@ -169,6 +169,8 @@ func addWidget(app *tview.Application, pages *tview.Pages, widgetName string) { switch widgetName { case "bamboohr": Widgets = append(Widgets, bamboohr.NewWidget()) + case "bargraph": + Widgets = append(Widgets, bargraph.NewWidget()) case "bittrex": Widgets = append(Widgets, bittrex.NewWidget()) case "clocks": @@ -216,7 +218,7 @@ func makeWidgets(app *tview.Application, pages *tview.Pages) { // Always in alphabetical order bamboohr.Config = Config - bargraph.Config = Config + bargraph.Config = Config bittrex.Config = Config clocks.Config = Config cmdrunner.Config = Config diff --git a/wtf/bargraph.go b/wtf/bargraph.go index 7c7da2ac..977f526f 100644 --- a/wtf/bargraph.go +++ b/wtf/bargraph.go @@ -54,6 +54,10 @@ func (widget *BarGraph) BorderColor() string { return Config.UString("wtf.colors.border.normal", "gray") } +func (widget *BarGraph) Disable() { + widget.enabled = false +} + func (widget *BarGraph) Disabled() bool { return !widget.Enabled() } diff --git a/wtf/text_widget.go b/wtf/text_widget.go index b62868ef..c1bef129 100644 --- a/wtf/text_widget.go +++ b/wtf/text_widget.go @@ -53,6 +53,10 @@ func (widget *TextWidget) BorderColor() string { return Config.UString("wtf.colors.border.normal", "gray") } +func (widget *TextWidget) Disable() { + widget.enabled = false +} + func (widget *TextWidget) Disabled() bool { return !widget.Enabled() } @@ -61,10 +65,6 @@ func (widget *TextWidget) Enabled() bool { return widget.enabled } -func (widget *TextWidget) Disable() { - widget.enabled = false -} - func (widget *TextWidget) Focusable() bool { return widget.enabled && widget.focusable } From e319ab69e78ded09aeb7635247f18b3fd214b105 Mon Sep 17 00:00:00 2001 From: Chris Cummer Date: Fri, 8 Jun 2018 15:57:22 -0700 Subject: [PATCH 06/11] Make Google Spreadsheet widget work with new module lazy-loading --- gspreadsheets/widget.go | 6 +----- wtf.go | 6 ++++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/gspreadsheets/widget.go b/gspreadsheets/widget.go index e754ce74..680869cf 100644 --- a/gspreadsheets/widget.go +++ b/gspreadsheets/widget.go @@ -3,8 +3,8 @@ package gspreadsheets import ( "fmt" - "github.com/senorprogrammer/wtf/wtf" "github.com/olebedev/config" + "github.com/senorprogrammer/wtf/wtf" sheets "google.golang.org/api/sheets/v4" ) @@ -26,10 +26,6 @@ func NewWidget() *Widget { /* -------------------- Exported Functions -------------------- */ func (widget *Widget) Refresh() { - if widget.Disabled() { - return - } - cells, _ := Fetch() widget.UpdateRefreshedAt() diff --git a/wtf.go b/wtf.go index 22659cd7..954d3871 100644 --- a/wtf.go +++ b/wtf.go @@ -17,9 +17,9 @@ import ( "github.com/senorprogrammer/wtf/cryptoexchanges/bittrex" "github.com/senorprogrammer/wtf/cryptoexchanges/cryptolive" "github.com/senorprogrammer/wtf/gcal" - "github.com/senorprogrammer/wtf/gspreadsheets" "github.com/senorprogrammer/wtf/git" "github.com/senorprogrammer/wtf/github" + "github.com/senorprogrammer/wtf/gspreadsheets" "github.com/senorprogrammer/wtf/help" "github.com/senorprogrammer/wtf/ipinfo" "github.com/senorprogrammer/wtf/jira" @@ -186,6 +186,8 @@ func addWidget(app *tview.Application, pages *tview.Pages, widgetName string) { Widgets = append(Widgets, git.NewWidget(app, pages)) case "github": Widgets = append(Widgets, github.NewWidget(app, pages)) + case "gspreadsheets": + Widgets = append(Widgets, gspreadsheets.NewWidget()) case "ipinfo": Widgets = append(Widgets, ipinfo.NewWidget()) case "jira": @@ -225,9 +227,9 @@ func makeWidgets(app *tview.Application, pages *tview.Pages) { cmdrunner.Config = Config cryptolive.Config = Config gcal.Config = Config - gspreadsheets.Config = Config git.Config = Config github.Config = Config + gspreadsheets.Config = Config ipinfo.Config = Config jira.Config = Config newrelic.Config = Config From a1af76c17afdfe1dd3e8606e108612514e5ffb33 Mon Sep 17 00:00:00 2001 From: Chris Cummer Date: Fri, 8 Jun 2018 16:15:25 -0700 Subject: [PATCH 07/11] Remove experimental tag from PrettyWeather --- README.md | 19 ------------------- _site/content/posts/modules/prettyweather.md | 2 -- _site/themes/hyde-hyde/layouts/index.html | 6 +++--- .../hyde-hyde/layouts/partials/sidebar.html | 2 +- docs/404.html | 2 +- docs/categories/index.html | 2 +- docs/index.html | 8 ++++---- docs/index.xml | 3 +-- .../posts/configuration/attributes/index.html | 2 +- docs/posts/configuration/index.html | 2 +- docs/posts/configuration/iterm2/index.html | 2 +- docs/posts/glossary/index.html | 2 +- docs/posts/index.html | 2 +- docs/posts/index.xml | 3 +-- docs/posts/installation/index.html | 2 +- docs/posts/modules/bamboohr/index.html | 2 +- docs/posts/modules/clocks/index.html | 2 +- docs/posts/modules/cmdrunner/index.html | 2 +- .../cryptocurrencies/bittrex/index.html | 2 +- .../cryptocurrencies/cryptolive/index.html | 2 +- docs/posts/modules/gcal/index.html | 2 +- docs/posts/modules/git/index.html | 2 +- docs/posts/modules/github/index.html | 2 +- docs/posts/modules/index.html | 2 +- docs/posts/modules/ipinfo/index.html | 2 +- docs/posts/modules/jira/index.html | 2 +- docs/posts/modules/newrelic/index.html | 2 +- docs/posts/modules/opsgenie/index.html | 2 +- docs/posts/modules/power/index.html | 2 +- docs/posts/modules/prettyweather/index.html | 4 +--- docs/posts/modules/security/index.html | 2 +- docs/posts/modules/textfile/index.html | 2 +- docs/posts/modules/todo/index.html | 2 +- docs/posts/modules/weather/index.html | 2 +- docs/posts/overview/index.html | 2 +- docs/tags/index.html | 2 +- 36 files changed, 39 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 705c2be5..4fb8b7c7 100644 --- a/README.md +++ b/README.md @@ -43,25 +43,6 @@ documentation. Here's some short-cuts: * [Configuration](http://wtfutil.com/posts/configuration/) * [Module Documentation](http://wtfutil.com/posts/modules/) -And a "probably up-to-date" list of currently-implemented modules: - -* [BambooHR](http://wtfutil.com/posts/modules/bamboohr/) -* [World Clocks](http://wtfutil.com/posts/modules/clocks/) -* [Command Runner](http://wtfutil.com/posts/modules/cmdrunner/) -* [Google Calendar](http://wtfutil.com/posts/modules/gcal/) -* [Git](http://wtfutil.com/posts/modules/git/) -* [GitHub](http://wtfutil.com/posts/modules/github/) -* [IPInfo](http://wtfutil.com/posts/modules/ipinfo/) -* [Jira](http://wtfutil.com/posts/modules/jira/) -* [New Relic](http://wtfutil.com/posts/modules/newrelic/) -* [OpsGenie](http://wtfutil.com/posts/modules/opsgenie) -* [Power](http://wtfutil.com/posts/modules/power/) -* [PrettyWeather](http://wtfutil.com/posts/modules/prettyweather/)* -* [Security](http://wtfutil.com/posts/modules/security/) -* [Textfile](http://wtfutil.com/posts/modules/textfile/) -* [Todo List](http://wtfutil.com/posts/modules/todo/) -* [Weather](http://wtfutil.com/posts/modules/weather/) - *experimental ## Contributing diff --git a/_site/content/posts/modules/prettyweather.md b/_site/content/posts/modules/prettyweather.md index 1f748a32..66a78c1e 100644 --- a/_site/content/posts/modules/prettyweather.md +++ b/_site/content/posts/modules/prettyweather.md @@ -4,8 +4,6 @@ date: 2018-06-02T05:32:04-07:00 draft: false --- -**🔬 Experimental** - Displays weather information as ASCII art from [Wttr.in](http://wttr.in). diff --git a/_site/themes/hyde-hyde/layouts/index.html b/_site/themes/hyde-hyde/layouts/index.html index 888abc24..d6fed31f 100644 --- a/_site/themes/hyde-hyde/layouts/index.html +++ b/_site/themes/hyde-hyde/layouts/index.html @@ -15,7 +15,7 @@

- Keep an eye on your OpsGenie schedules, Google Calendar, Git + Keep an eye on your OpsGenie schedules, Google Calendar, Git and Github repositories, and New Relic deployments.

@@ -28,8 +28,8 @@

Download Latest - - On Github + Source on Github + Chat on Gitter

diff --git a/_site/themes/hyde-hyde/layouts/partials/sidebar.html b/_site/themes/hyde-hyde/layouts/partials/sidebar.html index fe5c83e2..7d593f21 100644 --- a/_site/themes/hyde-hyde/layouts/partials/sidebar.html +++ b/_site/themes/hyde-hyde/layouts/partials/sidebar.html @@ -35,7 +35,7 @@

- + diff --git a/docs/404.html b/docs/404.html index a6f51b08..262aef29 100644 --- a/docs/404.html +++ b/docs/404.html @@ -76,7 +76,7 @@ - + diff --git a/docs/categories/index.html b/docs/categories/index.html index b52bb86e..8803ffb8 100644 --- a/docs/categories/index.html +++ b/docs/categories/index.html @@ -78,7 +78,7 @@ - + diff --git a/docs/index.html b/docs/index.html index 5b332b6c..fb5764ed 100644 --- a/docs/index.html +++ b/docs/index.html @@ -77,7 +77,7 @@ - + @@ -107,7 +107,7 @@

- Keep an eye on your OpsGenie schedules, Google Calendar, Git + Keep an eye on your OpsGenie schedules, Google Calendar, Git and Github repositories, and New Relic deployments.

@@ -120,8 +120,8 @@

Download Latest - - On Github + Source on Github + Chat on Gitter

diff --git a/docs/index.xml b/docs/index.xml index a86b5ce2..28d48bd1 100644 --- a/docs/index.xml +++ b/docs/index.xml @@ -53,8 +53,7 @@ position Defines where in the grid this module&rsquo;s widget will be displa Sat, 02 Jun 2018 05:32:04 -0700 https://wtfutil.com/posts/modules/prettyweather/ - 🔬 Experimental -Displays weather information as ASCII art from Wttr.in. + Displays weather information as ASCII art from Wttr.in. Source Code wtf/prettyweather/ Required ENV Variables None. Keyboard Commands None. Configuration prettyweather:enabled:truecity:&#34;tehran&#34;position:top:3left:5height:1width:1refreshInterval:300unit:&#34;c&#34;view:0 Attributes city Optional. It will grab the current location from your IP address if omitted. diff --git a/docs/posts/configuration/attributes/index.html b/docs/posts/configuration/attributes/index.html index a5354fce..bd308fdf 100644 --- a/docs/posts/configuration/attributes/index.html +++ b/docs/posts/configuration/attributes/index.html @@ -76,7 +76,7 @@

- + diff --git a/docs/posts/configuration/index.html b/docs/posts/configuration/index.html index ec410d85..067aab24 100644 --- a/docs/posts/configuration/index.html +++ b/docs/posts/configuration/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/configuration/iterm2/index.html b/docs/posts/configuration/iterm2/index.html index dcb4d6c6..ba679394 100644 --- a/docs/posts/configuration/iterm2/index.html +++ b/docs/posts/configuration/iterm2/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/glossary/index.html b/docs/posts/glossary/index.html index 9a4f7187..b88135ae 100644 --- a/docs/posts/glossary/index.html +++ b/docs/posts/glossary/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/index.html b/docs/posts/index.html index ae588754..120d52e0 100644 --- a/docs/posts/index.html +++ b/docs/posts/index.html @@ -78,7 +78,7 @@ - + diff --git a/docs/posts/index.xml b/docs/posts/index.xml index 2dc5ba0f..71eef75b 100644 --- a/docs/posts/index.xml +++ b/docs/posts/index.xml @@ -53,8 +53,7 @@ position Defines where in the grid this module&rsquo;s widget will be displa Sat, 02 Jun 2018 05:32:04 -0700 https://wtfutil.com/posts/modules/prettyweather/ - 🔬 Experimental -Displays weather information as ASCII art from Wttr.in. + Displays weather information as ASCII art from Wttr.in. Source Code wtf/prettyweather/ Required ENV Variables None. Keyboard Commands None. Configuration prettyweather:enabled:truecity:&#34;tehran&#34;position:top:3left:5height:1width:1refreshInterval:300unit:&#34;c&#34;view:0 Attributes city Optional. It will grab the current location from your IP address if omitted. diff --git a/docs/posts/installation/index.html b/docs/posts/installation/index.html index 1237a416..57c8acd7 100644 --- a/docs/posts/installation/index.html +++ b/docs/posts/installation/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/bamboohr/index.html b/docs/posts/modules/bamboohr/index.html index 240a8181..e3b3ac7c 100644 --- a/docs/posts/modules/bamboohr/index.html +++ b/docs/posts/modules/bamboohr/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/clocks/index.html b/docs/posts/modules/clocks/index.html index 04333353..71a519c8 100644 --- a/docs/posts/modules/clocks/index.html +++ b/docs/posts/modules/clocks/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/cmdrunner/index.html b/docs/posts/modules/cmdrunner/index.html index 1ec0afca..edca5f69 100644 --- a/docs/posts/modules/cmdrunner/index.html +++ b/docs/posts/modules/cmdrunner/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/cryptocurrencies/bittrex/index.html b/docs/posts/modules/cryptocurrencies/bittrex/index.html index f9f033eb..18730b2b 100644 --- a/docs/posts/modules/cryptocurrencies/bittrex/index.html +++ b/docs/posts/modules/cryptocurrencies/bittrex/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/cryptocurrencies/cryptolive/index.html b/docs/posts/modules/cryptocurrencies/cryptolive/index.html index 832af988..f84dea13 100644 --- a/docs/posts/modules/cryptocurrencies/cryptolive/index.html +++ b/docs/posts/modules/cryptocurrencies/cryptolive/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/gcal/index.html b/docs/posts/modules/gcal/index.html index 7e6f4a4b..fa6de93d 100644 --- a/docs/posts/modules/gcal/index.html +++ b/docs/posts/modules/gcal/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/git/index.html b/docs/posts/modules/git/index.html index e958c4f1..0ab526af 100644 --- a/docs/posts/modules/git/index.html +++ b/docs/posts/modules/git/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/github/index.html b/docs/posts/modules/github/index.html index eb556adb..55afa1e3 100644 --- a/docs/posts/modules/github/index.html +++ b/docs/posts/modules/github/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/index.html b/docs/posts/modules/index.html index fad43ff2..5ffb2199 100644 --- a/docs/posts/modules/index.html +++ b/docs/posts/modules/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/ipinfo/index.html b/docs/posts/modules/ipinfo/index.html index f3cd4865..5f3994cb 100644 --- a/docs/posts/modules/ipinfo/index.html +++ b/docs/posts/modules/ipinfo/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/jira/index.html b/docs/posts/modules/jira/index.html index 4a60fb7a..258cb919 100644 --- a/docs/posts/modules/jira/index.html +++ b/docs/posts/modules/jira/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/newrelic/index.html b/docs/posts/modules/newrelic/index.html index eac8a8db..db3a032b 100644 --- a/docs/posts/modules/newrelic/index.html +++ b/docs/posts/modules/newrelic/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/opsgenie/index.html b/docs/posts/modules/opsgenie/index.html index dd2eef1a..ff57d7cb 100644 --- a/docs/posts/modules/opsgenie/index.html +++ b/docs/posts/modules/opsgenie/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/power/index.html b/docs/posts/modules/power/index.html index 7cbb1d72..e42e346c 100644 --- a/docs/posts/modules/power/index.html +++ b/docs/posts/modules/power/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/prettyweather/index.html b/docs/posts/modules/prettyweather/index.html index d6bd7e91..a0c4d36b 100644 --- a/docs/posts/modules/prettyweather/index.html +++ b/docs/posts/modules/prettyweather/index.html @@ -76,7 +76,7 @@ - + @@ -113,8 +113,6 @@ -

🔬 Experimental

-

Displays weather information as ASCII art from Wttr.in.

diff --git a/docs/posts/modules/security/index.html b/docs/posts/modules/security/index.html index 04437186..98c131b0 100644 --- a/docs/posts/modules/security/index.html +++ b/docs/posts/modules/security/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/textfile/index.html b/docs/posts/modules/textfile/index.html index c18d9229..a8e53ab4 100644 --- a/docs/posts/modules/textfile/index.html +++ b/docs/posts/modules/textfile/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/todo/index.html b/docs/posts/modules/todo/index.html index 9ffa4bc6..6c105daa 100644 --- a/docs/posts/modules/todo/index.html +++ b/docs/posts/modules/todo/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/modules/weather/index.html b/docs/posts/modules/weather/index.html index f22a2a21..7021ceb2 100644 --- a/docs/posts/modules/weather/index.html +++ b/docs/posts/modules/weather/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/posts/overview/index.html b/docs/posts/overview/index.html index beef7287..a3d75261 100644 --- a/docs/posts/overview/index.html +++ b/docs/posts/overview/index.html @@ -76,7 +76,7 @@ - + diff --git a/docs/tags/index.html b/docs/tags/index.html index 68425b68..9681fff7 100644 --- a/docs/tags/index.html +++ b/docs/tags/index.html @@ -78,7 +78,7 @@ - + From 00c07a9421334d00178c9f5b939af3315cb7bb48 Mon Sep 17 00:00:00 2001 From: Chris Cummer Date: Fri, 8 Jun 2018 16:26:51 -0700 Subject: [PATCH 08/11] Remove experimental tag from README --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 4fb8b7c7..d9a53bd7 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@

-

- +

A personal terminal-based dashboard utility, designed for @@ -43,8 +42,6 @@ documentation. Here's some short-cuts: * [Configuration](http://wtfutil.com/posts/configuration/) * [Module Documentation](http://wtfutil.com/posts/modules/) -*experimental - ## Contributing Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests. From d2dfcd8978a4ae886c3dda17227dbdd1f7e687f2 Mon Sep 17 00:00:00 2001 From: Chris Cummer Date: Sat, 9 Jun 2018 03:58:45 -0700 Subject: [PATCH 09/11] Add cfg/ as a top-level package concept --- {wtf => cfg}/config_files.go | 9 +++++---- todo/widget.go | 7 ++++--- wtf.go | 7 ++++--- 3 files changed, 13 insertions(+), 10 deletions(-) rename {wtf => cfg}/config_files.go (94%) diff --git a/wtf/config_files.go b/cfg/config_files.go similarity index 94% rename from wtf/config_files.go rename to cfg/config_files.go index ff57654d..85e848e8 100644 --- a/wtf/config_files.go +++ b/cfg/config_files.go @@ -1,4 +1,4 @@ -package wtf +package cfg import ( "fmt" @@ -6,10 +6,11 @@ import ( "os" "github.com/olebedev/config" + "github.com/senorprogrammer/wtf/wtf" ) func ConfigDir() (string, error) { - configDir, err := ExpandHomeDir("~/.wtf/") + configDir, err := wtf.ExpandHomeDir("~/.wtf/") if err != nil { return "", err } @@ -59,7 +60,7 @@ func CreateFile(fileName string) (string, error) { // LoadConfigFile loads the config.yml file to configure the app func LoadConfigFile(filePath string) *config.Config { - absPath, _ := ExpandHomeDir(filePath) + absPath, _ := wtf.ExpandHomeDir(filePath) cfg, err := config.ParseYamlFile(absPath) if err != nil { @@ -79,7 +80,7 @@ func ReadConfigFile(fileName string) (string, error) { filePath := fmt.Sprintf("%s/%s", configDir, fileName) - fileData, err := ReadFileBytes(filePath) + fileData, err := wtf.ReadFileBytes(filePath) if err != nil { return "", err } diff --git a/todo/widget.go b/todo/widget.go index 00dcb984..f529f53e 100644 --- a/todo/widget.go +++ b/todo/widget.go @@ -7,6 +7,7 @@ import ( "github.com/gdamore/tcell" "github.com/olebedev/config" "github.com/rivo/tview" + "github.com/senorprogrammer/wtf/cfg" "github.com/senorprogrammer/wtf/wtf" "gopkg.in/yaml.v2" ) @@ -95,7 +96,7 @@ func (widget *Widget) editItem() { } func (widget *Widget) init() { - _, err := wtf.CreateFile(widget.filePath) + _, err := cfg.CreateFile(widget.filePath) if err != nil { panic(err) } @@ -177,7 +178,7 @@ func (widget *Widget) keyboardIntercept(event *tcell.EventKey) *tcell.EventKey { // Loads the todo list from Yaml file func (widget *Widget) load() { - confDir, _ := wtf.ConfigDir() + confDir, _ := cfg.ConfigDir() filePath := fmt.Sprintf("%s/%s", confDir, widget.filePath) fileData, _ := wtf.ReadFileBytes(filePath) @@ -203,7 +204,7 @@ func (widget *Widget) newItem() { // persist writes the todo list to Yaml file func (widget *Widget) persist() { - confDir, _ := wtf.ConfigDir() + confDir, _ := cfg.ConfigDir() filePath := fmt.Sprintf("%s/%s", confDir, widget.filePath) fileData, _ := yaml.Marshal(&widget.list) diff --git a/wtf.go b/wtf.go index 954d3871..2c27fb13 100644 --- a/wtf.go +++ b/wtf.go @@ -12,6 +12,7 @@ import ( "github.com/rivo/tview" "github.com/senorprogrammer/wtf/bamboohr" "github.com/senorprogrammer/wtf/bargraph" + "github.com/senorprogrammer/wtf/cfg" "github.com/senorprogrammer/wtf/clocks" "github.com/senorprogrammer/wtf/cmdrunner" "github.com/senorprogrammer/wtf/cryptoexchanges/bittrex" @@ -260,7 +261,7 @@ func makeWidgets(app *tview.Application, pages *tview.Pages) { } func loadConfig(configFlag string) { - Config = wtf.LoadConfigFile(configFlag) + Config = cfg.LoadConfigFile(configFlag) } func main() { @@ -277,8 +278,8 @@ func main() { // Responsible for creating the configuration directory and default // configuration file if they don't already exist - wtf.CreateConfigDir() - wtf.WriteConfigFile() + cfg.CreateConfigDir() + cfg.WriteConfigFile() loadConfig(cmdFlags.Config) os.Setenv("TERM", Config.UString("wtf.term", os.Getenv("TERM"))) From b593f3517ae4928a54ef0bcb4f2536984db38e7e Mon Sep 17 00:00:00 2001 From: Chris Cummer Date: Sat, 9 Jun 2018 04:07:01 -0700 Subject: [PATCH 10/11] Basic Weather widget API key validation --- weather/display.go | 5 +++++ weather/widget.go | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/weather/display.go b/weather/display.go index 1b237789..9acd9e3d 100644 --- a/weather/display.go +++ b/weather/display.go @@ -11,6 +11,11 @@ import ( func (widget *Widget) display() { widget.View.Clear() + if widget.apiKeyValid() == false { + fmt.Fprintf(widget.View, "%s", " Environment variable WTF_OWM_API_KEY is not set") + return + } + cityData := widget.currentData() if cityData == nil { fmt.Fprintf(widget.View, "%s", " Weather data is unavailable (1)") diff --git a/weather/widget.go b/weather/widget.go index 992c7207..fa92b60d 100644 --- a/weather/widget.go +++ b/weather/widget.go @@ -104,6 +104,18 @@ func (widget *Widget) Prev() { /* -------------------- Unexported Functions -------------------- */ +func (widget *Widget) apiKeyValid() bool { + if widget.APIKey == "" { + return false + } + + if len(widget.APIKey) != 32 { + return false + } + + return true +} + func (widget *Widget) currentData() *owm.CurrentWeatherData { if len(widget.Data) == 0 { return nil From cca845d4ffd266184c62c9597130d2e272268026 Mon Sep 17 00:00:00 2001 From: Chris Cummer Date: Sat, 9 Jun 2018 04:09:04 -0700 Subject: [PATCH 11/11] Don't crash if the Weather API is missing or invalid --- weather/widget.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/weather/widget.go b/weather/widget.go index fa92b60d..8a1176cb 100644 --- a/weather/widget.go +++ b/weather/widget.go @@ -74,7 +74,9 @@ func (widget *Widget) Fetch(cityIDs []int) []*owm.CurrentWeatherData { // Refresh fetches new data from the OpenWeatherMap API and loads the new data into the. // widget's view for rendering func (widget *Widget) Refresh() { - widget.Data = widget.Fetch(wtf.ToInts(Config.UList("wtf.mods.weather.cityids", widget.defaultCityCodes()))) + if widget.apiKeyValid() { + widget.Data = widget.Fetch(wtf.ToInts(Config.UList("wtf.mods.weather.cityids", widget.defaultCityCodes()))) + } widget.UpdateRefreshedAt() widget.display()