mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
Add Asana Module (#1052)
* Add Asana Module * Add Asana Module to the widget_maker * Asana Module - addressing linting concerns * Asana Module - ran go mod tidy
This commit is contained in:
parent
6ecec4f149
commit
a8b0fa897a
@ -3,6 +3,7 @@ package app
|
||||
import (
|
||||
"github.com/olebedev/config"
|
||||
"github.com/rivo/tview"
|
||||
"github.com/wtfutil/wtf/modules/asana"
|
||||
"github.com/wtfutil/wtf/modules/azuredevops"
|
||||
"github.com/wtfutil/wtf/modules/bamboohr"
|
||||
"github.com/wtfutil/wtf/modules/bargraph"
|
||||
@ -106,6 +107,9 @@ func MakeWidget(
|
||||
case "arpansagovau":
|
||||
settings := arpansagovau.NewSettingsFromYAML(moduleName, moduleConfig, config)
|
||||
widget = arpansagovau.NewWidget(tviewApp, settings)
|
||||
case "asana":
|
||||
settings := asana.NewSettingsFromYAML(moduleName, moduleConfig, config)
|
||||
widget = asana.NewWidget(tviewApp, pages, settings)
|
||||
case "azuredevops":
|
||||
settings := azuredevops.NewSettingsFromYAML(moduleName, moduleConfig, config)
|
||||
widget = azuredevops.NewWidget(tviewApp, pages, settings)
|
||||
|
2
go.mod
2
go.mod
@ -3,6 +3,7 @@ module github.com/wtfutil/wtf
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
bitbucket.org/mikehouston/asana-go v0.0.0-20201102222432-715318d0343a
|
||||
code.cloudfoundry.org/bytefmt v0.0.0-20190819182555-854d396b647c
|
||||
github.com/Azure/go-autorest v11.1.2+incompatible // indirect
|
||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||
@ -31,7 +32,6 @@ require (
|
||||
github.com/gophercloud/gophercloud v0.5.0 // indirect
|
||||
github.com/hekmon/cunits v2.0.1+incompatible // indirect
|
||||
github.com/hekmon/transmissionrpc v0.0.0-20190525133028-1d589625bacd
|
||||
github.com/imdario/mergo v0.3.8 // indirect
|
||||
github.com/jedib0t/go-pretty v4.3.0+incompatible
|
||||
github.com/jessevdk/go-flags v1.4.0
|
||||
github.com/lib/pq v1.2.0 // indirect
|
||||
|
8
go.sum
8
go.sum
@ -1,3 +1,5 @@
|
||||
bitbucket.org/mikehouston/asana-go v0.0.0-20201102222432-715318d0343a h1:qH51iOpTres3x2kNb0f2R3ggMpbYCyCvaRrsvdndhvY=
|
||||
bitbucket.org/mikehouston/asana-go v0.0.0-20201102222432-715318d0343a/go.mod h1:HcP4iCG6i6uVAyX2X7yKOsjbzLFiTfX0EMT20CYn5Ig=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
@ -388,8 +390,8 @@ github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63
|
||||
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.0.0-20180119215619-163f41321a19/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
|
||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
@ -609,6 +611,8 @@ github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rubenv/sql-migrate v0.0.0-20160620083229-6f4757563362/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
|
280
modules/asana/client.go
Normal file
280
modules/asana/client.go
Normal file
@ -0,0 +1,280 @@
|
||||
package asana
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
asana "bitbucket.org/mikehouston/asana-go"
|
||||
)
|
||||
|
||||
func fetchTasksFromProject(token, projectId, mode string) ([]*TaskItem, error) {
|
||||
taskItems := []*TaskItem{}
|
||||
uidToName := make(map[string]string)
|
||||
|
||||
client := asana.NewClientWithAccessToken(token)
|
||||
|
||||
uid, err := getCurrentUserId(client, mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q := &asana.TaskQuery{
|
||||
Project: projectId,
|
||||
}
|
||||
|
||||
fetchedTasks, _, err := getTasksFromAsana(client, q)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching tasks: %s", err)
|
||||
}
|
||||
|
||||
processFetchedTasks(client, &fetchedTasks, &taskItems, &uidToName, mode, projectId, uid)
|
||||
|
||||
return taskItems, nil
|
||||
}
|
||||
|
||||
func fetchTasksFromProjectSections(token, projectId string, sections []string, mode string) ([]*TaskItem, error) {
|
||||
taskItems := []*TaskItem{}
|
||||
uidToName := make(map[string]string)
|
||||
|
||||
client := asana.NewClientWithAccessToken(token)
|
||||
|
||||
uid, err := getCurrentUserId(client, mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := &asana.Project{
|
||||
ID: projectId,
|
||||
}
|
||||
|
||||
for _, section := range sections {
|
||||
|
||||
sectionId, err := findSection(client, p, section)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching tasks: %s", err)
|
||||
}
|
||||
|
||||
q := &asana.TaskQuery{
|
||||
Section: sectionId,
|
||||
}
|
||||
|
||||
fetchedTasks, _, err := getTasksFromAsana(client, q)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching tasks: %s", err)
|
||||
}
|
||||
|
||||
if len(fetchedTasks) > 0 {
|
||||
taskItem := &TaskItem{
|
||||
name: section,
|
||||
taskType: TASK_SECTION,
|
||||
}
|
||||
|
||||
taskItems = append(taskItems, taskItem)
|
||||
}
|
||||
|
||||
processFetchedTasks(client, &fetchedTasks, &taskItems, &uidToName, mode, projectId, uid)
|
||||
|
||||
}
|
||||
|
||||
return taskItems, nil
|
||||
}
|
||||
|
||||
func fetchTasksFromWorkspace(token, workspaceId, mode string) ([]*TaskItem, error) {
|
||||
taskItems := []*TaskItem{}
|
||||
uidToName := make(map[string]string)
|
||||
|
||||
client := asana.NewClientWithAccessToken(token)
|
||||
|
||||
uid, err := getCurrentUserId(client, mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q := &asana.TaskQuery{
|
||||
Workspace: workspaceId,
|
||||
Assignee: "me",
|
||||
}
|
||||
|
||||
fetchedTasks, _, err := getTasksFromAsana(client, q)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching tasks: %s", err)
|
||||
}
|
||||
|
||||
processFetchedTasks(client, &fetchedTasks, &taskItems, &uidToName, mode, workspaceId, uid)
|
||||
|
||||
return taskItems, nil
|
||||
|
||||
}
|
||||
|
||||
func toggleTaskCompletionById(token, taskId string) error {
|
||||
client := asana.NewClientWithAccessToken(token)
|
||||
|
||||
t := &asana.Task{
|
||||
ID: taskId,
|
||||
}
|
||||
|
||||
err := t.Fetch(client)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error fetching task: %s", err)
|
||||
}
|
||||
|
||||
updateReq := &asana.UpdateTaskRequest{}
|
||||
|
||||
if *t.Completed {
|
||||
f := false
|
||||
updateReq.Completed = &f
|
||||
} else {
|
||||
t := true
|
||||
updateReq.Completed = &t
|
||||
}
|
||||
|
||||
err = t.Update(client, updateReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error updating task: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func processFetchedTasks(client *asana.Client, fetchedTasks *[]*asana.Task, taskItems *[]*TaskItem, uidToName *map[string]string, mode, projectId, uid string) {
|
||||
|
||||
for _, task := range *fetchedTasks {
|
||||
switch {
|
||||
case strings.HasSuffix(mode, "_all"):
|
||||
if task.Assignee != nil {
|
||||
// Check if we have already looked up this user
|
||||
if assigneeName, ok := (*uidToName)[task.Assignee.ID]; ok {
|
||||
task.Assignee.Name = assigneeName
|
||||
} else {
|
||||
// We haven't looked up this user before, perform the lookup now
|
||||
assigneeName, err := getOtherUserEmail(client, task.Assignee.ID)
|
||||
if err != nil {
|
||||
task.Assignee.Name = "Error"
|
||||
}
|
||||
(*uidToName)[task.Assignee.ID] = assigneeName
|
||||
task.Assignee.Name = assigneeName
|
||||
}
|
||||
} else {
|
||||
task.Assignee = &asana.User{
|
||||
Name: "Unassigned",
|
||||
}
|
||||
}
|
||||
taskItem := buildTaskItem(task, projectId)
|
||||
(*taskItems) = append((*taskItems), taskItem)
|
||||
case !strings.HasSuffix(mode, "_all") && task.Assignee != nil && task.Assignee.ID == uid:
|
||||
taskItem := buildTaskItem(task, projectId)
|
||||
(*taskItems) = append((*taskItems), taskItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildTaskItem(task *asana.Task, projectId string) *TaskItem {
|
||||
dueOnString := ""
|
||||
if task.DueOn != nil {
|
||||
dueOn := time.Time(*task.DueOn)
|
||||
currentYear, _, _ := time.Now().Date()
|
||||
if currentYear != dueOn.Year() {
|
||||
dueOnString = dueOn.Format("Jan 2 2006")
|
||||
} else {
|
||||
dueOnString = dueOn.Format("Jan 2")
|
||||
}
|
||||
}
|
||||
|
||||
assignString := ""
|
||||
if task.Assignee != nil {
|
||||
assignString = task.Assignee.Name
|
||||
}
|
||||
|
||||
taskItem := &TaskItem{
|
||||
name: task.Name,
|
||||
id: task.ID,
|
||||
numSubtasks: task.NumSubtasks,
|
||||
dueOn: dueOnString,
|
||||
url: fmt.Sprintf("https://app.asana.com/0/%s/%s/f", projectId, task.ID),
|
||||
taskType: TASK_TYPE,
|
||||
completed: *task.Completed,
|
||||
assignee: assignString,
|
||||
}
|
||||
|
||||
return taskItem
|
||||
|
||||
}
|
||||
|
||||
func getOtherUserEmail(client *asana.Client, uid string) (string, error) {
|
||||
if uid == "" {
|
||||
return "", fmt.Errorf("missing uid")
|
||||
}
|
||||
|
||||
u := &asana.User{
|
||||
ID: uid,
|
||||
}
|
||||
|
||||
err := u.Fetch(client, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error fetching user: %s", err)
|
||||
}
|
||||
|
||||
return u.Email, nil
|
||||
}
|
||||
|
||||
func getCurrentUserId(client *asana.Client, mode string) (string, error) {
|
||||
if strings.HasSuffix(mode, "_all") {
|
||||
return "", nil
|
||||
}
|
||||
u, err := client.CurrentUser()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error getting current user: %s", err)
|
||||
}
|
||||
|
||||
return u.ID, nil
|
||||
}
|
||||
|
||||
func findSection(client *asana.Client, project *asana.Project, sectionName string) (string, error) {
|
||||
sectionId := ""
|
||||
|
||||
sections, _, err := project.Sections(client, &asana.Options{
|
||||
Limit: 100,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error getting sections: %s", err)
|
||||
}
|
||||
|
||||
for _, section := range sections {
|
||||
if section.Name == sectionName {
|
||||
sectionId = section.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if sectionId == "" {
|
||||
return "", fmt.Errorf("we didn't find the section %s", sectionName)
|
||||
}
|
||||
|
||||
return sectionId, nil
|
||||
}
|
||||
|
||||
func getTasksFromAsana(client *asana.Client, q *asana.TaskQuery) ([]*asana.Task, bool, error) {
|
||||
moreTasks := false
|
||||
|
||||
tasks, np, err := client.QueryTasks(q, &asana.Options{
|
||||
Limit: 100,
|
||||
Fields: []string{
|
||||
"assignee",
|
||||
"name",
|
||||
"num_subtasks",
|
||||
"due_on",
|
||||
"completed",
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("error querying tasks: %s", err)
|
||||
}
|
||||
|
||||
if np != nil {
|
||||
moreTasks = true
|
||||
}
|
||||
|
||||
return tasks, moreTasks, nil
|
||||
}
|
20
modules/asana/keyboard.go
Normal file
20
modules/asana/keyboard.go
Normal file
@ -0,0 +1,20 @@
|
||||
package asana
|
||||
|
||||
import "github.com/gdamore/tcell"
|
||||
|
||||
func (widget *Widget) initializeKeyboardControls() {
|
||||
widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
|
||||
widget.InitializeRefreshKeyboardControl(widget.Refresh)
|
||||
|
||||
widget.SetKeyboardChar("j", widget.Next, "Select next task")
|
||||
widget.SetKeyboardChar("k", widget.Prev, "Select previous task")
|
||||
widget.SetKeyboardChar("q", widget.Unselect, "Unselect task")
|
||||
widget.SetKeyboardChar("o", widget.openTask, "Open task in browser")
|
||||
widget.SetKeyboardChar("x", widget.toggleTaskCompletion, "Toggles the task's completion state")
|
||||
widget.SetKeyboardChar("?", widget.ShowHelp, "Shows help")
|
||||
|
||||
widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next task")
|
||||
widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous task")
|
||||
widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Unselect task")
|
||||
widget.SetKeyboardKey(tcell.KeyEnter, widget.openTask, "Open task in browser")
|
||||
}
|
48
modules/asana/settings.go
Normal file
48
modules/asana/settings.go
Normal file
@ -0,0 +1,48 @@
|
||||
package asana
|
||||
|
||||
import (
|
||||
"github.com/olebedev/config"
|
||||
"github.com/wtfutil/wtf/cfg"
|
||||
"github.com/wtfutil/wtf/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultFocusable = true
|
||||
defaultTitle = "Asana"
|
||||
)
|
||||
|
||||
type Settings struct {
|
||||
*cfg.Common
|
||||
|
||||
projectId string `help:"The Asana Project ID. If the mode is 'project' or 'project_sections' this is required to known which Asana Project to pull your tasks from" values:"A valid Asana Project ID string" optional:"true"`
|
||||
|
||||
workspaceId string `help:"The Asana Workspace ID. If mode is 'workspace' this is required" values:"A valid Asana Workspace ID string" optional:"true"`
|
||||
|
||||
sections []string `help:"The Asana Section Labels to fetch from the Project. Required if the mode is 'project_sections'" values:"An array of Asana Section Label strings" optional:"true"`
|
||||
|
||||
allUsers bool `help:"Fetch tasks for all users, defaults to false" values:"bool" optional:"true"`
|
||||
|
||||
mode string `help:"What mode to query Asana, 'project', 'project_sections', 'workspace'" values:"A string with either 'project', 'project_sections' or 'workspace'"`
|
||||
|
||||
hideComplete bool `help:"Hide completed tasks, defaults to false" values:"bool" optional:"true"`
|
||||
|
||||
apiKey string `help:"Your Asana Personal Access Token. Leave this blank to use the WTF_ASANA_TOKEN environment variable." values:"Your Asana Personal Access Token as a string" optional:"true"`
|
||||
|
||||
token string
|
||||
}
|
||||
|
||||
func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
|
||||
settings := Settings{
|
||||
Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig,
|
||||
globalConfig),
|
||||
projectId: ymlConfig.UString("projectId", ""),
|
||||
apiKey: ymlConfig.UString("apiKey", ""),
|
||||
workspaceId: ymlConfig.UString("workspaceId", ""),
|
||||
sections: utils.ToStrs(ymlConfig.UList("sections")),
|
||||
allUsers: ymlConfig.UBool("allUsers", false),
|
||||
mode: ymlConfig.UString("mode", ""),
|
||||
hideComplete: ymlConfig.UBool("hideComplete", false),
|
||||
}
|
||||
|
||||
return &settings
|
||||
}
|
259
modules/asana/widget.go
Normal file
259
modules/asana/widget.go
Normal file
@ -0,0 +1,259 @@
|
||||
package asana
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/rivo/tview"
|
||||
"github.com/wtfutil/wtf/utils"
|
||||
"github.com/wtfutil/wtf/view"
|
||||
)
|
||||
|
||||
type TaskType int
|
||||
|
||||
const (
|
||||
TASK_TYPE TaskType = iota
|
||||
TASK_SECTION
|
||||
TASK_BREAK
|
||||
)
|
||||
|
||||
type TaskItem struct {
|
||||
name string
|
||||
numSubtasks int32
|
||||
dueOn string
|
||||
id string
|
||||
url string
|
||||
taskType TaskType
|
||||
completed bool
|
||||
assignee string
|
||||
}
|
||||
|
||||
type Widget struct {
|
||||
view.ScrollableWidget
|
||||
|
||||
tasks []*TaskItem
|
||||
|
||||
mu sync.Mutex
|
||||
err error
|
||||
settings *Settings
|
||||
tviewApp *tview.Application
|
||||
}
|
||||
|
||||
func NewWidget(tviewApp *tview.Application, pages *tview.Pages, settings *Settings) *Widget {
|
||||
widget := &Widget{
|
||||
ScrollableWidget: view.NewScrollableWidget(tviewApp, pages, settings.Common),
|
||||
|
||||
tviewApp: tviewApp,
|
||||
settings: settings,
|
||||
}
|
||||
|
||||
widget.SetRenderFunction(widget.Render)
|
||||
widget.initializeKeyboardControls()
|
||||
|
||||
return widget
|
||||
}
|
||||
|
||||
/* -------------------- Exported Functions -------------------- */
|
||||
|
||||
func (widget *Widget) Refresh() {
|
||||
widget.tasks = nil
|
||||
widget.err = nil
|
||||
widget.SetItemCount(0)
|
||||
|
||||
widget.mu.Lock()
|
||||
defer widget.mu.Unlock()
|
||||
tasks, err := widget.Fetch(
|
||||
widget.settings.workspaceId,
|
||||
widget.settings.projectId,
|
||||
widget.settings.mode,
|
||||
widget.settings.sections,
|
||||
widget.settings.allUsers,
|
||||
)
|
||||
if err != nil {
|
||||
widget.err = err
|
||||
} else {
|
||||
widget.tasks = tasks
|
||||
widget.SetItemCount(len(tasks))
|
||||
}
|
||||
|
||||
widget.Render()
|
||||
}
|
||||
|
||||
func (widget *Widget) Render() {
|
||||
widget.Redraw(widget.content)
|
||||
}
|
||||
|
||||
func (widget *Widget) Fetch(workspaceId, projectId, mode string, sections []string, allUsers bool) ([]*TaskItem, error) {
|
||||
|
||||
availableModes := map[string]interface{}{
|
||||
"project": nil,
|
||||
"project_sections": nil,
|
||||
"workspace": nil,
|
||||
}
|
||||
|
||||
if _, ok := availableModes[mode]; !ok {
|
||||
return nil, fmt.Errorf("missing mode, or mode is invalid - please set to project, project_sections or workspace")
|
||||
}
|
||||
|
||||
if widget.settings.apiKey != "" {
|
||||
widget.settings.token = widget.settings.apiKey
|
||||
} else {
|
||||
widget.settings.token = os.Getenv("WTF_ASANA_TOKEN")
|
||||
}
|
||||
|
||||
if widget.settings.token == "" {
|
||||
return nil, fmt.Errorf("missing environment variable token or apikey config")
|
||||
}
|
||||
|
||||
subMode := mode
|
||||
if allUsers && mode != "workspace" {
|
||||
subMode += "_all"
|
||||
}
|
||||
|
||||
if projectId == "" && strings.HasPrefix(subMode, "project") {
|
||||
return nil, fmt.Errorf("missing project id")
|
||||
}
|
||||
|
||||
if workspaceId == "" && subMode == "workspace" {
|
||||
return nil, fmt.Errorf("missing workspace id")
|
||||
}
|
||||
|
||||
var tasks []*TaskItem
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(subMode, "project_sections"):
|
||||
tasks, err = fetchTasksFromProjectSections(widget.settings.token, projectId, sections, subMode)
|
||||
case strings.HasPrefix(subMode, "project"):
|
||||
tasks, err = fetchTasksFromProject(widget.settings.token, projectId, subMode)
|
||||
case subMode == "workspace":
|
||||
tasks, err = fetchTasksFromWorkspace(widget.settings.token, workspaceId, subMode)
|
||||
default:
|
||||
err = fmt.Errorf("no mode found")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tasks, nil
|
||||
|
||||
}
|
||||
|
||||
/* -------------------- Unexported Functions -------------------- */
|
||||
|
||||
func (widget *Widget) content() (string, string, bool) {
|
||||
|
||||
title := widget.CommonSettings().Title
|
||||
if widget.err != nil {
|
||||
return title, widget.err.Error(), true
|
||||
}
|
||||
|
||||
data := widget.tasks
|
||||
if len(data) == 0 {
|
||||
return title, "No data", false
|
||||
}
|
||||
|
||||
var str string
|
||||
|
||||
for idx, taskItem := range data {
|
||||
switch {
|
||||
case taskItem.taskType == TASK_TYPE:
|
||||
if widget.settings.hideComplete && taskItem.completed {
|
||||
continue
|
||||
}
|
||||
|
||||
rowColor := widget.RowColor(idx)
|
||||
|
||||
completed := "[ []"
|
||||
if taskItem.completed {
|
||||
completed = "[x[]"
|
||||
}
|
||||
|
||||
row := ""
|
||||
|
||||
if widget.settings.allUsers && taskItem.assignee != "" {
|
||||
row = fmt.Sprintf(
|
||||
"[%s] %s %s: %s",
|
||||
rowColor,
|
||||
completed,
|
||||
taskItem.assignee,
|
||||
taskItem.name,
|
||||
)
|
||||
} else {
|
||||
row = fmt.Sprintf(
|
||||
"[%s] %s %s",
|
||||
rowColor,
|
||||
completed,
|
||||
taskItem.name,
|
||||
)
|
||||
}
|
||||
|
||||
if taskItem.numSubtasks > 0 {
|
||||
row += fmt.Sprintf(" (%d)", taskItem.numSubtasks)
|
||||
}
|
||||
|
||||
if taskItem.dueOn != "" {
|
||||
row += fmt.Sprintf(" due: %s", taskItem.dueOn)
|
||||
}
|
||||
|
||||
row += " [white]"
|
||||
|
||||
str += utils.HighlightableHelper(widget.View, row, idx, len(taskItem.name))
|
||||
|
||||
case taskItem.taskType == TASK_SECTION:
|
||||
if idx > 1 {
|
||||
row := "[white] "
|
||||
|
||||
str += utils.HighlightableHelper(widget.View, row, idx, len(taskItem.name))
|
||||
}
|
||||
row := fmt.Sprintf(
|
||||
"[white] %s [white]",
|
||||
taskItem.name,
|
||||
)
|
||||
|
||||
str += utils.HighlightableHelper(widget.View, row, idx, len(taskItem.name))
|
||||
|
||||
row = "[white] "
|
||||
|
||||
str += utils.HighlightableHelper(widget.View, row, idx, len(taskItem.name))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return title, str, false
|
||||
|
||||
}
|
||||
|
||||
func (widget *Widget) openTask() {
|
||||
sel := widget.GetSelected()
|
||||
|
||||
if sel >= 0 && widget.tasks != nil && sel < len(widget.tasks) {
|
||||
task := widget.tasks[sel]
|
||||
if task.taskType == TASK_TYPE && task.url != "" {
|
||||
utils.OpenFile(task.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (widget *Widget) toggleTaskCompletion() {
|
||||
sel := widget.GetSelected()
|
||||
|
||||
if sel >= 0 && widget.tasks != nil && sel < len(widget.tasks) {
|
||||
task := widget.tasks[sel]
|
||||
if task.taskType == TASK_TYPE {
|
||||
widget.mu.Lock()
|
||||
|
||||
err := toggleTaskCompletionById(widget.settings.token, task.id)
|
||||
if err != nil {
|
||||
widget.err = err
|
||||
}
|
||||
|
||||
widget.mu.Unlock()
|
||||
widget.Refresh()
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user