Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.6.1 #53

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
510a4d6
edited something [spent 7m0s]
advdv Jul 7, 2015
915f333
started on v0.6.1 [spent 9m20s]
advdv Jul 9, 2015
1166224
added a snow index to get informed of file events [spent 0]
advdv Jul 9, 2015
1606cd1
less racy timer implementation [spent 5h3m26s]
advdv Jul 10, 2015
9fe1856
working version without racy behaviour [spent 1m6s]
advdv Jul 10, 2015
d743183
made date of staged changed available to status command [spent 15m14s]
advdv Jul 12, 2015
e483ca3
first distributor implementation [spent 25m52s]
advdv Jul 12, 2015
7ccebf9
basic integration with status command [spent 58s]
advdv Jul 12, 2015
6358437
implemented upto logic for extraction [spent 3m6s]
advdv Jul 12, 2015
586cd16
fixed status to use upto parameter, keeper saves are only triggered o…
advdv Jul 12, 2015
2478340
integrated lazy file indexer [spent 2m28s]
advdv Jul 13, 2015
01b6f30
Switched to a block based file tracking method
advdv Jul 13, 2015
a0c80dc
added tests for basic distributor
advdv Jul 13, 2015
72ebcef
distributor can now be reset [spent 18s]
advdv Jul 13, 2015
ceb445c
staged time is now visible on status [spent 1m16s]
advdv Jul 13, 2015
a01f2c0
distributor now allows for staging up to a certain time and resetting…
advdv Jul 13, 2015
c4669d0
basic staging mechanism [spent 6m24s]
advdv Jul 13, 2015
f945aeb
added distr tests and more save emits [spent 2m12s]
advdv Jul 13, 2015
600eeb9
added stage& unstage command, staging now resets
advdv Jul 14, 2015
797e6c4
buggy reset implementation [spent 0]
advdv Jul 14, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Timeglass


![Timeglass Screenshot](/docs/screenshot.png?raw=true "Timeglass Screenshot")

Fully automated time tracking for Git repositories. It uses hooks and file monitoring to make sure you'll never forget to start or stop your timer ever again. It is written in [Go](http://golang.org/) and runs 100% on your own workstation: no internet or account registration required.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.6.0
0.6.1
32 changes: 26 additions & 6 deletions command/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import (
"io"
"net/http"
"net/url"
"strconv"
"strings"

"github.com/timeglass/glass/_vendor/github.com/hashicorp/errwrap"

daemon "github.com/timeglass/glass/glass-daemon"
"github.com/timeglass/glass/timer"
"github.com/timeglass/glass/vcs"
)

var ErrRequestFailed = errors.New("Couldn't reach background service, did you install it using 'glass install'?")
Expand All @@ -31,8 +33,8 @@ func NewClient() *Client {
}

func (c *Client) Call(method string, params url.Values) ([]byte, error) {
loc := fmt.Sprintf("%s/api/%s?%s", c.endpoint, method, params.Encode())
resp, err := c.Get(loc)
loc := fmt.Sprintf("%s/api/%s", c.endpoint, method)
resp, err := c.Post(loc, "application/x-www-form-urlencoded", bytes.NewBuffer([]byte(params.Encode())))
if err != nil {
return nil, ErrRequestFailed
}
Expand Down Expand Up @@ -101,9 +103,11 @@ func (c *Client) DeleteTimer(dir string) error {
return nil
}

func (c *Client) ResetTimer(dir string) error {
func (c *Client) ResetTimer(dir string, staged, unstaged bool) error {
params := url.Values{}
params.Set("dir", dir)
params.Set("staged", strconv.FormatBool(staged))
params.Set("unstaged", strconv.FormatBool(unstaged))

_, err := c.Call("timers.reset", params)
if err != nil {
Expand All @@ -113,6 +117,22 @@ func (c *Client) ResetTimer(dir string) error {
return nil
}

func (c *Client) StageTimer(dir string, files map[string]*vcs.StagedFile) error {
params := url.Values{}
params.Set("dir", dir)

for _, sf := range files {
params.Add("files", fmt.Sprintf("%s:%d", sf.Path(), sf.Date().UnixNano()))
}

_, err := c.Call("timers.stage", params)
if err != nil {
return err
}

return nil
}

func (c *Client) PauseTimer(dir string) error {
params := url.Values{}
params.Set("dir", dir)
Expand All @@ -125,8 +145,8 @@ func (c *Client) PauseTimer(dir string) error {
return nil
}

func (c *Client) ReadTimer(dir string) (*daemon.Timer, error) {
timers := []*daemon.Timer{}
func (c *Client) ReadTimer(dir string) (*timer.Timer, error) {
timers := []*timer.Timer{}
params := url.Values{}
params.Set("dir", dir)

Expand Down
2 changes: 1 addition & 1 deletion command/punch.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (c *Punch) Run(ctx *cli.Context) error {
}

if input == "" {
return fmt.Errorf("Please provide the time you spent as the first argument")
return fmt.Errorf("Please provide the time you spent as the first argument or over Stdin")
}

t, err := time.ParseDuration(input)
Expand Down
4 changes: 2 additions & 2 deletions command/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/timeglass/glass/_vendor/github.com/mattn/go-isatty"

"github.com/timeglass/glass/config"
daemon "github.com/timeglass/glass/glass-daemon"
"github.com/timeglass/glass/timer"
"github.com/timeglass/glass/vcs"
)

Expand Down Expand Up @@ -48,7 +48,7 @@ func (c *Push) Run(ctx *cli.Context) error {
return errwrap.Wrapf("Failed to fetch current working dir: {{err}}", err)
}

sysdir, err := daemon.SystemTimeglassPath()
sysdir, err := timer.SystemTimeglassPath()
if err != nil {
return errwrap.Wrapf(fmt.Sprintf("Failed to get system config path: {{err}}"), err)
}
Expand Down
7 changes: 5 additions & 2 deletions command/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ func (c *Reset) Usage() string {
}

func (c *Reset) Flags() []cli.Flag {
return []cli.Flag{}
return []cli.Flag{
cli.BoolFlag{Name: "staged", Usage: "reset staged time as well"},
cli.BoolFlag{Name: "keep-unstaged", Usage: "prevent the removal of unstaged time"},
}
}

func (c *Reset) Action() func(ctx *cli.Context) {
Expand All @@ -52,7 +55,7 @@ func (c *Reset) Run(ctx *cli.Context) error {
c.Printf("Resetting timer to 0s...")

client := NewClient()
err = client.ResetTimer(vc.Root())
err = client.ResetTimer(vc.Root(), ctx.Bool("staged"), !ctx.Bool("keep-unstaged"))
if err != nil {
return errwrap.Wrapf(fmt.Sprintf("Failed to reset timer: {{err}}"), err)
}
Expand Down
82 changes: 82 additions & 0 deletions command/stage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package command

import (
"fmt"
"os"
"path/filepath"

"github.com/timeglass/glass/_vendor/github.com/codegangsta/cli"
"github.com/timeglass/glass/_vendor/github.com/hashicorp/errwrap"

"github.com/timeglass/glass/vcs"
)

type Stage struct {
*command
}

func NewStage() *Stage {
return &Stage{newCommand()}
}

func (c *Stage) Name() string {
return "stage"
}

func (c *Stage) Description() string {
return fmt.Sprintf("...")
}

func (c *Stage) Usage() string {
return "..."
}

func (c *Stage) Flags() []cli.Flag {
return []cli.Flag{
cli.BoolFlag{Name: "all,A", Usage: "Stage time spend on all files currently staged in git"},
}
}

func (c *Stage) Action() func(ctx *cli.Context) {
return c.command.Action(c.Run)
}

func (c *Stage) Run(ctx *cli.Context) error {
dir, err := os.Getwd()
if err != nil {
return errwrap.Wrapf("Failed to fetch current working dir: {{err}}", err)
}

vc, err := vcs.GetVCS(dir)
if err != nil {
return errwrap.Wrapf("Failed to setup VCS: {{err}}", err)
}

staged, err := vc.Staging()
if err != nil {
return errwrap.Wrapf(fmt.Sprintf("Failed to get staged files from the VCS: {{err}}"), err)
}

client := NewClient()

if ctx.Bool("all") {
c.Printf("Staging time for %d files:", len(staged))
for _, f := range staged {
rel, err := filepath.Rel(vc.Root(), f.Path())
if err != nil {
c.Printf("Failed to determine relative path for '%s'", f.Path())
}

c.Printf(" %s: %s", f.Date(), rel)
}

err := client.StageTimer(vc.Root(), staged)
if err != nil {
return errwrap.Wrapf(fmt.Sprintf("Failed to stage files: {{err}}"), err)
}
} else {
c.Fatal("Not yet implemented")
}

return nil
}
61 changes: 50 additions & 11 deletions command/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package command
import (
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"text/template"
Expand All @@ -11,7 +12,7 @@ import (
"github.com/timeglass/glass/_vendor/github.com/hashicorp/errwrap"

"github.com/timeglass/glass/config"
daemon "github.com/timeglass/glass/glass-daemon"
"github.com/timeglass/glass/timer"
"github.com/timeglass/glass/vcs"
)

Expand Down Expand Up @@ -57,7 +58,7 @@ func (c *Status) Run(ctx *cli.Context) error {
return errwrap.Wrapf("Failed to setup VCS: {{err}}", err)
}

sysdir, err := daemon.SystemTimeglassPath()
sysdir, err := timer.SystemTimeglassPath()
if err != nil {
return errwrap.Wrapf(fmt.Sprintf("Failed to get system config path: {{err}}"), err)
}
Expand All @@ -82,17 +83,17 @@ func (c *Status) Run(ctx *cli.Context) error {
c.Println("A new version is available, please upgrade: https://github.com/timeglass/glass/releases")
}

//fetch information on the timer specific to this directory
//fetch information on the timer specific to this directory.
c.Printf("Fetching timer info...")
timer, err := client.ReadTimer(vc.Root())
t, err := client.ReadTimer(vc.Root())
if err != nil {
return errwrap.Wrapf(fmt.Sprintf("Failed to fetch timer: {{err}}"), err)
}

if reason := timer.HasFailed(); reason != "" {
c.Printf("Timer has failed: %s", reason)
if terr := t.Error(); terr != nil {
c.Printf("Timer has failed: %s", terr)
} else {
if timer.IsPaused() {
if t.IsPaused() {
c.Printf("Timer is currently: PAUSED")
} else {
c.Printf("Timer is currently: RUNNING")
Expand All @@ -107,21 +108,59 @@ func (c *Status) Run(ctx *cli.Context) error {
//we got some template specified
if tmpls != "" {

//parse temlate and only report error if we're talking to a human
//parse template and only report error if we're talking to a human
tmpl, err := template.New("commit-msg").Parse(tmpls)
if err != nil {
return errwrap.Wrapf(fmt.Sprintf("Failed to parse commit_message: '%s' in configuration as a text/template: {{err}}", conf.CommitMessage), err)
}

//execute template and write to stdout
err = tmpl.Execute(os.Stdout, timer.Time())
err = tmpl.Execute(os.Stdout, t.Time())
if err != nil {
return errwrap.Wrapf(fmt.Sprintf("Failed to execute commit_message: template for time '%s': {{err}}", timer.Time()), err)
return errwrap.Wrapf(fmt.Sprintf("Failed to execute commit_message: template for time '%s': {{err}}", t.Time()), err)
}

} else {
//just print
c.Printf("Timer reads: %s", timer.Time())
c.Printf("Total time reads: %s", t.Time())

all := t.Distributor().Timelines()
unstaged := map[string]*timer.Timeline{}
staged := map[string]*timer.Timeline{}
for path, tl := range all {
if tl.Unstaged() != 0 {
unstaged[path] = tl
}

if tl.Staged() != 0 {
staged[path] = tl
}
}

if len(staged) > 0 {
c.Printf("Staged time:")
for path, tl := range staged {
rel, err := filepath.Rel(vc.Root(), path)
if err != nil {
c.Printf("Failed to rel dir: %s", err)
}

c.Printf("- %s: %s", rel, tl.Staged())
}
}

if len(unstaged) > 0 {
c.Printf("Unstaged time:")
for path, tl := range unstaged {
rel, err := filepath.Rel(vc.Root(), path)
if err != nil {
c.Printf("Failed to rel dir: %s", err)
}

c.Printf("- %s: %s", rel, tl.Unstaged())
}
}

}

return nil
Expand Down
Loading