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’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:"tehran"position:top:3left:5height:1width:1refreshInterval:300unit:"c"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’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:"tehran"position:top:3left:5height:1width:1refreshInterval:300unit:"c"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()