@ -1,2 +1,25 @@ |
|||
# wtt |
|||
Work Time Tracker - console time tracking app |
|||
|
|||
|
|||
### Use |
|||
Create new project |
|||
``` |
|||
./wtt new ProjectName |
|||
``` |
|||
|
|||
|
|||
Start working on a project |
|||
``` |
|||
./wtt start ProjectName |
|||
``` |
|||
|
|||
Stop working on the current project |
|||
``` |
|||
./wtt stop |
|||
``` |
|||
|
|||
List projects |
|||
``` |
|||
./wtt list |
|||
``` |
@ -0,0 +1,9 @@ |
|||
package main |
|||
|
|||
import "log" |
|||
|
|||
func check(err error) { |
|||
if err != nil { |
|||
log.Println(err) |
|||
} |
|||
} |
@ -0,0 +1,97 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"fmt" |
|||
"log" |
|||
"os" |
|||
"os/user" |
|||
"time" |
|||
|
|||
"github.com/fatih/color" |
|||
) |
|||
|
|||
var directoryPath string |
|||
var filePath string |
|||
|
|||
func main() { |
|||
usr, err := user.Current() |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
directoryPath = usr.HomeDir + "/.wtt" |
|||
filePath = directoryPath + "/work.json" |
|||
readProjects() |
|||
|
|||
//get the command line parameters
|
|||
if len(os.Args) > 1 { |
|||
switch os.Args[1] { |
|||
case "new": |
|||
//create project os.Args[2]
|
|||
if len(os.Args) > 2 { |
|||
projectName := os.Args[2] |
|||
color.Green("creating new project: " + projectName) |
|||
err := newProject(projectName) |
|||
if err != nil { |
|||
break |
|||
} |
|||
saveWork() |
|||
} else { |
|||
color.Red("No project name specified") |
|||
} |
|||
break |
|||
case "list": |
|||
//list projects
|
|||
listProjects() |
|||
break |
|||
case "start": |
|||
//check if already there is any project started
|
|||
if work.CurrentProjectName != "" { |
|||
color.Red("Can not start project, already project " + work.CurrentProjectName + " running") |
|||
break |
|||
} |
|||
//start project os.Args[2]
|
|||
if len(os.Args) > 2 { |
|||
projectName := os.Args[2] |
|||
i := getProjectIByName(projectName) |
|||
if i < 0 { |
|||
color.Red("Project name: " + projectName + ", no exists") |
|||
break |
|||
} |
|||
var newStreak Streak |
|||
newStreak.Start = time.Now() |
|||
work.Projects[i].Streaks = append(work.Projects[i].Streaks, newStreak) |
|||
work.CurrentProjectName = projectName |
|||
saveWork() |
|||
fmt.Println("starting to work in project " + work.Projects[i].Name) |
|||
} |
|||
break |
|||
case "stop": |
|||
if work.CurrentProjectName == "" { |
|||
color.Red("no project started to stop") |
|||
break |
|||
} |
|||
i := getProjectIByName(work.CurrentProjectName) |
|||
if i < 0 { |
|||
color.Red("Project name: " + work.CurrentProjectName + ", no exists") |
|||
break |
|||
} |
|||
j := len(work.Projects[i].Streaks) - 1 |
|||
work.Projects[i].Streaks[j].End = time.Now() |
|||
work.Projects[i].Streaks[j].Duration = time.Now().Sub(work.Projects[i].Streaks[j].Start) |
|||
work.CurrentProjectName = "" |
|||
saveWork() |
|||
fmt.Print("Worked ") |
|||
fmt.Print(work.Projects[i].Streaks[j].Duration) |
|||
fmt.Println(" in the project " + work.Projects[i].Name) |
|||
|
|||
//stop project os.Args[2]
|
|||
break |
|||
default: |
|||
color.Red("no option selected") |
|||
os.Exit(1) |
|||
} |
|||
} else { |
|||
color.Red("no option selected") |
|||
os.Exit(1) |
|||
} |
|||
} |
@ -0,0 +1,98 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"errors" |
|||
"fmt" |
|||
"io/ioutil" |
|||
"os" |
|||
"strconv" |
|||
"time" |
|||
|
|||
"github.com/fatih/color" |
|||
) |
|||
|
|||
type Project struct { |
|||
Name string `json:"name"` |
|||
Streaks []Streak `json:"streaks"` |
|||
} |
|||
type Streak struct { |
|||
Start time.Time `json:"start"` |
|||
End time.Time `json:"end"` |
|||
Duration time.Duration `json:"duration"` |
|||
Description string `json:"description"` |
|||
} |
|||
type Work struct { |
|||
Projects []Project `json:"projects"` |
|||
CurrentProjectName string `json:"currentProjectName"` |
|||
} |
|||
|
|||
var work Work |
|||
|
|||
func readProjects() { |
|||
file, err := ioutil.ReadFile(filePath) |
|||
if err != nil { |
|||
//file not exists, create directory and file
|
|||
color.Yellow(directoryPath + " not exists, creating directory") |
|||
_ = os.Mkdir(directoryPath, os.ModePerm) |
|||
saveWork() |
|||
} |
|||
content := string(file) |
|||
json.Unmarshal([]byte(content), &work) |
|||
} |
|||
|
|||
func saveWork() { |
|||
|
|||
jsonProjects, err := json.Marshal(work) |
|||
check(err) |
|||
err = ioutil.WriteFile(filePath, jsonProjects, 0644) |
|||
check(err) |
|||
} |
|||
func getProjectIByName(name string) int { |
|||
for i, project := range work.Projects { |
|||
if project.Name == name { |
|||
return i |
|||
} |
|||
} |
|||
return -1 |
|||
} |
|||
func projectExist(name string) bool { |
|||
for _, project := range work.Projects { |
|||
if project.Name == name { |
|||
return true |
|||
} |
|||
} |
|||
return false |
|||
} |
|||
func newProject(name string) error { |
|||
//first, check if the project name is not taken yet
|
|||
if projectExist(name) { |
|||
color.Red("Project name: " + name + ", already exists") |
|||
return errors.New("project name already exist") |
|||
} |
|||
var newProject Project |
|||
newProject.Name = name |
|||
work.Projects = append(work.Projects, newProject) |
|||
return nil |
|||
} |
|||
func listProjects() { |
|||
fmt.Println("") |
|||
fmt.Println("") |
|||
fmt.Println("Listing projects") |
|||
fmt.Println("") |
|||
for k, project := range work.Projects { |
|||
fmt.Println("project " + strconv.Itoa(k)) |
|||
fmt.Print("name: ") |
|||
color.Blue(project.Name) |
|||
for k2, streak := range project.Streaks { |
|||
fmt.Println(" streak: " + strconv.Itoa(k2)) |
|||
fmt.Print(" Start:") |
|||
fmt.Println(streak.Start) |
|||
fmt.Print(" End:") |
|||
fmt.Println(streak.End) |
|||
fmt.Print(" Duration:") |
|||
fmt.Println(streak.Duration) |
|||
} |
|||
fmt.Println("") |
|||
} |
|||
} |