Skip to content

Commit

Permalink
feat: Add support for multiple workspaces
Browse files Browse the repository at this point in the history
* Add support for multiple workspaces

closes #22
  • Loading branch information
danstis authored Apr 2, 2019
1 parent 51d0541 commit 64924df
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 22 deletions.
26 changes: 26 additions & 0 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,32 @@ environment:
install:
- choco install gitversion.portable -y
- rm C:\Tools\GitVersion\GitVersion.exe
- ps: |
Remove-Item 'C:\go' -Recurse -Force -ErrorAction Ignore
Remove-Item 'C:\go-x86' -Recurse -Force -ErrorAction Ignore
Write-Host "Downloading..."
$goDistPath = "$env:TEMP\go1.12.windows-386.zip"
(New-Object Net.WebClient).DownloadFile('https://storage.googleapis.com/golang/go1.12.windows-386.zip', $goDistPath)
Write-Host "Unpacking..."
7z x $goDistPath -oC:\go1120-x86 | Out-Null
[IO.Directory]::Move('C:\go1120-x86\go', 'C:\go112-x86')
Remove-Item 'C:\go1120-x86' -Recurse -Force
del $goDistPath
Write-Host "Downloading..."
$goDistPath = "$env:TEMP\go1.12.windows-amd64.zip"
(New-Object Net.WebClient).DownloadFile('https://storage.googleapis.com/golang/go1.12.windows-amd64.zip', $goDistPath)
Write-Host "Unpacking..."
7z x $goDistPath -oC:\go1120-x64 | Out-Null
[IO.Directory]::Move('C:\go1120-x64\go', 'C:\go112')
Remove-Item 'C:\go1120-x64' -Recurse -Force
del $goDistPath
cmd /c mklink /J C:\go C:\go112
cmd /c mklink /J C:\go-x86 C:\go112-x86
- mkdir %GOPATH%\bin
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
- go version
Expand Down
2 changes: 0 additions & 2 deletions config.toml.default
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
token = "" # Retrieve your token from https://toggl.com/app/profile
email = "" # Your Email address to send with requests to the toggl API
userId = "" # Get from URL segment when selecting the individual user in this report: https://toggl.com/app/reports/weekly
workspaceId = "" # Get from URL segment when opening settings of the workspace from this page: https://toggl.com/app/workspaces
syncInterval = 5 # Sync interval in minutes. This controls how often the app checks for updates
highlightThreshold = 40 # Threshold in hours to control highlighting
99 changes: 79 additions & 20 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,17 @@ type togglTime struct {
// Settings contains the application configuration
type Settings struct {
Token string `toml:"token"`
UserID string `toml:"userId"`
WorkspaceID string `toml:"workspaceId"`
Email string `toml:"email"`
SyncInterval int `toml:"syncInterval"`
HighlightThreshold int `toml:"highlightThreshold"`
UserID string
Workspaces []Workspaces
}

// Workspaces stores the available toggl workspaces for the user
type Workspaces struct {
ID int32 `json:"id"`
Name string `json:"name"`
}

// Main entry point for the app.
Expand All @@ -40,34 +46,51 @@ func main() {

func onReady() {
var t togglTime
// Get the settings
var config Settings
_, err := toml.DecodeFile(configFile, &config)
if err != nil {
log.Fatalf("Failed to read configuration file: %v", err)
}
err = config.getUserDetail()
if err != nil {
log.Printf("Failed to get Toggl user details: %v\n", err)
}
log.Printf("- Workspaces:%v\n", config.Workspaces)

// Configure the systray item
updateIcon(0, config.HighlightThreshold)
mVersion := systray.AddMenuItem(fmt.Sprintf("Toggl Weekly Tracker v%v", Version), "Version")
mVersion.Disable()
systray.AddSeparator()
mTime := systray.AddMenuItem(fmt.Sprintf("This week: %d:%02d", 0, 0), "Current timer")
systray.SetTitle("Toggl Weekly Time")
menuItems := make(map[int32]*systray.MenuItem)
for _, item := range config.Workspaces {
menuItems[item.ID] = systray.AddMenuItem(fmt.Sprintf("%s: %d:%02d", item.Name, 0, 0), item.Name)
}
mQuit := systray.AddMenuItem("Quit", "Quit the app")
go func() {
<-mQuit.ClickedCh
log.Println("Applicaiton exiting...")
systray.Quit()
}()

systray.SetTitle("Toggl Weekly Time")

for {
t, err = getWeeklyTime(&config)
if err != nil {
log.Printf("Failed to get Toggl details: %v\n", err)
totalTime := togglTime{hours: 0, minutes: 0}
for _, item := range config.Workspaces {
t, err = getWeeklyTime(&config, fmt.Sprint(item.ID))
if err != nil {
log.Printf("Failed to get Toggl details: %v\n", err)
}
log.Printf("- %s [%s] time: %d:%02d\n", item.Name, fmt.Sprint(item.ID), t.hours, t.minutes)
// Set the title of the menuItem to contain the time for the individual workspace
menuItems[item.ID].SetTitle(fmt.Sprintf("%s: %d:%02d", item.Name, t.hours, t.minutes))
totalTime.add(t)
}
log.Printf("- Got new time %d:%02d\n", t.hours, t.minutes) // TODO: remove this when logging goes away
updateIcon(int(t.hours), config.HighlightThreshold)
mTime.SetTitle(fmt.Sprintf("This week: %d:%02d", t.hours, t.minutes))
systray.SetTooltip(fmt.Sprintf("Toggl time tracker: %d:%02d", t.hours, t.minutes))

log.Printf("- Got new total time %d:%02d\n", totalTime.hours, totalTime.minutes)
updateIcon(int(totalTime.hours), config.HighlightThreshold)
systray.SetTooltip(fmt.Sprintf("Toggl time tracker: %d:%02d", totalTime.hours, totalTime.minutes))
time.Sleep(time.Duration(config.SyncInterval) * time.Minute)
}
}
Expand All @@ -76,12 +99,37 @@ func onExit() {
// Cleaning stuff here.
}

func getWeeklyTime(c *Settings) (togglTime, error) {
closedTime, err := getClosedTimeEntries(c)
func (c *Settings) getUserDetail() error {
type UserResponse struct {
Data struct {
ID int32 `json:"id"`
Workspaces []Workspaces `json:"workspaces"`
} `json:"data"`
}
var ur UserResponse
toggl := resty.New().SetHostURL("https://www.toggl.com/api/v8").SetBasicAuth(c.Token, "api_token")

_, err := toggl.R().
SetQueryParams(map[string]string{
"user_agent": c.Email,
}).
SetResult(&ur).
Get("/me?with_related_data=true")
if err != nil {
return fmt.Errorf("unable to get user details from the Toggl API: %v", err)
}

c.UserID = fmt.Sprint(ur.Data.ID)
c.Workspaces = ur.Data.Workspaces
return nil
}

func getWeeklyTime(c *Settings, w string) (togglTime, error) {
closedTime, err := getClosedTimeEntries(c, w)
if err != nil {
return togglTime{}, fmt.Errorf("failed to get closed time entries: %v", err)
}
openTime, err := getOpenTimeEntry(c)
openTime, err := getOpenTimeEntry(c, w)
if err != nil {
return togglTime{}, fmt.Errorf("failed to get open time entry: %v", err)
}
Expand All @@ -92,7 +140,7 @@ func getWeeklyTime(c *Settings) (togglTime, error) {
}, nil
}

func getClosedTimeEntries(c *Settings) (time.Duration, error) {
func getClosedTimeEntries(c *Settings, w string) (time.Duration, error) {
type WeeklyResponse struct {
TotalGrand int `json:"total_grand"`
}
Expand All @@ -103,7 +151,7 @@ func getClosedTimeEntries(c *Settings) (time.Duration, error) {
_, err := toggleReports.R().
SetQueryParams(map[string]string{
"user_agent": c.Email,
"workspace_id": c.WorkspaceID,
"workspace_id": w,
"user_ids": c.UserID,
"since": getLastMonday(),
}).
Expand All @@ -116,9 +164,10 @@ func getClosedTimeEntries(c *Settings) (time.Duration, error) {
return time.Duration(ct.TotalGrand) * time.Millisecond, nil
}

func getOpenTimeEntry(c *Settings) (time.Duration, error) {
func getOpenTimeEntry(c *Settings, w string) (time.Duration, error) {
type TimeEntriesResponse struct {
Data struct {
WID int32 `json:"wid"`
Duration int32 `json:"duration"`
} `json:"data"`
}
Expand All @@ -129,7 +178,7 @@ func getOpenTimeEntry(c *Settings) (time.Duration, error) {
_, err := toggl.R().
SetQueryParams(map[string]string{
"user_agent": c.Email,
"wid": c.WorkspaceID,
"wid": w,
}).
SetResult(&ot).
Get("/time_entries/current")
Expand All @@ -138,7 +187,8 @@ func getOpenTimeEntry(c *Settings) (time.Duration, error) {
}

// if the returned duration is not negative then there is no open entry.
if ot.Data.Duration >= 0 {
// we also filter entries that do not match the workspace here.
if ot.Data.Duration >= 0 || fmt.Sprint(ot.Data.WID) != w {
return 0, nil
}

Expand Down Expand Up @@ -169,6 +219,15 @@ func getMinutes(t time.Duration) int {
return int(m)
}

func (t *togglTime) add(n togglTime) {
t.minutes += n.minutes
t.hours += n.hours
if t.minutes >= 60 {
t.hours++
t.minutes -= 60
}
}

func updateIcon(hours, threshold int) {
icon, err := createIcon(16, 16, hours, threshold)
if err != nil {
Expand Down

0 comments on commit 64924df

Please sign in to comment.