mirror of
https://github.com/Threnklyn/zeit.git
synced 2026-05-18 12:53:30 +02:00
Update .editorconfig, run gofumpt -w ./
This commit is contained in:
@@ -4,8 +4,6 @@ root = true
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
max_line = 80
|
||||
|
||||
|
||||
+143
-142
@@ -1,201 +1,202 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"strings"
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/jinzhu/now"
|
||||
// "github.com/gookit/color"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/now"
|
||||
"github.com/shopspring/decimal"
|
||||
// "github.com/gookit/color"
|
||||
)
|
||||
|
||||
type Statistic struct {
|
||||
Hours decimal.Decimal
|
||||
Project string
|
||||
Color (func(...interface {}) string)
|
||||
Hours decimal.Decimal
|
||||
Project string
|
||||
Color (func(...interface{}) string)
|
||||
}
|
||||
|
||||
type WeekStatistics map[string][]Statistic
|
||||
|
||||
type Week struct {
|
||||
Statistics WeekStatistics
|
||||
Statistics WeekStatistics
|
||||
}
|
||||
|
||||
type Month struct {
|
||||
Name string
|
||||
Weeks [5]Week
|
||||
Name string
|
||||
Weeks [5]Week
|
||||
}
|
||||
|
||||
type Calendar struct {
|
||||
Months [12]Month
|
||||
Distribution map[string]Statistic
|
||||
TotalHours decimal.Decimal
|
||||
Months [12]Month
|
||||
Distribution map[string]Statistic
|
||||
TotalHours decimal.Decimal
|
||||
}
|
||||
|
||||
func NewCalendar(entries []Entry) (Calendar, error) {
|
||||
cal := Calendar{}
|
||||
cal := Calendar{}
|
||||
|
||||
cal.Distribution = make(map[string]Statistic)
|
||||
cal.Distribution = make(map[string]Statistic)
|
||||
|
||||
projects := make(map[string]Project)
|
||||
projects := make(map[string]Project)
|
||||
|
||||
for _, entry := range entries {
|
||||
var entryFinish time.Time
|
||||
endOfBeginDay := now.With(entry.Begin).EndOfDay()
|
||||
sameDayHours := decimal.NewFromInt(0)
|
||||
nextDayHours := decimal.NewFromInt(0)
|
||||
for _, entry := range entries {
|
||||
var entryFinish time.Time
|
||||
endOfBeginDay := now.With(entry.Begin).EndOfDay()
|
||||
sameDayHours := decimal.NewFromInt(0)
|
||||
nextDayHours := decimal.NewFromInt(0)
|
||||
|
||||
projectId := GetIdFromName(entry.Project)
|
||||
projectId := GetIdFromName(entry.Project)
|
||||
|
||||
if projects[projectId].Name == "" {
|
||||
project, err := database.GetProject(entry.User, entry.Project)
|
||||
if err != nil {
|
||||
return cal, err
|
||||
}
|
||||
if projects[projectId].Name == "" {
|
||||
project, err := database.GetProject(entry.User, entry.Project)
|
||||
if err != nil {
|
||||
return cal, err
|
||||
}
|
||||
|
||||
projects[projectId] = project
|
||||
}
|
||||
projects[projectId] = project
|
||||
}
|
||||
|
||||
if entry.Finish.IsZero() {
|
||||
entryFinish = time.Now()
|
||||
} else {
|
||||
entryFinish = entry.Finish
|
||||
}
|
||||
if entry.Finish.IsZero() {
|
||||
entryFinish = time.Now()
|
||||
} else {
|
||||
entryFinish = entry.Finish
|
||||
}
|
||||
|
||||
/*
|
||||
* Apparently the activity end is on a new day.
|
||||
* This means we have to split the activity across two days.
|
||||
*/
|
||||
if endOfBeginDay.Before(entryFinish) == true {
|
||||
startOfFinishDay := now.With(entryFinish).BeginningOfDay()
|
||||
/*
|
||||
* Apparently the activity end is on a new day.
|
||||
* This means we have to split the activity across two days.
|
||||
*/
|
||||
if endOfBeginDay.Before(entryFinish) == true {
|
||||
startOfFinishDay := now.With(entryFinish).BeginningOfDay()
|
||||
|
||||
sameDayDuration := endOfBeginDay.Sub(entry.Begin)
|
||||
sameDay := sameDayDuration.Hours()
|
||||
sameDayHours = decimal.NewFromFloat(sameDay)
|
||||
sameDayDuration := endOfBeginDay.Sub(entry.Begin)
|
||||
sameDay := sameDayDuration.Hours()
|
||||
sameDayHours = decimal.NewFromFloat(sameDay)
|
||||
|
||||
nextDayDuration := entryFinish.Sub(startOfFinishDay)
|
||||
nextDay := nextDayDuration.Hours()
|
||||
nextDayHours = decimal.NewFromFloat(nextDay)
|
||||
nextDayDuration := entryFinish.Sub(startOfFinishDay)
|
||||
nextDay := nextDayDuration.Hours()
|
||||
nextDayHours = decimal.NewFromFloat(nextDay)
|
||||
|
||||
} else {
|
||||
sameDayDuration := entryFinish.Sub(entry.Begin)
|
||||
sameDay := sameDayDuration.Hours()
|
||||
sameDayHours = decimal.NewFromFloat(sameDay)
|
||||
}
|
||||
} else {
|
||||
sameDayDuration := entryFinish.Sub(entry.Begin)
|
||||
sameDay := sameDayDuration.Hours()
|
||||
sameDayHours = decimal.NewFromFloat(sameDay)
|
||||
}
|
||||
|
||||
if sameDayHours.GreaterThan(decimal.NewFromInt(0)) {
|
||||
month, weeknumber := GetISOWeekInMonth(entry.Begin)
|
||||
month0 := month - 1
|
||||
weeknumber0 := weeknumber - 1
|
||||
weekday := entry.Begin.Weekday()
|
||||
weekdayName := weekday.String()[:2]
|
||||
if sameDayHours.GreaterThan(decimal.NewFromInt(0)) {
|
||||
month, weeknumber := GetISOWeekInMonth(entry.Begin)
|
||||
month0 := month - 1
|
||||
weeknumber0 := weeknumber - 1
|
||||
weekday := entry.Begin.Weekday()
|
||||
weekdayName := weekday.String()[:2]
|
||||
|
||||
stat := Statistic{
|
||||
Hours: sameDayHours,
|
||||
Project: entry.Project,
|
||||
Color: GetColorFnFromHex(projects[projectId].Color),
|
||||
}
|
||||
stat := Statistic{
|
||||
Hours: sameDayHours,
|
||||
Project: entry.Project,
|
||||
Color: GetColorFnFromHex(projects[projectId].Color),
|
||||
}
|
||||
|
||||
if cal.Months[month0].Weeks[weeknumber0].Statistics == nil {
|
||||
cal.Months[month0].Weeks[weeknumber0].Statistics = make(WeekStatistics)
|
||||
}
|
||||
if cal.Months[month0].Weeks[weeknumber0].Statistics == nil {
|
||||
cal.Months[month0].Weeks[weeknumber0].Statistics = make(WeekStatistics)
|
||||
}
|
||||
|
||||
cal.Months[month0].Weeks[weeknumber0].Statistics[weekdayName] = append(cal.Months[month0].Weeks[weeknumber0].Statistics[weekdayName], stat)
|
||||
}
|
||||
cal.Months[month0].Weeks[weeknumber0].Statistics[weekdayName] = append(cal.Months[month0].Weeks[weeknumber0].Statistics[weekdayName], stat)
|
||||
}
|
||||
|
||||
if nextDayHours.GreaterThan(decimal.NewFromInt(0)) {
|
||||
month, weeknumber := GetISOWeekInMonth(entryFinish)
|
||||
month0 := month - 1
|
||||
weeknumber0 := weeknumber - 1
|
||||
weekday := entry.Begin.Weekday()
|
||||
weekdayName := weekday.String()[:2]
|
||||
if nextDayHours.GreaterThan(decimal.NewFromInt(0)) {
|
||||
month, weeknumber := GetISOWeekInMonth(entryFinish)
|
||||
month0 := month - 1
|
||||
weeknumber0 := weeknumber - 1
|
||||
weekday := entry.Begin.Weekday()
|
||||
weekdayName := weekday.String()[:2]
|
||||
|
||||
stat := Statistic{
|
||||
Hours: nextDayHours,
|
||||
Project: entry.Project,
|
||||
Color: GetColorFnFromHex(projects[projectId].Color),
|
||||
}
|
||||
stat := Statistic{
|
||||
Hours: nextDayHours,
|
||||
Project: entry.Project,
|
||||
Color: GetColorFnFromHex(projects[projectId].Color),
|
||||
}
|
||||
|
||||
if cal.Months[month0].Weeks[weeknumber0].Statistics == nil {
|
||||
cal.Months[month0].Weeks[weeknumber0].Statistics = make(WeekStatistics)
|
||||
}
|
||||
if cal.Months[month0].Weeks[weeknumber0].Statistics == nil {
|
||||
cal.Months[month0].Weeks[weeknumber0].Statistics = make(WeekStatistics)
|
||||
}
|
||||
|
||||
cal.Months[month0].Weeks[weeknumber0].Statistics[weekdayName] = append(cal.Months[month0].Weeks[weeknumber0].Statistics[weekdayName], stat)
|
||||
}
|
||||
cal.Months[month0].Weeks[weeknumber0].Statistics[weekdayName] = append(cal.Months[month0].Weeks[weeknumber0].Statistics[weekdayName], stat)
|
||||
}
|
||||
|
||||
var dist = cal.Distribution[entry.Project]
|
||||
dist.Project = entry.Project
|
||||
dist.Hours = dist.Hours.Add(sameDayHours)
|
||||
dist.Hours = dist.Hours.Add(nextDayHours)
|
||||
dist.Color = GetColorFnFromHex(projects[projectId].Color)
|
||||
cal.Distribution[entry.Project] = dist
|
||||
dist := cal.Distribution[entry.Project]
|
||||
dist.Project = entry.Project
|
||||
dist.Hours = dist.Hours.Add(sameDayHours)
|
||||
dist.Hours = dist.Hours.Add(nextDayHours)
|
||||
dist.Color = GetColorFnFromHex(projects[projectId].Color)
|
||||
cal.Distribution[entry.Project] = dist
|
||||
|
||||
// fmt.Printf("Same Day: %s \n Next Day: %s \n Project Hours: %s\n", sameDayHours.String(), nextDayHours.String(), dist.Hours.String())
|
||||
cal.TotalHours = cal.TotalHours.Add(sameDayHours)
|
||||
cal.TotalHours = cal.TotalHours.Add(nextDayHours)
|
||||
}
|
||||
// fmt.Printf("Same Day: %s \n Next Day: %s \n Project Hours: %s\n", sameDayHours.String(), nextDayHours.String(), dist.Hours.String())
|
||||
cal.TotalHours = cal.TotalHours.Add(sameDayHours)
|
||||
cal.TotalHours = cal.TotalHours.Add(nextDayHours)
|
||||
}
|
||||
|
||||
return cal, nil
|
||||
return cal, nil
|
||||
}
|
||||
|
||||
func (calendar *Calendar) GetOutputForWeekCalendar(date time.Time, month int, week int) (string) {
|
||||
var output string = ""
|
||||
var bars [][]string
|
||||
var totalHours = decimal.NewFromInt(0)
|
||||
func (calendar *Calendar) GetOutputForWeekCalendar(date time.Time, month int, week int) string {
|
||||
var output string = ""
|
||||
var bars [][]string
|
||||
totalHours := decimal.NewFromInt(0)
|
||||
|
||||
var days = []string{"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}
|
||||
for _, day := range days {
|
||||
var dayHours = decimal.NewFromInt(0)
|
||||
days := []string{"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}
|
||||
for _, day := range days {
|
||||
dayHours := decimal.NewFromInt(0)
|
||||
|
||||
for _, stat := range calendar.Months[month].Weeks[week].Statistics[day] {
|
||||
dayHours = dayHours.Add(stat.Hours)
|
||||
totalHours = totalHours.Add(stat.Hours)
|
||||
}
|
||||
for _, stat := range calendar.Months[month].Weeks[week].Statistics[day] {
|
||||
dayHours = dayHours.Add(stat.Hours)
|
||||
totalHours = totalHours.Add(stat.Hours)
|
||||
}
|
||||
|
||||
if dayHours.GreaterThan(decimal.NewFromInt(24)) {
|
||||
fmt.Printf("%s %s of week %d in month %d has more than 24h tracked; cutting at 24h now\n", CharError, day, (month+1), (week+1))
|
||||
dayHours = decimal.NewFromInt(24)
|
||||
}
|
||||
if dayHours.GreaterThan(decimal.NewFromInt(24)) {
|
||||
fmt.Printf("%s %s of week %d in month %d has more than 24h tracked; cutting at 24h now\n", CharError, day, (month + 1), (week + 1))
|
||||
dayHours = decimal.NewFromInt(24)
|
||||
}
|
||||
|
||||
bar := GetOutputBarForHours(dayHours, calendar.Months[month].Weeks[week].Statistics[day])
|
||||
bars = append(bars, bar)
|
||||
}
|
||||
bar := GetOutputBarForHours(dayHours, calendar.Months[month].Weeks[week].Statistics[day])
|
||||
bars = append(bars, bar)
|
||||
}
|
||||
|
||||
output = fmt.Sprintf("CW %02d %s H\n", GetISOCalendarWeek(date), fmtHours(totalHours))
|
||||
for row := 0; row < len(bars[0]); row++ {
|
||||
output = fmt.Sprintf("%s%2d │", output, ((6 - row) * 4))
|
||||
for col := 0; col < len(bars); col++ {
|
||||
output = fmt.Sprintf("%s%s", output, bars[col][row])
|
||||
}
|
||||
output = fmt.Sprintf("%s\n", output)
|
||||
}
|
||||
output = fmt.Sprintf("%s └────────────────────────────\n %s %s %s %s %s %s %s\n",
|
||||
output, days[0], days[1], days[2], days[3], days[4], days[5], days[6])
|
||||
output = fmt.Sprintf("CW %02d %s H\n", GetISOCalendarWeek(date), fmtHours(totalHours))
|
||||
for row := 0; row < len(bars[0]); row++ {
|
||||
output = fmt.Sprintf("%s%2d │", output, ((6 - row) * 4))
|
||||
for col := 0; col < len(bars); col++ {
|
||||
output = fmt.Sprintf("%s%s", output, bars[col][row])
|
||||
}
|
||||
output = fmt.Sprintf("%s\n", output)
|
||||
}
|
||||
output = fmt.Sprintf("%s └────────────────────────────\n %s %s %s %s %s %s %s\n",
|
||||
output, days[0], days[1], days[2], days[3], days[4], days[5], days[6])
|
||||
|
||||
return output
|
||||
return output
|
||||
}
|
||||
|
||||
func (calendar *Calendar) GetOutputForDistribution() (string) {
|
||||
var output string = ""
|
||||
func (calendar *Calendar) GetOutputForDistribution() string {
|
||||
var output string = ""
|
||||
|
||||
// fmt.Printf("%s\n", calendar.TotalHours.String())
|
||||
// fmt.Printf("%s\n", calendar.TotalHours.String())
|
||||
|
||||
var bar string = ""
|
||||
for _, stat := range calendar.Distribution {
|
||||
divided := stat.Hours.Div(calendar.TotalHours)
|
||||
percentage := divided.Mul(decimal.NewFromInt(100))
|
||||
hoursStr := fmtHours(stat.Hours)
|
||||
percentageStr := percentage.StringFixed(2)
|
||||
var bar string = ""
|
||||
for _, stat := range calendar.Distribution {
|
||||
divided := stat.Hours.Div(calendar.TotalHours)
|
||||
percentage := divided.Mul(decimal.NewFromInt(100))
|
||||
hoursStr := fmtHours(stat.Hours)
|
||||
percentageStr := percentage.StringFixed(2)
|
||||
|
||||
dividedByBarLength := percentage.Div(decimal.NewFromInt(100))
|
||||
percentageForBar := dividedByBarLength.Mul(decimal.NewFromInt(80))
|
||||
percentageForBarInt := int(percentageForBar.Round(0).IntPart())
|
||||
dividedByBarLength := percentage.Div(decimal.NewFromInt(100))
|
||||
percentageForBar := dividedByBarLength.Mul(decimal.NewFromInt(80))
|
||||
percentageForBarInt := int(percentageForBar.Round(0).IntPart())
|
||||
|
||||
bar = fmt.Sprintf("%s%s", bar, stat.Color(strings.Repeat("█", percentageForBarInt)))
|
||||
bar = fmt.Sprintf("%s%s", bar, stat.Color(strings.Repeat("█", percentageForBarInt)))
|
||||
|
||||
output = fmt.Sprintf("%s%s%*s H / %*s %%\n", output, stat.Color(stat.Project), (68 - len(stat.Project)), hoursStr, 5, percentageStr)
|
||||
}
|
||||
output = fmt.Sprintf("%s%s%*s H / %*s %%\n", output, stat.Color(stat.Project), (68 - len(stat.Project)), hoursStr, 5, percentageStr)
|
||||
}
|
||||
|
||||
output = fmt.Sprintf("DISTRIBUTION\n\n%s\n\n%s\n", bar, output)
|
||||
return output
|
||||
output = fmt.Sprintf("DISTRIBUTION\n\n%s\n\n%s\n", bar, output)
|
||||
return output
|
||||
}
|
||||
|
||||
+8
-9
@@ -1,19 +1,18 @@
|
||||
package z
|
||||
|
||||
const (
|
||||
FlagNoColors string = "no-colors"
|
||||
FlagDebug string = "debug"
|
||||
FlagNoColors string = "no-colors"
|
||||
FlagDebug string = "debug"
|
||||
)
|
||||
|
||||
const (
|
||||
TFAbsTwelveHour int = 0
|
||||
TFAbsTwentyfourHour int = 1
|
||||
TFRelHourMinute int = 2
|
||||
TFRelHourFraction int = 3
|
||||
TFAbsTwelveHour int = 0
|
||||
TFAbsTwentyfourHour int = 1
|
||||
TFRelHourMinute int = 2
|
||||
TFRelHourFraction int = 3
|
||||
)
|
||||
|
||||
const (
|
||||
FinishWithMetadata int = 0
|
||||
FinishOnlyTime int = 1
|
||||
FinishWithMetadata int = 0
|
||||
FinishOnlyTime int = 1
|
||||
)
|
||||
|
||||
|
||||
+217
-217
@@ -1,321 +1,321 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"sort"
|
||||
"strings"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/tidwall/buntdb"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/tidwall/buntdb"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
DB *buntdb.DB
|
||||
DB *buntdb.DB
|
||||
}
|
||||
|
||||
func InitDatabase() (*Database, error) {
|
||||
dbfile := viper.GetString("db")
|
||||
if dbfile == "" {
|
||||
return nil, errors.New("please `export ZEIT_DB` to the location the zeit database should be stored at")
|
||||
}
|
||||
dbfile := viper.GetString("db")
|
||||
if dbfile == "" {
|
||||
return nil, errors.New("please `export ZEIT_DB` to the location the zeit database should be stored at")
|
||||
}
|
||||
|
||||
db, err := buntdb.Open(dbfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db, err := buntdb.Open(dbfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db.CreateIndex("task", "*", buntdb.IndexJSON("task"))
|
||||
db.CreateIndex("project", "*", buntdb.IndexJSON("project"))
|
||||
db.CreateIndex("task", "*", buntdb.IndexJSON("task"))
|
||||
db.CreateIndex("project", "*", buntdb.IndexJSON("project"))
|
||||
|
||||
database := Database{db}
|
||||
return &database, nil
|
||||
database := Database{db}
|
||||
return &database, nil
|
||||
}
|
||||
|
||||
func (database *Database) NewID() (string) {
|
||||
id, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
log.Fatalln("could not generate UUID: %+v", err)
|
||||
}
|
||||
return id.String()
|
||||
func (database *Database) NewID() string {
|
||||
id, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
log.Fatalln("could not generate UUID: %+v", err)
|
||||
}
|
||||
return id.String()
|
||||
}
|
||||
|
||||
func (database *Database) AddEntry(user string, entry Entry, setRunning bool) (string, error) {
|
||||
id := database.NewID()
|
||||
id := database.NewID()
|
||||
|
||||
entryJson, jsonerr := json.Marshal(entry)
|
||||
if jsonerr != nil {
|
||||
return id, jsonerr
|
||||
}
|
||||
entryJson, jsonerr := json.Marshal(entry)
|
||||
if jsonerr != nil {
|
||||
return id, jsonerr
|
||||
}
|
||||
|
||||
dberr := database.DB.Update(func(tx *buntdb.Tx) error {
|
||||
if setRunning == true {
|
||||
_, _, seterr := tx.Set(user + ":status:running", id, nil)
|
||||
if seterr != nil {
|
||||
return seterr
|
||||
}
|
||||
}
|
||||
_, _, seterr := tx.Set(user + ":entry:" + id, string(entryJson), nil)
|
||||
if seterr != nil {
|
||||
return seterr
|
||||
}
|
||||
dberr := database.DB.Update(func(tx *buntdb.Tx) error {
|
||||
if setRunning == true {
|
||||
_, _, seterr := tx.Set(user+":status:running", id, nil)
|
||||
if seterr != nil {
|
||||
return seterr
|
||||
}
|
||||
}
|
||||
_, _, seterr := tx.Set(user+":entry:"+id, string(entryJson), nil)
|
||||
if seterr != nil {
|
||||
return seterr
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
return id, dberr
|
||||
return id, dberr
|
||||
}
|
||||
|
||||
func (database *Database) GetEntry(user string, entryId string) (Entry, error) {
|
||||
var entry Entry
|
||||
var entry Entry
|
||||
|
||||
dberr := database.DB.View(func(tx *buntdb.Tx) error {
|
||||
value, err := tx.Get(user + ":entry:" + entryId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
json.Unmarshal([]byte(value), &entry)
|
||||
dberr := database.DB.View(func(tx *buntdb.Tx) error {
|
||||
value, err := tx.Get(user + ":entry:" + entryId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
json.Unmarshal([]byte(value), &entry)
|
||||
|
||||
entry.ID = entryId
|
||||
return nil
|
||||
})
|
||||
entry.ID = entryId
|
||||
return nil
|
||||
})
|
||||
|
||||
return entry, dberr
|
||||
return entry, dberr
|
||||
}
|
||||
|
||||
func (database *Database) UpdateEntry(user string, entry Entry) (string, error) {
|
||||
entryJson, jsonerr := json.Marshal(entry)
|
||||
if jsonerr != nil {
|
||||
return entry.ID, jsonerr
|
||||
}
|
||||
entryJson, jsonerr := json.Marshal(entry)
|
||||
if jsonerr != nil {
|
||||
return entry.ID, jsonerr
|
||||
}
|
||||
|
||||
dberr := database.DB.Update(func(tx *buntdb.Tx) error {
|
||||
_, _, seerr := tx.Set(user + ":entry:" + entry.ID, string(entryJson), nil)
|
||||
if seerr != nil {
|
||||
return seerr
|
||||
}
|
||||
dberr := database.DB.Update(func(tx *buntdb.Tx) error {
|
||||
_, _, seerr := tx.Set(user+":entry:"+entry.ID, string(entryJson), nil)
|
||||
if seerr != nil {
|
||||
return seerr
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
return entry.ID, dberr
|
||||
return entry.ID, dberr
|
||||
}
|
||||
|
||||
func (database *Database) FinishEntry(user string, entry Entry) (string, error) {
|
||||
entryJson, jsonerr := json.Marshal(entry)
|
||||
if jsonerr != nil {
|
||||
return entry.ID, jsonerr
|
||||
}
|
||||
entryJson, jsonerr := json.Marshal(entry)
|
||||
if jsonerr != nil {
|
||||
return entry.ID, jsonerr
|
||||
}
|
||||
|
||||
dberr := database.DB.Update(func(tx *buntdb.Tx) error {
|
||||
runningEntryId, grerr := tx.Get(user + ":status:running")
|
||||
if grerr != nil {
|
||||
return errors.New("no currently running entry found!")
|
||||
}
|
||||
dberr := database.DB.Update(func(tx *buntdb.Tx) error {
|
||||
runningEntryId, grerr := tx.Get(user + ":status:running")
|
||||
if grerr != nil {
|
||||
return errors.New("no currently running entry found!")
|
||||
}
|
||||
|
||||
if runningEntryId != entry.ID {
|
||||
return errors.New("specified entry is not currently running!")
|
||||
}
|
||||
if runningEntryId != entry.ID {
|
||||
return errors.New("specified entry is not currently running!")
|
||||
}
|
||||
|
||||
_, _, srerr := tx.Set(user + ":status:running", "", nil)
|
||||
if srerr != nil {
|
||||
return srerr
|
||||
}
|
||||
_, _, srerr := tx.Set(user+":status:running", "", nil)
|
||||
if srerr != nil {
|
||||
return srerr
|
||||
}
|
||||
|
||||
_, _, seerr := tx.Set(user + ":entry:" + entry.ID, string(entryJson), nil)
|
||||
if seerr != nil {
|
||||
return seerr
|
||||
}
|
||||
_, _, seerr := tx.Set(user+":entry:"+entry.ID, string(entryJson), nil)
|
||||
if seerr != nil {
|
||||
return seerr
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
return entry.ID, dberr
|
||||
return entry.ID, dberr
|
||||
}
|
||||
|
||||
func (database *Database) EraseEntry(user string, id string) (error) {
|
||||
runningEntryId, err := database.GetRunningEntryId(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (database *Database) EraseEntry(user string, id string) error {
|
||||
runningEntryId, err := database.GetRunningEntryId(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dberr := database.DB.Update(func(tx *buntdb.Tx) error {
|
||||
if runningEntryId == id {
|
||||
_, _, seterr := tx.Set(user + ":status:running", "", nil)
|
||||
if seterr != nil {
|
||||
return seterr
|
||||
}
|
||||
}
|
||||
dberr := database.DB.Update(func(tx *buntdb.Tx) error {
|
||||
if runningEntryId == id {
|
||||
_, _, seterr := tx.Set(user+":status:running", "", nil)
|
||||
if seterr != nil {
|
||||
return seterr
|
||||
}
|
||||
}
|
||||
|
||||
_, delerr := tx.Delete(user + ":entry:" + id)
|
||||
if delerr != nil {
|
||||
return delerr
|
||||
}
|
||||
_, delerr := tx.Delete(user + ":entry:" + id)
|
||||
if delerr != nil {
|
||||
return delerr
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
return dberr
|
||||
return dberr
|
||||
}
|
||||
|
||||
func (database *Database) GetRunningEntryId(user string) (string, error) {
|
||||
var runningId string = ""
|
||||
var runningId string = ""
|
||||
|
||||
dberr := database.DB.View(func(tx *buntdb.Tx) error {
|
||||
value, err := tx.Get(user + ":status:running")
|
||||
if errors.Is(err, buntdb.ErrNotFound) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runningId = value
|
||||
return nil
|
||||
})
|
||||
dberr := database.DB.View(func(tx *buntdb.Tx) error {
|
||||
value, err := tx.Get(user + ":status:running")
|
||||
if errors.Is(err, buntdb.ErrNotFound) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runningId = value
|
||||
return nil
|
||||
})
|
||||
|
||||
return runningId, dberr
|
||||
return runningId, dberr
|
||||
}
|
||||
|
||||
func (database *Database) ListEntries(user string) ([]Entry, error) {
|
||||
var entries []Entry
|
||||
var entries []Entry
|
||||
|
||||
dberr := database.DB.View(func(tx *buntdb.Tx) error {
|
||||
tx.AscendKeys(user + ":entry:*", func(key, value string) bool {
|
||||
var entry Entry
|
||||
json.Unmarshal([]byte(value), &entry)
|
||||
dberr := database.DB.View(func(tx *buntdb.Tx) error {
|
||||
tx.AscendKeys(user+":entry:*", func(key, value string) bool {
|
||||
var entry Entry
|
||||
json.Unmarshal([]byte(value), &entry)
|
||||
|
||||
entry.SetIDFromDatabaseKey(key)
|
||||
entry.SetIDFromDatabaseKey(key)
|
||||
|
||||
entries = append(entries, entry)
|
||||
return true
|
||||
})
|
||||
entries = append(entries, entry)
|
||||
return true
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
sort.Slice(entries, func(i, j int) bool { return entries[i].Begin.Before(entries[j].Begin) })
|
||||
return entries, dberr
|
||||
sort.Slice(entries, func(i, j int) bool { return entries[i].Begin.Before(entries[j].Begin) })
|
||||
return entries, dberr
|
||||
}
|
||||
|
||||
func (database *Database) GetImportsSHA1List(user string) (map[string]string, error) {
|
||||
var sha1List = make(map[string]string)
|
||||
sha1List := make(map[string]string)
|
||||
|
||||
dberr := database.DB.View(func(tx *buntdb.Tx) error {
|
||||
value, err := tx.Get(user + ":imports:sha1", false)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
dberr := database.DB.View(func(tx *buntdb.Tx) error {
|
||||
value, err := tx.Get(user+":imports:sha1", false)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
sha1Entries := strings.Split(value, ",")
|
||||
sha1Entries := strings.Split(value, ",")
|
||||
|
||||
for _, sha1Entry := range sha1Entries {
|
||||
sha1EntrySplit := strings.Split(sha1Entry, ":")
|
||||
sha1 := sha1EntrySplit[0]
|
||||
id := sha1EntrySplit[1]
|
||||
sha1List[sha1] = id
|
||||
}
|
||||
for _, sha1Entry := range sha1Entries {
|
||||
sha1EntrySplit := strings.Split(sha1Entry, ":")
|
||||
sha1 := sha1EntrySplit[0]
|
||||
id := sha1EntrySplit[1]
|
||||
sha1List[sha1] = id
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
return sha1List, dberr
|
||||
return sha1List, dberr
|
||||
}
|
||||
|
||||
func (database *Database) UpdateImportsSHA1List(user string, sha1List map[string]string) (error) {
|
||||
var sha1Entries []string
|
||||
func (database *Database) UpdateImportsSHA1List(user string, sha1List map[string]string) error {
|
||||
var sha1Entries []string
|
||||
|
||||
for sha1, id := range sha1List {
|
||||
sha1Entries = append(sha1Entries, sha1 + ":" + id)
|
||||
}
|
||||
for sha1, id := range sha1List {
|
||||
sha1Entries = append(sha1Entries, sha1+":"+id)
|
||||
}
|
||||
|
||||
value := strings.Join(sha1Entries, ",")
|
||||
value := strings.Join(sha1Entries, ",")
|
||||
|
||||
dberr := database.DB.Update(func(tx *buntdb.Tx) error {
|
||||
_, _, seterr := tx.Set(user + ":imports:sha1", value, nil)
|
||||
if seterr != nil {
|
||||
return seterr
|
||||
}
|
||||
dberr := database.DB.Update(func(tx *buntdb.Tx) error {
|
||||
_, _, seterr := tx.Set(user+":imports:sha1", value, nil)
|
||||
if seterr != nil {
|
||||
return seterr
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
return dberr
|
||||
return dberr
|
||||
}
|
||||
|
||||
func (database *Database) UpdateProject(user string, projectName string, project Project) (error) {
|
||||
projectJson, jsonerr := json.Marshal(project)
|
||||
if jsonerr != nil {
|
||||
return jsonerr
|
||||
}
|
||||
func (database *Database) UpdateProject(user string, projectName string, project Project) error {
|
||||
projectJson, jsonerr := json.Marshal(project)
|
||||
if jsonerr != nil {
|
||||
return jsonerr
|
||||
}
|
||||
|
||||
projectId := GetIdFromName(projectName)
|
||||
projectId := GetIdFromName(projectName)
|
||||
|
||||
dberr := database.DB.Update(func(tx *buntdb.Tx) error {
|
||||
_, _, sperr := tx.Set(user + ":project:" + projectId, string(projectJson), nil)
|
||||
if sperr != nil {
|
||||
return sperr
|
||||
}
|
||||
dberr := database.DB.Update(func(tx *buntdb.Tx) error {
|
||||
_, _, sperr := tx.Set(user+":project:"+projectId, string(projectJson), nil)
|
||||
if sperr != nil {
|
||||
return sperr
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
return dberr
|
||||
return dberr
|
||||
}
|
||||
|
||||
func (database *Database) GetProject(user string, projectName string) (Project, error) {
|
||||
var project Project
|
||||
projectId := GetIdFromName(projectName)
|
||||
var project Project
|
||||
projectId := GetIdFromName(projectName)
|
||||
|
||||
dberr := database.DB.View(func(tx *buntdb.Tx) error {
|
||||
value, err := tx.Get(user + ":project:" + projectId, false)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
dberr := database.DB.View(func(tx *buntdb.Tx) error {
|
||||
value, err := tx.Get(user+":project:"+projectId, false)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
json.Unmarshal([]byte(value), &project)
|
||||
json.Unmarshal([]byte(value), &project)
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
return project, dberr
|
||||
return project, dberr
|
||||
}
|
||||
|
||||
func (database *Database) UpdateTask(user string, taskName string, task Task) (error) {
|
||||
taskJson, jsonerr := json.Marshal(task)
|
||||
if jsonerr != nil {
|
||||
return jsonerr
|
||||
}
|
||||
func (database *Database) UpdateTask(user string, taskName string, task Task) error {
|
||||
taskJson, jsonerr := json.Marshal(task)
|
||||
if jsonerr != nil {
|
||||
return jsonerr
|
||||
}
|
||||
|
||||
taskId := GetIdFromName(taskName)
|
||||
taskId := GetIdFromName(taskName)
|
||||
|
||||
dberr := database.DB.Update(func(tx *buntdb.Tx) error {
|
||||
_, _, sperr := tx.Set(user + ":task:" + taskId, string(taskJson), nil)
|
||||
if sperr != nil {
|
||||
return sperr
|
||||
}
|
||||
dberr := database.DB.Update(func(tx *buntdb.Tx) error {
|
||||
_, _, sperr := tx.Set(user+":task:"+taskId, string(taskJson), nil)
|
||||
if sperr != nil {
|
||||
return sperr
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
return dberr
|
||||
return dberr
|
||||
}
|
||||
|
||||
func (database *Database) GetTask(user string, taskName string) (Task, error) {
|
||||
var task Task
|
||||
taskId := GetIdFromName(taskName)
|
||||
var task Task
|
||||
taskId := GetIdFromName(taskName)
|
||||
|
||||
dberr := database.DB.View(func(tx *buntdb.Tx) error {
|
||||
value, err := tx.Get(user + ":task:" + taskId, false)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
dberr := database.DB.View(func(tx *buntdb.Tx) error {
|
||||
value, err := tx.Get(user+":task:"+taskId, false)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
json.Unmarshal([]byte(value), &task)
|
||||
json.Unmarshal([]byte(value), &task)
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
return task, dberr
|
||||
return task, dberr
|
||||
}
|
||||
|
||||
+173
-173
@@ -1,240 +1,240 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
"fmt"
|
||||
"github.com/gookit/color"
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/spf13/viper"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
ID string `json:"-"`
|
||||
Begin time.Time `json:"begin,omitempty"`
|
||||
Finish time.Time `json:"finish,omitempty"`
|
||||
Project string `json:"project,omitempty"`
|
||||
Task string `json:"task,omitempty"`
|
||||
Notes string `json:"notes,omitempty"`
|
||||
User string `json:"user,omitempty"`
|
||||
ID string `json:"-"`
|
||||
Begin time.Time `json:"begin,omitempty"`
|
||||
Finish time.Time `json:"finish,omitempty"`
|
||||
Project string `json:"project,omitempty"`
|
||||
Task string `json:"task,omitempty"`
|
||||
Notes string `json:"notes,omitempty"`
|
||||
User string `json:"user,omitempty"`
|
||||
|
||||
SHA1 string `json:"-"`
|
||||
SHA1 string `json:"-"`
|
||||
}
|
||||
|
||||
func NewEntry(
|
||||
id string,
|
||||
begin string,
|
||||
finish string,
|
||||
project string,
|
||||
task string,
|
||||
user string) (Entry, error) {
|
||||
var err error
|
||||
id string,
|
||||
begin string,
|
||||
finish string,
|
||||
project string,
|
||||
task string,
|
||||
user string,
|
||||
) (Entry, error) {
|
||||
var err error
|
||||
|
||||
newEntry := Entry{}
|
||||
newEntry := Entry{}
|
||||
|
||||
newEntry.ID = id
|
||||
newEntry.Project = project
|
||||
newEntry.Task = task
|
||||
newEntry.User = user
|
||||
newEntry.ID = id
|
||||
newEntry.Project = project
|
||||
newEntry.Task = task
|
||||
newEntry.User = user
|
||||
|
||||
_, err = newEntry.SetBeginFromString(begin, time.Time{})
|
||||
if err != nil {
|
||||
return Entry{}, err
|
||||
}
|
||||
_, err = newEntry.SetBeginFromString(begin, time.Time{})
|
||||
if err != nil {
|
||||
return Entry{}, err
|
||||
}
|
||||
|
||||
_, err = newEntry.SetFinishFromString(finish, time.Time{})
|
||||
if err != nil {
|
||||
return Entry{}, err
|
||||
}
|
||||
_, err = newEntry.SetFinishFromString(finish, time.Time{})
|
||||
if err != nil {
|
||||
return Entry{}, err
|
||||
}
|
||||
|
||||
if id == "" && newEntry.IsFinishedAfterBegan() == false {
|
||||
return Entry{}, errors.New("beginning time of tracking cannot be after finish time")
|
||||
}
|
||||
if id == "" && newEntry.IsFinishedAfterBegan() == false {
|
||||
return Entry{}, errors.New("beginning time of tracking cannot be after finish time")
|
||||
}
|
||||
|
||||
return newEntry, nil
|
||||
return newEntry, nil
|
||||
}
|
||||
|
||||
func (entry *Entry) SetIDFromDatabaseKey(key string) (error) {
|
||||
splitKey := strings.Split(key, ":")
|
||||
func (entry *Entry) SetIDFromDatabaseKey(key string) error {
|
||||
splitKey := strings.Split(key, ":")
|
||||
|
||||
if len(splitKey) < 3 || len(splitKey) > 3 {
|
||||
return errors.New("not a valid database key")
|
||||
}
|
||||
if len(splitKey) < 3 || len(splitKey) > 3 {
|
||||
return errors.New("not a valid database key")
|
||||
}
|
||||
|
||||
entry.ID = splitKey[2]
|
||||
return nil
|
||||
entry.ID = splitKey[2]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (entry *Entry) SetBeginFromString(begin string, contextTime time.Time) (time.Time, error) {
|
||||
var beginTime time.Time
|
||||
var err error
|
||||
var beginTime time.Time
|
||||
var err error
|
||||
|
||||
if begin == "" {
|
||||
beginTime = time.Now()
|
||||
} else {
|
||||
beginTime, err = ParseTime(begin, contextTime)
|
||||
if err != nil {
|
||||
return beginTime, err
|
||||
}
|
||||
}
|
||||
if begin == "" {
|
||||
beginTime = time.Now()
|
||||
} else {
|
||||
beginTime, err = ParseTime(begin, contextTime)
|
||||
if err != nil {
|
||||
return beginTime, err
|
||||
}
|
||||
}
|
||||
|
||||
entry.Begin = beginTime
|
||||
entry.secondsBegin()
|
||||
return entry.Begin, nil
|
||||
entry.Begin = beginTime
|
||||
entry.secondsBegin()
|
||||
return entry.Begin, nil
|
||||
}
|
||||
|
||||
func (entry *Entry) SetFinishFromString(finish string, contextTime time.Time) (time.Time, error) {
|
||||
var finishTime time.Time
|
||||
var err error
|
||||
var finishTime time.Time
|
||||
var err error
|
||||
|
||||
if finish != "" {
|
||||
finishTime, err = ParseTime(finish, contextTime)
|
||||
if err != nil {
|
||||
return finishTime, err
|
||||
}
|
||||
}
|
||||
if finish != "" {
|
||||
finishTime, err = ParseTime(finish, contextTime)
|
||||
if err != nil {
|
||||
return finishTime, err
|
||||
}
|
||||
}
|
||||
|
||||
entry.Finish = finishTime
|
||||
entry.secondsFinish()
|
||||
return entry.Finish, nil
|
||||
entry.Finish = finishTime
|
||||
entry.secondsFinish()
|
||||
return entry.Finish, nil
|
||||
}
|
||||
|
||||
func (entry *Entry) IsFinishedAfterBegan() (bool) {
|
||||
return (entry.Finish.IsZero() || entry.Begin.Before(entry.Finish) || entry.Begin.Equal(entry.Finish))
|
||||
func (entry *Entry) IsFinishedAfterBegan() bool {
|
||||
return (entry.Finish.IsZero() || entry.Begin.Before(entry.Finish) || entry.Begin.Equal(entry.Finish))
|
||||
}
|
||||
|
||||
func (entry *Entry) GetOutputForTrack(isRunning bool, wasRunning bool) (string) {
|
||||
var outputPrefix string = ""
|
||||
var outputSuffix string = ""
|
||||
func (entry *Entry) GetOutputForTrack(isRunning bool, wasRunning bool) string {
|
||||
var outputPrefix string = ""
|
||||
var outputSuffix string = ""
|
||||
|
||||
now := time.Now()
|
||||
trackDiffNow := now.Sub(entry.Begin)
|
||||
durationString := fmtDuration(trackDiffNow)
|
||||
now := time.Now()
|
||||
trackDiffNow := now.Sub(entry.Begin)
|
||||
durationString := fmtDuration(trackDiffNow)
|
||||
|
||||
if isRunning == true && wasRunning == false {
|
||||
outputPrefix = "began tracking"
|
||||
} else if isRunning == true && wasRunning == true {
|
||||
outputPrefix = "tracking"
|
||||
outputSuffix = fmt.Sprintf(" for %sh", color.FgLightWhite.Render(durationString))
|
||||
} else if isRunning == false && wasRunning == false {
|
||||
outputPrefix = "tracked"
|
||||
}
|
||||
if isRunning == true && wasRunning == false {
|
||||
outputPrefix = "began tracking"
|
||||
} else if isRunning == true && wasRunning == true {
|
||||
outputPrefix = "tracking"
|
||||
outputSuffix = fmt.Sprintf(" for %sh", color.FgLightWhite.Render(durationString))
|
||||
} else if isRunning == false && wasRunning == false {
|
||||
outputPrefix = "tracked"
|
||||
}
|
||||
|
||||
if entry.Task != "" && entry.Project != "" {
|
||||
return fmt.Sprintf("%s %s %s on %s%s\n", CharTrack, outputPrefix, color.FgLightWhite.Render(entry.Task), color.FgLightWhite.Render(entry.Project), outputSuffix)
|
||||
} else if entry.Task != "" && entry.Project == "" {
|
||||
return fmt.Sprintf("%s %s %s%s\n", CharTrack, outputPrefix, color.FgLightWhite.Render(entry.Task), outputSuffix)
|
||||
} else if entry.Task == "" && entry.Project != "" {
|
||||
return fmt.Sprintf("%s %s task on %s%s\n", CharTrack, outputPrefix, color.FgLightWhite.Render(entry.Project), outputSuffix)
|
||||
}
|
||||
if entry.Task != "" && entry.Project != "" {
|
||||
return fmt.Sprintf("%s %s %s on %s%s\n", CharTrack, outputPrefix, color.FgLightWhite.Render(entry.Task), color.FgLightWhite.Render(entry.Project), outputSuffix)
|
||||
} else if entry.Task != "" && entry.Project == "" {
|
||||
return fmt.Sprintf("%s %s %s%s\n", CharTrack, outputPrefix, color.FgLightWhite.Render(entry.Task), outputSuffix)
|
||||
} else if entry.Task == "" && entry.Project != "" {
|
||||
return fmt.Sprintf("%s %s task on %s%s\n", CharTrack, outputPrefix, color.FgLightWhite.Render(entry.Project), outputSuffix)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %s task%s\n", CharTrack, outputPrefix, outputSuffix)
|
||||
return fmt.Sprintf("%s %s task%s\n", CharTrack, outputPrefix, outputSuffix)
|
||||
}
|
||||
|
||||
func (entry *Entry) GetDuration() (decimal.Decimal) {
|
||||
duration := entry.Finish.Sub(entry.Begin)
|
||||
if (duration < 0) {
|
||||
duration = time.Now().Sub(entry.Begin)
|
||||
}
|
||||
return decimal.NewFromFloat(duration.Hours())
|
||||
func (entry *Entry) GetDuration() decimal.Decimal {
|
||||
duration := entry.Finish.Sub(entry.Begin)
|
||||
if duration < 0 {
|
||||
duration = time.Now().Sub(entry.Begin)
|
||||
}
|
||||
return decimal.NewFromFloat(duration.Hours())
|
||||
}
|
||||
|
||||
func (entry *Entry) GetOutputForFinish() (string) {
|
||||
var outputSuffix string = ""
|
||||
func (entry *Entry) GetOutputForFinish() string {
|
||||
var outputSuffix string = ""
|
||||
|
||||
trackDiff := entry.Finish.Sub(entry.Begin)
|
||||
taskDuration := fmtDuration(trackDiff)
|
||||
trackDiff := entry.Finish.Sub(entry.Begin)
|
||||
taskDuration := fmtDuration(trackDiff)
|
||||
|
||||
outputSuffix = fmt.Sprintf(" for %sh", color.FgLightWhite.Render(taskDuration))
|
||||
outputSuffix = fmt.Sprintf(" for %sh", color.FgLightWhite.Render(taskDuration))
|
||||
|
||||
if entry.Task != "" && entry.Project != "" {
|
||||
return fmt.Sprintf("%s finished tracking %s on %s%s\n", CharFinish, color.FgLightWhite.Render(entry.Task), color.FgLightWhite.Render(entry.Project), outputSuffix)
|
||||
} else if entry.Task != "" && entry.Project == "" {
|
||||
return fmt.Sprintf("%s finished tracking %s%s\n", CharFinish, color.FgLightWhite.Render(entry.Task), outputSuffix)
|
||||
} else if entry.Task == "" && entry.Project != "" {
|
||||
return fmt.Sprintf("%s finished tracking task on %s%s\n", CharFinish, color.FgLightWhite.Render(entry.Project), outputSuffix)
|
||||
}
|
||||
if entry.Task != "" && entry.Project != "" {
|
||||
return fmt.Sprintf("%s finished tracking %s on %s%s\n", CharFinish, color.FgLightWhite.Render(entry.Task), color.FgLightWhite.Render(entry.Project), outputSuffix)
|
||||
} else if entry.Task != "" && entry.Project == "" {
|
||||
return fmt.Sprintf("%s finished tracking %s%s\n", CharFinish, color.FgLightWhite.Render(entry.Task), outputSuffix)
|
||||
} else if entry.Task == "" && entry.Project != "" {
|
||||
return fmt.Sprintf("%s finished tracking task on %s%s\n", CharFinish, color.FgLightWhite.Render(entry.Project), outputSuffix)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s finished tracking task%s\n", CharFinish, outputSuffix)
|
||||
return fmt.Sprintf("%s finished tracking task%s\n", CharFinish, outputSuffix)
|
||||
}
|
||||
|
||||
func (entry *Entry) GetOutput(full bool) (string) {
|
||||
var output string = ""
|
||||
var entryFinish time.Time
|
||||
var isRunning string = ""
|
||||
func (entry *Entry) GetOutput(full bool) string {
|
||||
var output string = ""
|
||||
var entryFinish time.Time
|
||||
var isRunning string = ""
|
||||
|
||||
if entry.Finish.IsZero() {
|
||||
entryFinish = time.Now()
|
||||
isRunning = "[running]"
|
||||
} else {
|
||||
entryFinish = entry.Finish
|
||||
}
|
||||
if entry.Finish.IsZero() {
|
||||
entryFinish = time.Now()
|
||||
isRunning = "[running]"
|
||||
} else {
|
||||
entryFinish = entry.Finish
|
||||
}
|
||||
|
||||
trackDiff := entryFinish.Sub(entry.Begin)
|
||||
taskDuration := fmtDuration(trackDiff)
|
||||
if full == false {
|
||||
trackDiff := entryFinish.Sub(entry.Begin)
|
||||
taskDuration := fmtDuration(trackDiff)
|
||||
if full == false {
|
||||
output = fmt.Sprintf("%s %s on %s from %s to %s (%sh) %s",
|
||||
color.FgGray.Render(entry.ID),
|
||||
color.FgLightWhite.Render(entry.Task),
|
||||
color.FgLightWhite.Render(entry.Project),
|
||||
color.FgLightWhite.Render(entry.Begin.Format("2006-01-02 15:04 -0700")),
|
||||
color.FgLightWhite.Render(entryFinish.Format("2006-01-02 15:04 -0700")),
|
||||
color.FgLightWhite.Render(taskDuration),
|
||||
color.FgLightYellow.Render(isRunning),
|
||||
)
|
||||
} else {
|
||||
output = fmt.Sprintf("%s\n %s on %s\n %sh from %s to %s %s\n\n Notes:\n %s\n",
|
||||
color.FgGray.Render(entry.ID),
|
||||
color.FgLightWhite.Render(entry.Task),
|
||||
color.FgLightWhite.Render(entry.Project),
|
||||
color.FgLightWhite.Render(taskDuration),
|
||||
color.FgLightWhite.Render(entry.Begin.Format("2006-01-02 15:04 -0700")),
|
||||
color.FgLightWhite.Render(entryFinish.Format("2006-01-02 15:04 -0700")),
|
||||
color.FgLightYellow.Render(isRunning),
|
||||
color.FgLightWhite.Render(strings.Replace(entry.Notes, "\n", "\n ", -1)),
|
||||
)
|
||||
}
|
||||
|
||||
output = fmt.Sprintf("%s %s on %s from %s to %s (%sh) %s",
|
||||
color.FgGray.Render(entry.ID),
|
||||
color.FgLightWhite.Render(entry.Task),
|
||||
color.FgLightWhite.Render(entry.Project),
|
||||
color.FgLightWhite.Render(entry.Begin.Format("2006-01-02 15:04 -0700")),
|
||||
color.FgLightWhite.Render(entryFinish.Format("2006-01-02 15:04 -0700")),
|
||||
color.FgLightWhite.Render(taskDuration) ,
|
||||
color.FgLightYellow.Render(isRunning),
|
||||
)
|
||||
} else {
|
||||
output = fmt.Sprintf("%s\n %s on %s\n %sh from %s to %s %s\n\n Notes:\n %s\n",
|
||||
color.FgGray.Render(entry.ID),
|
||||
color.FgLightWhite.Render(entry.Task),
|
||||
color.FgLightWhite.Render(entry.Project),
|
||||
color.FgLightWhite.Render(taskDuration),
|
||||
color.FgLightWhite.Render(entry.Begin.Format("2006-01-02 15:04 -0700")),
|
||||
color.FgLightWhite.Render(entryFinish.Format("2006-01-02 15:04 -0700")),
|
||||
color.FgLightYellow.Render(isRunning),
|
||||
color.FgLightWhite.Render(strings.Replace(entry.Notes, "\n", "\n ", -1)),
|
||||
)
|
||||
}
|
||||
|
||||
return output
|
||||
return output
|
||||
}
|
||||
|
||||
func GetFilteredEntries(entries []Entry, project string, task string, since time.Time, until time.Time) ([]Entry, error) {
|
||||
var filteredEntries []Entry
|
||||
var filteredEntries []Entry
|
||||
|
||||
for _, entry := range entries {
|
||||
if project != "" && GetIdFromName(entry.Project) != GetIdFromName(project) {
|
||||
continue
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if project != "" && GetIdFromName(entry.Project) != GetIdFromName(project) {
|
||||
continue
|
||||
}
|
||||
|
||||
if task != "" && GetIdFromName(entry.Task) != GetIdFromName(task) {
|
||||
continue
|
||||
}
|
||||
if task != "" && GetIdFromName(entry.Task) != GetIdFromName(task) {
|
||||
continue
|
||||
}
|
||||
|
||||
if since.IsZero() == false && since.Before(entry.Begin) == false && since.Equal(entry.Begin) == false {
|
||||
continue
|
||||
}
|
||||
if since.IsZero() == false && since.Before(entry.Begin) == false && since.Equal(entry.Begin) == false {
|
||||
continue
|
||||
}
|
||||
|
||||
if until.IsZero() == false && until.After(entry.Finish) == false && until.Equal(entry.Finish) == false {
|
||||
continue
|
||||
}
|
||||
if until.IsZero() == false && until.After(entry.Finish) == false && until.Equal(entry.Finish) == false {
|
||||
continue
|
||||
}
|
||||
|
||||
filteredEntries = append(filteredEntries, entry)
|
||||
}
|
||||
filteredEntries = append(filteredEntries, entry)
|
||||
}
|
||||
|
||||
return filteredEntries, nil
|
||||
return filteredEntries, nil
|
||||
}
|
||||
|
||||
func (entry *Entry) secondsBegin() {
|
||||
if viper.GetBool("time.no-seconds") {
|
||||
entry.Begin = entry.Begin.Truncate(time.Duration(time.Minute))
|
||||
}
|
||||
if viper.GetBool("time.no-seconds") {
|
||||
entry.Begin = entry.Begin.Truncate(time.Duration(time.Minute))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) secondsFinish() {
|
||||
if viper.GetBool("time.no-seconds") {
|
||||
entry.Finish = entry.Finish.Truncate(time.Duration(time.Minute))
|
||||
}
|
||||
if viper.GetBool("time.no-seconds") {
|
||||
entry.Finish = entry.Finish.Truncate(time.Duration(time.Minute))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+63
-63
@@ -1,83 +1,83 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var entryCmd = &cobra.Command{
|
||||
Use: "entry ([flags]) [id]",
|
||||
Short: "Display or update activity",
|
||||
Long: "Display or update tracked activity.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
user := GetCurrentUser()
|
||||
id := args[0]
|
||||
Use: "entry ([flags]) [id]",
|
||||
Short: "Display or update activity",
|
||||
Long: "Display or update tracked activity.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
user := GetCurrentUser()
|
||||
id := args[0]
|
||||
|
||||
entry, err := database.GetEntry(user, id)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
entry, err := database.GetEntry(user, id)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if begin != "" || finish != "" || project != "" || notes != "" || task != "" {
|
||||
if begin != "" {
|
||||
entry.Begin, err = entry.SetBeginFromString(begin, entry.Begin)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
if begin != "" || finish != "" || project != "" || notes != "" || task != "" {
|
||||
if begin != "" {
|
||||
entry.Begin, err = entry.SetBeginFromString(begin, entry.Begin)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if finish != "" {
|
||||
entry.Finish, err = entry.SetFinishFromString(finish, entry.Finish)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
if finish != "" {
|
||||
entry.Finish, err = entry.SetFinishFromString(finish, entry.Finish)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if project != "" {
|
||||
entry.Project = project
|
||||
}
|
||||
if project != "" {
|
||||
entry.Project = project
|
||||
}
|
||||
|
||||
if task != "" {
|
||||
entry.Task = task
|
||||
}
|
||||
if task != "" {
|
||||
entry.Task = task
|
||||
}
|
||||
|
||||
if notes != "" {
|
||||
entry.Notes = strings.Replace(notes, "\\n", "\n", -1)
|
||||
}
|
||||
if notes != "" {
|
||||
entry.Notes = strings.Replace(notes, "\\n", "\n", -1)
|
||||
}
|
||||
|
||||
_, err = database.UpdateEntry(user, entry)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
_, err = database.UpdateEntry(user, entry)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", CharInfo, entry.GetOutput(true))
|
||||
return
|
||||
},
|
||||
fmt.Printf("%s %s\n", CharInfo, entry.GetOutput(true))
|
||||
return
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(entryCmd)
|
||||
entryCmd.Flags().StringVarP(&begin, "begin", "b", "", "Update date/time the activity began at")
|
||||
entryCmd.Flags().StringVarP(&finish, "finish", "s", "", "Update date/time the activity finished at")
|
||||
entryCmd.Flags().StringVarP(&project, "project", "p", "", "Update activity project")
|
||||
entryCmd.Flags().StringVarP(¬es, "notes", "n", "", "Update activity notes")
|
||||
entryCmd.Flags().StringVarP(&task, "task", "t", "", "Update activity task")
|
||||
entryCmd.Flags().BoolVar(&fractional, "decimal", false, "Show fractional hours in decimal format instead of minutes")
|
||||
rootCmd.AddCommand(entryCmd)
|
||||
entryCmd.Flags().StringVarP(&begin, "begin", "b", "", "Update date/time the activity began at")
|
||||
entryCmd.Flags().StringVarP(&finish, "finish", "s", "", "Update date/time the activity finished at")
|
||||
entryCmd.Flags().StringVarP(&project, "project", "p", "", "Update activity project")
|
||||
entryCmd.Flags().StringVarP(¬es, "notes", "n", "", "Update activity notes")
|
||||
entryCmd.Flags().StringVarP(&task, "task", "t", "", "Update activity task")
|
||||
entryCmd.Flags().BoolVar(&fractional, "decimal", false, "Show fractional hours in decimal format instead of minutes")
|
||||
|
||||
flagName := "task"
|
||||
entryCmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
user := GetCurrentUser()
|
||||
entries, _ := database.ListEntries(user)
|
||||
_, tasks := listProjectsAndTasks(entries)
|
||||
return tasks, cobra.ShellCompDirectiveDefault
|
||||
})
|
||||
flagName := "task"
|
||||
entryCmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
user := GetCurrentUser()
|
||||
entries, _ := database.ListEntries(user)
|
||||
_, tasks := listProjectsAndTasks(entries)
|
||||
return tasks, cobra.ShellCompDirectiveDefault
|
||||
})
|
||||
}
|
||||
|
||||
+21
-20
@@ -1,32 +1,33 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/gookit/color"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var eraseCmd = &cobra.Command{
|
||||
Use: "erase ([flags]) [id]",
|
||||
Short: "Erase activity",
|
||||
Long: "Erase tracked activity.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
user := GetCurrentUser()
|
||||
id := args[0]
|
||||
Use: "erase ([flags]) [id]",
|
||||
Short: "Erase activity",
|
||||
Long: "Erase tracked activity.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
user := GetCurrentUser()
|
||||
id := args[0]
|
||||
|
||||
err := database.EraseEntry(user, id)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err := database.EraseEntry(user, id)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("%s erased %s\n", CharInfo, color.FgLightWhite.Render(id))
|
||||
return
|
||||
},
|
||||
fmt.Printf("%s erased %s\n", CharInfo, color.FgLightWhite.Render(id))
|
||||
return
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(eraseCmd)
|
||||
rootCmd.AddCommand(eraseCmd)
|
||||
}
|
||||
|
||||
+72
-71
@@ -1,95 +1,96 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"strings"
|
||||
"encoding/json"
|
||||
"github.com/spf13/cobra"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func exportZeitJson(user string, entries []Entry) (string, error) {
|
||||
stringified, err := json.Marshal(entries)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
stringified, err := json.Marshal(entries)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(stringified), nil
|
||||
return string(stringified), nil
|
||||
}
|
||||
|
||||
func exportTymeJson(user string, entries []Entry) (string, error) {
|
||||
tyme := Tyme{}
|
||||
err := tyme.FromEntries(entries)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tyme := Tyme{}
|
||||
err := tyme.FromEntries(entries)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tyme.Stringify(), nil
|
||||
return tyme.Stringify(), nil
|
||||
}
|
||||
|
||||
var exportCmd = &cobra.Command{
|
||||
Use: "export ([flags])",
|
||||
Short: "Export tracked activities",
|
||||
Long: "Export tracked activities to various formats.",
|
||||
// Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var entries []Entry
|
||||
var err error
|
||||
Use: "export ([flags])",
|
||||
Short: "Export tracked activities",
|
||||
Long: "Export tracked activities to various formats.",
|
||||
// Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var entries []Entry
|
||||
var err error
|
||||
|
||||
user := GetCurrentUser()
|
||||
user := GetCurrentUser()
|
||||
|
||||
entries, err = database.ListEntries(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
sinceTime, untilTime := ParseSinceUntil(since, until, listRange)
|
||||
entries, err = database.ListEntries(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
sinceTime, untilTime := ParseSinceUntil(since, until, listRange)
|
||||
|
||||
var filteredEntries []Entry
|
||||
filteredEntries, err = GetFilteredEntries(entries, project, task, sinceTime, untilTime)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
var filteredEntries []Entry
|
||||
filteredEntries, err = GetFilteredEntries(entries, project, task, sinceTime, untilTime)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var output string = ""
|
||||
switch(format) {
|
||||
case "zeit":
|
||||
output, err = exportZeitJson(user, filteredEntries)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case "tyme":
|
||||
output, err = exportTymeJson(user, filteredEntries)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
default:
|
||||
fmt.Printf("%s specify an export format; see `zeit export --help` for more info\n", CharError)
|
||||
os.Exit(1)
|
||||
}
|
||||
var output string = ""
|
||||
switch format {
|
||||
case "zeit":
|
||||
output, err = exportZeitJson(user, filteredEntries)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case "tyme":
|
||||
output, err = exportTymeJson(user, filteredEntries)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
default:
|
||||
fmt.Printf("%s specify an export format; see `zeit export --help` for more info\n", CharError)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", output)
|
||||
return
|
||||
},
|
||||
fmt.Printf("%s\n", output)
|
||||
return
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(exportCmd)
|
||||
exportCmd.Flags().StringVar(&format, "format", "zeit", "Format to export, possible values: zeit, tyme")
|
||||
exportCmd.Flags().StringVar(&since, "since", "", "Date/time to start the export from")
|
||||
exportCmd.Flags().StringVar(&until, "until", "", "Date/time to export until")
|
||||
exportCmd.Flags().StringVar(&listRange, "range", "", "Shortcut for --since and --until that accepts: " + strings.Join(Ranges(), ", "))
|
||||
exportCmd.Flags().StringVarP(&project, "project", "p", "", "Project to be exported")
|
||||
exportCmd.Flags().StringVarP(&task, "task", "t", "", "Task to be exported")
|
||||
rootCmd.AddCommand(exportCmd)
|
||||
exportCmd.Flags().StringVar(&format, "format", "zeit", "Format to export, possible values: zeit, tyme")
|
||||
exportCmd.Flags().StringVar(&since, "since", "", "Date/time to start the export from")
|
||||
exportCmd.Flags().StringVar(&until, "until", "", "Date/time to export until")
|
||||
exportCmd.Flags().StringVar(&listRange, "range", "", "Shortcut for --since and --until that accepts: "+strings.Join(Ranges(), ", "))
|
||||
exportCmd.Flags().StringVarP(&project, "project", "p", "", "Project to be exported")
|
||||
exportCmd.Flags().StringVarP(&task, "task", "t", "", "Task to be exported")
|
||||
|
||||
flagName := "task"
|
||||
exportCmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
user := GetCurrentUser()
|
||||
entries, _ := database.ListEntries(user)
|
||||
_, tasks := listProjectsAndTasks(entries)
|
||||
return tasks, cobra.ShellCompDirectiveDefault
|
||||
})
|
||||
flagName := "task"
|
||||
exportCmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
user := GetCurrentUser()
|
||||
entries, _ := database.ListEntries(user)
|
||||
_, tasks := listProjectsAndTasks(entries)
|
||||
return tasks, cobra.ShellCompDirectiveDefault
|
||||
})
|
||||
}
|
||||
|
||||
+20
-20
@@ -1,31 +1,31 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var finishCmd = &cobra.Command{
|
||||
Use: "finish",
|
||||
Short: "Finish currently running activity",
|
||||
Long: "Finishing tracking of currently running activity.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
finishTask(FinishWithMetadata)
|
||||
},
|
||||
Use: "finish",
|
||||
Short: "Finish currently running activity",
|
||||
Long: "Finishing tracking of currently running activity.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
finishTask(FinishWithMetadata)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(finishCmd)
|
||||
finishCmd.Flags().StringVarP(&begin, "begin", "b", "", "Time the activity should begin at\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).")
|
||||
finishCmd.Flags().StringVarP(&finish, "finish", "s", "", "Time the activity should finish at\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).\nMust be after --begin time.")
|
||||
finishCmd.Flags().StringVarP(&project, "project", "p", "", "Project to be assigned")
|
||||
finishCmd.Flags().StringVarP(¬es, "notes", "n", "", "Activity notes")
|
||||
finishCmd.Flags().StringVarP(&task, "task", "t", "", "Task to be assigned")
|
||||
rootCmd.AddCommand(finishCmd)
|
||||
finishCmd.Flags().StringVarP(&begin, "begin", "b", "", "Time the activity should begin at\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).")
|
||||
finishCmd.Flags().StringVarP(&finish, "finish", "s", "", "Time the activity should finish at\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).\nMust be after --begin time.")
|
||||
finishCmd.Flags().StringVarP(&project, "project", "p", "", "Project to be assigned")
|
||||
finishCmd.Flags().StringVarP(¬es, "notes", "n", "", "Activity notes")
|
||||
finishCmd.Flags().StringVarP(&task, "task", "t", "", "Task to be assigned")
|
||||
|
||||
flagName := "task"
|
||||
finishCmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
user := GetCurrentUser()
|
||||
entries, _ := database.ListEntries(user)
|
||||
_, tasks := listProjectsAndTasks(entries)
|
||||
return tasks, cobra.ShellCompDirectiveDefault
|
||||
})
|
||||
flagName := "task"
|
||||
finishCmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
user := GetCurrentUser()
|
||||
entries, _ := database.ListEntries(user)
|
||||
_, tasks := listProjectsAndTasks(entries)
|
||||
return tasks, cobra.ShellCompDirectiveDefault
|
||||
})
|
||||
}
|
||||
|
||||
+196
-198
@@ -1,264 +1,262 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"os/user"
|
||||
"os/exec"
|
||||
"bytes"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"math"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/araddon/dateparse"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/jinzhu/now"
|
||||
"github.com/araddon/dateparse"
|
||||
"github.com/jinzhu/now"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TimeFormats() []string {
|
||||
return []string{
|
||||
`^\d{1,2}:\d{1,2}(am|pm)$`, // Absolute twelve hour format
|
||||
`^\d{1,2}:\d{1,2}$`, // Absolute twenty four hour format
|
||||
`^([+-])(\d{1,2}):(\d{1,2})$`, // Relative hour:minute format
|
||||
`^([+-])(\d{1,2})\.(\d{1,2})$`, // Relative hour.fraction format
|
||||
}
|
||||
return []string{
|
||||
`^\d{1,2}:\d{1,2}(am|pm)$`, // Absolute twelve hour format
|
||||
`^\d{1,2}:\d{1,2}$`, // Absolute twenty four hour format
|
||||
`^([+-])(\d{1,2}):(\d{1,2})$`, // Relative hour:minute format
|
||||
`^([+-])(\d{1,2})\.(\d{1,2})$`, // Relative hour.fraction format
|
||||
}
|
||||
}
|
||||
|
||||
func GetCurrentUser() (string) {
|
||||
user, err := user.Current()
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
}
|
||||
func GetCurrentUser() string {
|
||||
user, err := user.Current()
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
return user.Username
|
||||
return user.Username
|
||||
}
|
||||
|
||||
func GetTimeFormat(timeStr string) (int) {
|
||||
var matched bool
|
||||
var regerr error
|
||||
func GetTimeFormat(timeStr string) int {
|
||||
var matched bool
|
||||
var regerr error
|
||||
|
||||
for timeFormatId, timeFormat := range TimeFormats() {
|
||||
matched, regerr = regexp.MatchString(timeFormat, timeStr)
|
||||
if regerr != nil {
|
||||
return -1
|
||||
}
|
||||
for timeFormatId, timeFormat := range TimeFormats() {
|
||||
matched, regerr = regexp.MatchString(timeFormat, timeStr)
|
||||
if regerr != nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
if matched == true {
|
||||
return timeFormatId
|
||||
}
|
||||
}
|
||||
if matched == true {
|
||||
return timeFormatId
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
return -1
|
||||
}
|
||||
|
||||
// TODO: Use https://golang.org/pkg/time/#ParseDuration
|
||||
func RelToTime(timeStr string, ftId int, contextTime time.Time) (time.Time, error) {
|
||||
var re = regexp.MustCompile(TimeFormats()[ftId])
|
||||
gm := re.FindStringSubmatch(timeStr)
|
||||
re := regexp.MustCompile(TimeFormats()[ftId])
|
||||
gm := re.FindStringSubmatch(timeStr)
|
||||
|
||||
if len(gm) < 4 {
|
||||
return time.Now(), errors.New("No match")
|
||||
}
|
||||
if len(gm) < 4 {
|
||||
return time.Now(), errors.New("No match")
|
||||
}
|
||||
|
||||
var hours int = 0
|
||||
var minutes int = 0
|
||||
var hours int = 0
|
||||
var minutes int = 0
|
||||
|
||||
if ftId == TFRelHourFraction {
|
||||
f, _ := strconv.ParseFloat(gm[2] + "." + gm[3], 32)
|
||||
minutes = int(f * 60.0)
|
||||
} else {
|
||||
hours, _ = strconv.Atoi(gm[2])
|
||||
minutes, _ = strconv.Atoi(gm[3])
|
||||
}
|
||||
if ftId == TFRelHourFraction {
|
||||
f, _ := strconv.ParseFloat(gm[2]+"."+gm[3], 32)
|
||||
minutes = int(f * 60.0)
|
||||
} else {
|
||||
hours, _ = strconv.Atoi(gm[2])
|
||||
minutes, _ = strconv.Atoi(gm[3])
|
||||
}
|
||||
|
||||
var t time.Time
|
||||
var t time.Time
|
||||
|
||||
if viper.IsSet("time.relative") && viper.GetString("time.relative") == "context" && !contextTime.IsZero() {
|
||||
switch gm[1] {
|
||||
case "+":
|
||||
t = contextTime.Add(time.Hour * time.Duration(hours) + time.Minute * time.Duration(minutes))
|
||||
case "-":
|
||||
t = contextTime.Add((time.Hour * time.Duration(hours) + time.Minute * time.Duration(minutes)) * -1)
|
||||
}
|
||||
if viper.IsSet("time.relative") && viper.GetString("time.relative") == "context" && !contextTime.IsZero() {
|
||||
switch gm[1] {
|
||||
case "+":
|
||||
t = contextTime.Add(time.Hour*time.Duration(hours) + time.Minute*time.Duration(minutes))
|
||||
case "-":
|
||||
t = contextTime.Add((time.Hour*time.Duration(hours) + time.Minute*time.Duration(minutes)) * -1)
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
switch gm[1] {
|
||||
case "+":
|
||||
t = time.Now().Local().Add(time.Hour * time.Duration(hours) + time.Minute * time.Duration(minutes))
|
||||
case "-":
|
||||
t = time.Now().Local().Add((time.Hour * time.Duration(hours) + time.Minute * time.Duration(minutes)) * -1)
|
||||
}
|
||||
switch gm[1] {
|
||||
case "+":
|
||||
t = time.Now().Local().Add(time.Hour*time.Duration(hours) + time.Minute*time.Duration(minutes))
|
||||
case "-":
|
||||
t = time.Now().Local().Add((time.Hour*time.Duration(hours) + time.Minute*time.Duration(minutes)) * -1)
|
||||
}
|
||||
|
||||
return t, nil
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func ParseTime(timeStr string, contextTime time.Time) (time.Time, error) {
|
||||
tfId := GetTimeFormat(timeStr)
|
||||
tfId := GetTimeFormat(timeStr)
|
||||
|
||||
t:= time.Now()
|
||||
t := time.Now()
|
||||
|
||||
switch tfId {
|
||||
case TFAbsTwelveHour:
|
||||
tadj, err := time.Parse("3:04pm", timeStr)
|
||||
tnew := time.Date(t.Year(), t.Month(), t.Day(), tadj.Hour(), tadj.Minute(), t.Second(), t.Nanosecond(), t.Location())
|
||||
return tnew, err
|
||||
case TFAbsTwentyfourHour:
|
||||
tadj, err := time.Parse("15:04", timeStr)
|
||||
tnew := time.Date(t.Year(), t.Month(), t.Day(), tadj.Hour(), tadj.Minute(), t.Second(), t.Nanosecond(), t.Location())
|
||||
return tnew, err
|
||||
case TFRelHourMinute, TFRelHourFraction:
|
||||
return RelToTime(timeStr, tfId, contextTime)
|
||||
default:
|
||||
loc, err := time.LoadLocation("Local")
|
||||
switch tfId {
|
||||
case TFAbsTwelveHour:
|
||||
tadj, err := time.Parse("3:04pm", timeStr)
|
||||
tnew := time.Date(t.Year(), t.Month(), t.Day(), tadj.Hour(), tadj.Minute(), t.Second(), t.Nanosecond(), t.Location())
|
||||
return tnew, err
|
||||
case TFAbsTwentyfourHour:
|
||||
tadj, err := time.Parse("15:04", timeStr)
|
||||
tnew := time.Date(t.Year(), t.Month(), t.Day(), tadj.Hour(), tadj.Minute(), t.Second(), t.Nanosecond(), t.Location())
|
||||
return tnew, err
|
||||
case TFRelHourMinute, TFRelHourFraction:
|
||||
return RelToTime(timeStr, tfId, contextTime)
|
||||
default:
|
||||
loc, err := time.LoadLocation("Local")
|
||||
if err != nil {
|
||||
return time.Now(), errors.New("could not load location")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return time.Now(), errors.New("could not load location")
|
||||
}
|
||||
time.Local = loc
|
||||
|
||||
time.Local = loc
|
||||
tnew, err := dateparse.ParseIn(timeStr, loc)
|
||||
if err != nil {
|
||||
return time.Now(), errors.New("could not match passed time")
|
||||
}
|
||||
|
||||
tnew, err := dateparse.ParseIn(timeStr, loc)
|
||||
if err != nil {
|
||||
return time.Now(), errors.New("could not match passed time")
|
||||
}
|
||||
|
||||
return tnew, nil
|
||||
}
|
||||
return tnew, nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetIdFromName(name string) string {
|
||||
reg, regerr := regexp.Compile("[^a-zA-Z0-9]+")
|
||||
if regerr != nil {
|
||||
return ""
|
||||
}
|
||||
reg, regerr := regexp.Compile("[^a-zA-Z0-9]+")
|
||||
if regerr != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
id := strings.ToLower(reg.ReplaceAllString(name, ""))
|
||||
id := strings.ToLower(reg.ReplaceAllString(name, ""))
|
||||
|
||||
return id
|
||||
return id
|
||||
}
|
||||
|
||||
func GetISOCalendarWeek(date time.Time) (int) {
|
||||
var _, cw = date.ISOWeek()
|
||||
return cw
|
||||
func GetISOCalendarWeek(date time.Time) int {
|
||||
_, cw := date.ISOWeek()
|
||||
return cw
|
||||
}
|
||||
|
||||
func GetISOWeekInMonth(date time.Time) (month int, weeknumber int) {
|
||||
if date.IsZero() {
|
||||
return -1, -1
|
||||
}
|
||||
if date.IsZero() {
|
||||
return -1, -1
|
||||
}
|
||||
|
||||
newDay := (date.Day() - int(date.Weekday()) + 1)
|
||||
addDay := (date.Day() - newDay) * -1
|
||||
changedDate := date.AddDate(0, 0, addDay)
|
||||
newDay := (date.Day() - int(date.Weekday()) + 1)
|
||||
addDay := (date.Day() - newDay) * -1
|
||||
changedDate := date.AddDate(0, 0, addDay)
|
||||
|
||||
return int(changedDate.Month()), int(math.Ceil(float64(changedDate.Day()) / 7.0));
|
||||
return int(changedDate.Month()), int(math.Ceil(float64(changedDate.Day()) / 7.0))
|
||||
}
|
||||
|
||||
func GetGitLog(repo string, since time.Time, until time.Time) (string, string, error) {
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd := exec.Command("git", "-C", repo, "config", "user.name")
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
gitUserStr, gitUserErrStr := string(stdout.Bytes()), string(stderr.Bytes())
|
||||
if gitUserStr == "" && gitUserErrStr != "" {
|
||||
return gitUserStr, gitUserErrStr, errors.New(gitUserErrStr)
|
||||
}
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd := exec.Command("git", "-C", repo, "config", "user.name")
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
gitUserStr, gitUserErrStr := string(stdout.Bytes()), string(stderr.Bytes())
|
||||
if gitUserStr == "" && gitUserErrStr != "" {
|
||||
return gitUserStr, gitUserErrStr, errors.New(gitUserErrStr)
|
||||
}
|
||||
|
||||
stdout.Reset()
|
||||
stderr.Reset()
|
||||
stdout.Reset()
|
||||
stderr.Reset()
|
||||
|
||||
cmd = exec.Command("git", "-C", repo, "log", "--author", gitUserStr, "--since", since.Format("2006-01-02T15:04:05-0700"), "--until", until.Format("2006-01-02T15:04:05-0700"), "--pretty=oneline")
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
cmd = exec.Command("git", "-C", repo, "log", "--author", gitUserStr, "--since", since.Format("2006-01-02T15:04:05-0700"), "--until", until.Format("2006-01-02T15:04:05-0700"), "--pretty=oneline")
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
stdoutStr, stderrStr := string(stdout.Bytes()), string(stderr.Bytes())
|
||||
return stdoutStr, stderrStr, nil
|
||||
stdoutStr, stderrStr := string(stdout.Bytes()), string(stderr.Bytes())
|
||||
return stdoutStr, stderrStr, nil
|
||||
}
|
||||
|
||||
func Ranges() []string {
|
||||
return []string{
|
||||
"today",
|
||||
"yesterday",
|
||||
"thisWeek",
|
||||
"lastWeek",
|
||||
"thisMonth",
|
||||
"lastMonth",
|
||||
}
|
||||
return []string{
|
||||
"today",
|
||||
"yesterday",
|
||||
"thisWeek",
|
||||
"lastWeek",
|
||||
"thisMonth",
|
||||
"lastMonth",
|
||||
}
|
||||
}
|
||||
|
||||
func ParseSinceUntil(since string, until string, listRange string) (time.Time, time.Time) {
|
||||
var sinceTime time.Time
|
||||
var untilTime time.Time
|
||||
var err error
|
||||
|
||||
var sinceTime time.Time
|
||||
var untilTime time.Time
|
||||
var err error
|
||||
if since != "" {
|
||||
sinceTime, err = now.Parse(since)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if since != "" {
|
||||
sinceTime, err = now.Parse(since)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
if until != "" {
|
||||
untilTime, err = now.Parse(until)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if until != "" {
|
||||
untilTime, err = now.Parse(until)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
if listRange != "" {
|
||||
if since != "" || until != "" {
|
||||
fmt.Println("Range and since/until can't be used together, select one of them")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if listRange != "" {
|
||||
if since != "" || until != "" {
|
||||
fmt.Println("Range and since/until can't be used together, select one of them")
|
||||
os.Exit(1)
|
||||
}
|
||||
if viper.GetBool("firstWeekDayMonday") {
|
||||
now.WeekStartDay = time.Monday
|
||||
}
|
||||
|
||||
if viper.GetBool("firstWeekDayMonday") {
|
||||
now.WeekStartDay = time.Monday
|
||||
}
|
||||
loc, _ := time.LoadLocation("Local")
|
||||
time.Local = loc
|
||||
switch strings.ToLower(listRange) {
|
||||
case "today":
|
||||
sinceTime = now.BeginningOfDay()
|
||||
untilTime = now.EndOfDay()
|
||||
case "yesterday":
|
||||
sinceTime = now.BeginningOfDay().AddDate(0, 0, -1)
|
||||
untilTime = now.EndOfDay().AddDate(0, 0, -1)
|
||||
case "thisweek":
|
||||
sinceTime = now.BeginningOfWeek()
|
||||
untilTime = now.EndOfWeek()
|
||||
case "lastweek":
|
||||
lastWeekDay := time.Now().AddDate(0, 0, -7)
|
||||
sinceTime = now.With(lastWeekDay).BeginningOfWeek()
|
||||
untilTime = now.With(lastWeekDay).EndOfWeek()
|
||||
case "thismonth":
|
||||
sinceTime = now.BeginningOfMonth()
|
||||
untilTime = now.EndOfMonth()
|
||||
case "lastmonth":
|
||||
lastMonthDay := time.Now().AddDate(0, -1, 0)
|
||||
sinceTime = now.With(lastMonthDay).BeginningOfMonth()
|
||||
untilTime = now.With(lastMonthDay).EndOfMonth()
|
||||
default:
|
||||
fmt.Println("Unknown range selection, possible options: ", strings.Join(Ranges(), " "))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
loc, _ := time.LoadLocation("Local")
|
||||
time.Local = loc
|
||||
switch strings.ToLower(listRange) {
|
||||
case "today":
|
||||
sinceTime = now.BeginningOfDay()
|
||||
untilTime = now.EndOfDay()
|
||||
case "yesterday":
|
||||
sinceTime = now.BeginningOfDay().AddDate(0, 0, -1)
|
||||
untilTime = now.EndOfDay().AddDate(0, 0, -1)
|
||||
case "thisweek":
|
||||
sinceTime = now.BeginningOfWeek()
|
||||
untilTime = now.EndOfWeek()
|
||||
case "lastweek":
|
||||
lastWeekDay := time.Now().AddDate(0, 0, -7)
|
||||
sinceTime = now.With(lastWeekDay).BeginningOfWeek()
|
||||
untilTime = now.With(lastWeekDay).EndOfWeek()
|
||||
case "thismonth":
|
||||
sinceTime = now.BeginningOfMonth()
|
||||
untilTime = now.EndOfMonth()
|
||||
case "lastmonth":
|
||||
lastMonthDay := time.Now().AddDate(0, -1, 0)
|
||||
sinceTime = now.With(lastMonthDay).BeginningOfMonth()
|
||||
untilTime = now.With(lastMonthDay).EndOfMonth()
|
||||
default:
|
||||
fmt.Println("Unknown range selection, possible options: ", strings.Join(Ranges(), " "))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
return sinceTime, untilTime
|
||||
return sinceTime, untilTime
|
||||
}
|
||||
|
||||
+83
-82
@@ -1,111 +1,112 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"time"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/gookit/color"
|
||||
"github.com/cnf/structhash"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/cnf/structhash"
|
||||
"github.com/gookit/color"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func importTymeJson(user string, file string) ([]Entry, error) {
|
||||
var entries []Entry
|
||||
var entries []Entry
|
||||
|
||||
tyme := Tyme{}
|
||||
tyme.Load(file)
|
||||
tyme := Tyme{}
|
||||
tyme.Load(file)
|
||||
|
||||
for _, tymeEntry := range tyme.Data {
|
||||
tymeEntrySHA1 := structhash.Sha1(tymeEntry, 1)
|
||||
tymeStart, err := time.Parse("2006-01-02T15:04:05-07:00", tymeEntry.Start)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
continue
|
||||
}
|
||||
for _, tymeEntry := range tyme.Data {
|
||||
tymeEntrySHA1 := structhash.Sha1(tymeEntry, 1)
|
||||
tymeStart, err := time.Parse("2006-01-02T15:04:05-07:00", tymeEntry.Start)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
continue
|
||||
}
|
||||
|
||||
tymeEnd, err := time.Parse("2006-01-02T15:04:05-07:00", tymeEntry.End)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
continue
|
||||
}
|
||||
tymeEnd, err := time.Parse("2006-01-02T15:04:05-07:00", tymeEntry.End)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
continue
|
||||
}
|
||||
|
||||
entry, err := NewEntry("", "", "", tymeEntry.Project, tymeEntry.Task, user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
continue
|
||||
}
|
||||
entry, err := NewEntry("", "", "", tymeEntry.Project, tymeEntry.Task, user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
continue
|
||||
}
|
||||
|
||||
entry.Begin = tymeStart
|
||||
entry.Finish = tymeEnd
|
||||
entry.Begin = tymeStart
|
||||
entry.Finish = tymeEnd
|
||||
|
||||
entry.SHA1 = fmt.Sprintf("%x", tymeEntrySHA1)
|
||||
entry.SHA1 = fmt.Sprintf("%x", tymeEntrySHA1)
|
||||
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
var importCmd = &cobra.Command{
|
||||
Use: "import ([flags]) [file]",
|
||||
Short: "Import tracked activities",
|
||||
Long: "Import tracked activities from various formats.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var entries []Entry
|
||||
var err error
|
||||
Use: "import ([flags]) [file]",
|
||||
Short: "Import tracked activities",
|
||||
Long: "Import tracked activities from various formats.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var entries []Entry
|
||||
var err error
|
||||
|
||||
user := GetCurrentUser()
|
||||
user := GetCurrentUser()
|
||||
|
||||
switch(format) {
|
||||
case "zeit":
|
||||
// TODO:
|
||||
fmt.Printf("%s not yet implemented\n", CharError)
|
||||
os.Exit(1)
|
||||
case "tyme":
|
||||
entries, err = importTymeJson(user, args[0])
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
default:
|
||||
fmt.Printf("%s specify an import format; see `zeit import --help` for more info\n", CharError)
|
||||
os.Exit(1)
|
||||
}
|
||||
switch format {
|
||||
case "zeit":
|
||||
// TODO:
|
||||
fmt.Printf("%s not yet implemented\n", CharError)
|
||||
os.Exit(1)
|
||||
case "tyme":
|
||||
entries, err = importTymeJson(user, args[0])
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
default:
|
||||
fmt.Printf("%s specify an import format; see `zeit import --help` for more info\n", CharError)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
sha1List, sha1Err := database.GetImportsSHA1List(user)
|
||||
if sha1Err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, sha1Err)
|
||||
os.Exit(1)
|
||||
}
|
||||
sha1List, sha1Err := database.GetImportsSHA1List(user)
|
||||
if sha1Err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, sha1Err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if id, ok := sha1List[entry.SHA1]; ok {
|
||||
fmt.Printf("%s %s was previously imported as %s; not importing again\n", CharInfo, color.FgLightWhite.Render(entry.SHA1), color.FgLightWhite.Render(id))
|
||||
continue
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if id, ok := sha1List[entry.SHA1]; ok {
|
||||
fmt.Printf("%s %s was previously imported as %s; not importing again\n", CharInfo, color.FgLightWhite.Render(entry.SHA1), color.FgLightWhite.Render(id))
|
||||
continue
|
||||
}
|
||||
|
||||
importedId, err := database.AddEntry(user, entry, false)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %s could not be imported: %+v\n", CharError, color.FgLightWhite.Render(entry.SHA1), color.FgRed.Render(err))
|
||||
continue
|
||||
}
|
||||
importedId, err := database.AddEntry(user, entry, false)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %s could not be imported: %+v\n", CharError, color.FgLightWhite.Render(entry.SHA1), color.FgRed.Render(err))
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s was imported as %s\n", CharInfo, color.FgLightWhite.Render(entry.SHA1), color.FgLightWhite.Render(importedId))
|
||||
sha1List[entry.SHA1] = importedId
|
||||
}
|
||||
fmt.Printf("%s %s was imported as %s\n", CharInfo, color.FgLightWhite.Render(entry.SHA1), color.FgLightWhite.Render(importedId))
|
||||
sha1List[entry.SHA1] = importedId
|
||||
}
|
||||
|
||||
err = database.UpdateImportsSHA1List(user, sha1List)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = database.UpdateImportsSHA1List(user, sha1List)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
return
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(importCmd)
|
||||
importCmd.Flags().StringVar(&format, "format", "zeit", "Format to import, possible values: zeit, tyme")
|
||||
rootCmd.AddCommand(importCmd)
|
||||
importCmd.Flags().StringVar(&format, "format", "zeit", "Format to import, possible values: zeit, tyme")
|
||||
}
|
||||
|
||||
+43
-43
@@ -1,58 +1,58 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var listTotalTime bool
|
||||
var listOnlyProjectsAndTasks bool
|
||||
var listOnlyTasks bool
|
||||
var appendProjectIDToTask bool
|
||||
|
||||
var (
|
||||
listTotalTime bool
|
||||
listOnlyProjectsAndTasks bool
|
||||
listOnlyTasks bool
|
||||
appendProjectIDToTask bool
|
||||
)
|
||||
|
||||
var listCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List activities",
|
||||
Long: "List all tracked activities.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Use: "list",
|
||||
Short: "List activities",
|
||||
Long: "List all tracked activities.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
filteredEntries := listEntries()
|
||||
|
||||
filteredEntries := listEntries()
|
||||
totalHours := decimal.NewFromInt(0)
|
||||
for _, entry := range filteredEntries {
|
||||
totalHours = totalHours.Add(entry.GetDuration())
|
||||
fmt.Printf("%s\n", entry.GetOutput(false))
|
||||
}
|
||||
|
||||
totalHours := decimal.NewFromInt(0)
|
||||
for _, entry := range filteredEntries {
|
||||
totalHours = totalHours.Add(entry.GetDuration())
|
||||
fmt.Printf("%s\n", entry.GetOutput(false))
|
||||
}
|
||||
|
||||
if listTotalTime == true {
|
||||
fmt.Printf("\nTOTAL: %s H\n\n", fmtHours(totalHours));
|
||||
}
|
||||
return
|
||||
},
|
||||
if listTotalTime == true {
|
||||
fmt.Printf("\nTOTAL: %s H\n\n", fmtHours(totalHours))
|
||||
}
|
||||
return
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(listCmd)
|
||||
listCmd.Flags().StringVar(&since, "since", "", "Date/time to start the list from")
|
||||
listCmd.Flags().StringVar(&until, "until", "", "Date/time to list until")
|
||||
listCmd.Flags().StringVar(&listRange, "range", "", "Shortcut for --since and --until that accepts: " + strings.Join(Ranges(), ", "))
|
||||
listCmd.Flags().StringVarP(&project, "project", "p", "", "Project to be listed")
|
||||
listCmd.Flags().StringVarP(&task, "task", "t", "", "Task to be listed")
|
||||
listCmd.Flags().BoolVar(&fractional, "decimal", false, "Show fractional hours in decimal format instead of minutes")
|
||||
listCmd.Flags().BoolVar(&listTotalTime, "total", false, "Show total time of hours for listed activities")
|
||||
listCmd.Flags().BoolVar(&listOnlyProjectsAndTasks, "only-projects-and-tasks", false, "Only list projects and their tasks, no entries")
|
||||
listCmd.Flags().BoolVar(&listOnlyTasks, "only-tasks", false, "Only list tasks, no projects nor entries")
|
||||
listCmd.Flags().BoolVar(&appendProjectIDToTask, "append-project-id-to-task", false, "Append project ID to tasks in the list")
|
||||
rootCmd.AddCommand(listCmd)
|
||||
listCmd.Flags().StringVar(&since, "since", "", "Date/time to start the list from")
|
||||
listCmd.Flags().StringVar(&until, "until", "", "Date/time to list until")
|
||||
listCmd.Flags().StringVar(&listRange, "range", "", "Shortcut for --since and --until that accepts: "+strings.Join(Ranges(), ", "))
|
||||
listCmd.Flags().StringVarP(&project, "project", "p", "", "Project to be listed")
|
||||
listCmd.Flags().StringVarP(&task, "task", "t", "", "Task to be listed")
|
||||
listCmd.Flags().BoolVar(&fractional, "decimal", false, "Show fractional hours in decimal format instead of minutes")
|
||||
listCmd.Flags().BoolVar(&listTotalTime, "total", false, "Show total time of hours for listed activities")
|
||||
listCmd.Flags().BoolVar(&listOnlyProjectsAndTasks, "only-projects-and-tasks", false, "Only list projects and their tasks, no entries")
|
||||
listCmd.Flags().BoolVar(&listOnlyTasks, "only-tasks", false, "Only list tasks, no projects nor entries")
|
||||
listCmd.Flags().BoolVar(&appendProjectIDToTask, "append-project-id-to-task", false, "Append project ID to tasks in the list")
|
||||
|
||||
flagName := "task"
|
||||
listCmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
user := GetCurrentUser()
|
||||
entries, _ := database.ListEntries(user)
|
||||
_, tasks := listProjectsAndTasks(entries)
|
||||
return tasks, cobra.ShellCompDirectiveDefault
|
||||
})
|
||||
flagName := "task"
|
||||
listCmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
user := GetCurrentUser()
|
||||
entries, _ := database.ListEntries(user)
|
||||
_, tasks := listProjectsAndTasks(entries)
|
||||
return tasks, cobra.ShellCompDirectiveDefault
|
||||
})
|
||||
}
|
||||
|
||||
+2
-5
@@ -1,9 +1,6 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
)
|
||||
|
||||
type Project struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Color string `json:"color,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Color string `json:"color,omitempty"`
|
||||
}
|
||||
|
||||
+31
-31
@@ -1,48 +1,48 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
// "time"
|
||||
"github.com/spf13/cobra"
|
||||
// "github.com/gookit/color"
|
||||
"fmt"
|
||||
"os"
|
||||
// "time"
|
||||
"github.com/spf13/cobra"
|
||||
// "github.com/gookit/color"
|
||||
)
|
||||
|
||||
var projectColor string
|
||||
|
||||
var projectCmd = &cobra.Command{
|
||||
Use: "project ([flags]) [project]",
|
||||
Short: "Project settings",
|
||||
Long: "Configure project settings.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
user := GetCurrentUser()
|
||||
projectName := args[0]
|
||||
Use: "project ([flags]) [project]",
|
||||
Short: "Project settings",
|
||||
Long: "Configure project settings.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
user := GetCurrentUser()
|
||||
projectName := args[0]
|
||||
|
||||
project, err := database.GetProject(user, projectName)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
project, err := database.GetProject(user, projectName)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
project.Name = projectName
|
||||
project.Name = projectName
|
||||
|
||||
if projectColor != "" {
|
||||
project.Color = projectColor
|
||||
}
|
||||
if projectColor != "" {
|
||||
project.Color = projectColor
|
||||
}
|
||||
|
||||
err = database.UpdateProject(user, projectName, project)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = database.UpdateProject(user, projectName, project)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("%s project updated\n", CharInfo)
|
||||
return
|
||||
},
|
||||
fmt.Printf("%s project updated\n", CharInfo)
|
||||
return
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(projectCmd)
|
||||
projectCmd.Flags().StringVarP(&projectColor, "color", "c", "", "Set the color of the project (hex code, e.g. #121212)")
|
||||
rootCmd.AddCommand(projectCmd)
|
||||
projectCmd.Flags().StringVarP(&projectColor, "color", "c", "", "Set the color of the project (hex code, e.g. #121212)")
|
||||
}
|
||||
|
||||
+10
-10
@@ -1,21 +1,21 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var resumeCmd = &cobra.Command{
|
||||
Use: "resume",
|
||||
Short: "Resume last task",
|
||||
Long: "Track new activity with all parameters of the last task (based on begin time)",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
resumeTask(1)
|
||||
},
|
||||
Use: "resume",
|
||||
Short: "Resume last task",
|
||||
Long: "Track new activity with all parameters of the last task (based on begin time)",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
resumeTask(1)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(resumeCmd)
|
||||
rootCmd.AddCommand(resumeCmd)
|
||||
|
||||
resumeCmd.Flags().StringVarP(&begin, "begin", "b", "", "Time the activity should begin at\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).")
|
||||
resumeCmd.Flags().StringVarP(&finish, "finish", "s", "", "Time the activity should finish at\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).\nMust be after --begin time.")
|
||||
resumeCmd.Flags().StringVarP(&begin, "begin", "b", "", "Time the activity should begin at\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).")
|
||||
resumeCmd.Flags().StringVarP(&finish, "finish", "s", "", "Time the activity should finish at\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).\nMust be after --begin time.")
|
||||
}
|
||||
|
||||
+80
-71
@@ -1,104 +1,113 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/gookit/color"
|
||||
"os"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var database *Database
|
||||
|
||||
var begin string
|
||||
var finish string
|
||||
var switchString string
|
||||
var project string
|
||||
var task string
|
||||
var notes string
|
||||
var (
|
||||
begin string
|
||||
finish string
|
||||
switchString string
|
||||
project string
|
||||
task string
|
||||
notes string
|
||||
)
|
||||
|
||||
var since string
|
||||
var until string
|
||||
var listRange string
|
||||
var (
|
||||
since string
|
||||
until string
|
||||
listRange string
|
||||
)
|
||||
|
||||
var format string
|
||||
var force bool
|
||||
var (
|
||||
format string
|
||||
force bool
|
||||
)
|
||||
|
||||
var noColors bool
|
||||
var debug bool
|
||||
var cfgFile string
|
||||
var (
|
||||
noColors bool
|
||||
debug bool
|
||||
cfgFile string
|
||||
)
|
||||
|
||||
const(
|
||||
CharTrack = " ▶"
|
||||
CharFinish = " ■"
|
||||
CharErase = " ◀"
|
||||
CharError = " ▲"
|
||||
CharInfo = " ●"
|
||||
CharMore = " ◆"
|
||||
const (
|
||||
CharTrack = " ▶"
|
||||
CharFinish = " ■"
|
||||
CharErase = " ◀"
|
||||
CharError = " ▲"
|
||||
CharInfo = " ●"
|
||||
CharMore = " ◆"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "zeit",
|
||||
Short: "Command line Zeiterfassung",
|
||||
Long: `A command line time tracker.`,
|
||||
Use: "zeit",
|
||||
Short: "Command line Zeiterfassung",
|
||||
Long: `A command line time tracker.`,
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $XDG_CONFIG_HOME/zeit.[yaml|toml")
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $XDG_CONFIG_HOME/zeit.[yaml|toml")
|
||||
|
||||
rootCmd.PersistentFlags().BoolVar(&noColors, FlagNoColors, false, "Do not use colors in output")
|
||||
viper.BindPFlag(FlagNoColors, rootCmd.PersistentFlags().Lookup(FlagNoColors))
|
||||
rootCmd.PersistentFlags().BoolVar(&noColors, FlagNoColors, false, "Do not use colors in output")
|
||||
viper.BindPFlag(FlagNoColors, rootCmd.PersistentFlags().Lookup(FlagNoColors))
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(&debug, FlagDebug, "d", false, "Display debugging output in the console. (default: false)")
|
||||
viper.BindPFlag(FlagDebug, rootCmd.PersistentFlags().Lookup(FlagDebug))
|
||||
rootCmd.PersistentFlags().BoolVarP(&debug, FlagDebug, "d", false, "Display debugging output in the console. (default: false)")
|
||||
viper.BindPFlag(FlagDebug, rootCmd.PersistentFlags().Lookup(FlagDebug))
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
if noColors == true {
|
||||
color.Disable()
|
||||
}
|
||||
if noColors == true {
|
||||
color.Disable()
|
||||
}
|
||||
|
||||
viper.SetEnvPrefix("zeit")
|
||||
viper.BindEnv("db")
|
||||
viper.SetEnvPrefix("zeit")
|
||||
viper.BindEnv("db")
|
||||
|
||||
if cfgFile != "" {
|
||||
// Use config file from the flag.
|
||||
viper.SetConfigFile(cfgFile)
|
||||
} else {
|
||||
// Find home directory.
|
||||
home, err := os.UserHomeDir()
|
||||
cobra.CheckErr(err)
|
||||
if cfgFile != "" {
|
||||
// Use config file from the flag.
|
||||
viper.SetConfigFile(cfgFile)
|
||||
} else {
|
||||
// Find home directory.
|
||||
home, err := os.UserHomeDir()
|
||||
cobra.CheckErr(err)
|
||||
|
||||
viper.AddConfigPath("$XDG_CONFIG_HOME")
|
||||
viper.AddConfigPath("$XDG_CONFIG_HOME/zeit")
|
||||
viper.AddConfigPath(home + "/.config")
|
||||
viper.AddConfigPath(home + "/.config/zeit")
|
||||
viper.SetConfigName("zeit")
|
||||
}
|
||||
viper.AddConfigPath("$XDG_CONFIG_HOME")
|
||||
viper.AddConfigPath("$XDG_CONFIG_HOME/zeit")
|
||||
viper.AddConfigPath(home + "/.config")
|
||||
viper.AddConfigPath(home + "/.config/zeit")
|
||||
viper.SetConfigName("zeit")
|
||||
}
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
// Set default values for parameters
|
||||
viper.Set("debug", false)
|
||||
}
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
// Set default values for parameters
|
||||
viper.Set("debug", false)
|
||||
}
|
||||
|
||||
if viper.GetBool("debug") {
|
||||
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
|
||||
fmt.Fprintln(os.Stderr, "Using Database file:", viper.GetString("db"))
|
||||
}
|
||||
if viper.GetBool("debug") {
|
||||
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
|
||||
fmt.Fprintln(os.Stderr, "Using Database file:", viper.GetString("db"))
|
||||
}
|
||||
|
||||
var err error
|
||||
database, err = InitDatabase()
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
var err error
|
||||
database, err = InitDatabase()
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
+40
-39
@@ -1,55 +1,56 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"time"
|
||||
"strings"
|
||||
"github.com/spf13/cobra"
|
||||
// "github.com/shopspring/decimal"
|
||||
// "github.com/gookit/color"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
// "github.com/shopspring/decimal"
|
||||
// "github.com/gookit/color"
|
||||
)
|
||||
|
||||
var statsCmd = &cobra.Command{
|
||||
Use: "stats",
|
||||
Short: "Display activity statistics",
|
||||
Long: "Display statistics on all tracked activities.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
user := GetCurrentUser()
|
||||
Use: "stats",
|
||||
Short: "Display activity statistics",
|
||||
Long: "Display statistics on all tracked activities.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
user := GetCurrentUser()
|
||||
|
||||
entries, err := database.ListEntries(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
entries, err := database.ListEntries(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cal, _ := NewCalendar(entries)
|
||||
cal, _ := NewCalendar(entries)
|
||||
|
||||
weekMinus0 := time.Now()
|
||||
monthMinus0, weeknumberMinus0 := GetISOWeekInMonth(weekMinus0)
|
||||
monthMinus00 := monthMinus0 - 1
|
||||
weeknumberMinus00 := weeknumberMinus0 - 1
|
||||
thisWeek := cal.GetOutputForWeekCalendar(weekMinus0, monthMinus00, weeknumberMinus00)
|
||||
weekMinus0 := time.Now()
|
||||
monthMinus0, weeknumberMinus0 := GetISOWeekInMonth(weekMinus0)
|
||||
monthMinus00 := monthMinus0 - 1
|
||||
weeknumberMinus00 := weeknumberMinus0 - 1
|
||||
thisWeek := cal.GetOutputForWeekCalendar(weekMinus0, monthMinus00, weeknumberMinus00)
|
||||
|
||||
weekMinus1 := weekMinus0.AddDate(0, 0, -7)
|
||||
monthMinus1, weeknumberMinus1 := GetISOWeekInMonth(weekMinus1)
|
||||
monthMinus10 := monthMinus1 - 1
|
||||
weeknumberMinus10 := weeknumberMinus1 - 1
|
||||
previousWeek := cal.GetOutputForWeekCalendar(weekMinus1, monthMinus10, weeknumberMinus10)
|
||||
weekMinus1 := weekMinus0.AddDate(0, 0, -7)
|
||||
monthMinus1, weeknumberMinus1 := GetISOWeekInMonth(weekMinus1)
|
||||
monthMinus10 := monthMinus1 - 1
|
||||
weeknumberMinus10 := weeknumberMinus1 - 1
|
||||
previousWeek := cal.GetOutputForWeekCalendar(weekMinus1, monthMinus10, weeknumberMinus10)
|
||||
|
||||
if monthMinus00 == monthMinus10 {
|
||||
fmt.Printf("\n%s\n\n", strings.ToUpper(weekMinus0.Month().String()))
|
||||
} else {
|
||||
fmt.Printf("\n%s / %s\n\n", strings.ToUpper(weekMinus0.Month().String()), strings.ToUpper(weekMinus1.Month().String()))
|
||||
}
|
||||
fmt.Printf("%s\n\n\n", OutputAppendRight(thisWeek, previousWeek, 16))
|
||||
fmt.Printf("%s\n", cal.GetOutputForDistribution())
|
||||
if monthMinus00 == monthMinus10 {
|
||||
fmt.Printf("\n%s\n\n", strings.ToUpper(weekMinus0.Month().String()))
|
||||
} else {
|
||||
fmt.Printf("\n%s / %s\n\n", strings.ToUpper(weekMinus0.Month().String()), strings.ToUpper(weekMinus1.Month().String()))
|
||||
}
|
||||
fmt.Printf("%s\n\n\n", OutputAppendRight(thisWeek, previousWeek, 16))
|
||||
fmt.Printf("%s\n", cal.GetOutputForDistribution())
|
||||
|
||||
return
|
||||
},
|
||||
return
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(statsCmd)
|
||||
statsCmd.Flags().BoolVar(&fractional, "decimal", false, "Show fractional hours in decimal format instead of minutes")
|
||||
rootCmd.AddCommand(statsCmd)
|
||||
statsCmd.Flags().BoolVar(&fractional, "decimal", false, "Show fractional hours in decimal format instead of minutes")
|
||||
}
|
||||
|
||||
+13
-14
@@ -1,26 +1,25 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var switchBackCmd = &cobra.Command{
|
||||
Use: "switchback",
|
||||
Short: "switchback to the task before the last one",
|
||||
Long: "End running activity and resume the task which was before, which can either be kept running until 'finish' is being called or parameterized to be a finished activity.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Use: "switchback",
|
||||
Short: "switchback to the task before the last one",
|
||||
Long: "End running activity and resume the task which was before, which can either be kept running until 'finish' is being called or parameterized to be a finished activity.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
finish = switchString
|
||||
finishTask(FinishOnlyTime)
|
||||
|
||||
finish = switchString
|
||||
finishTask(FinishOnlyTime)
|
||||
|
||||
finish = ""
|
||||
begin = switchString
|
||||
resumeTask(2)
|
||||
},
|
||||
finish = ""
|
||||
begin = switchString
|
||||
resumeTask(2)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(switchBackCmd)
|
||||
rootCmd.AddCommand(switchBackCmd)
|
||||
|
||||
switchBackCmd.Flags().StringVarP(&switchString, "begin", "b", "", "Time the new activity should begin at and the old one ends\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).")
|
||||
switchBackCmd.Flags().StringVarP(&switchString, "begin", "b", "", "Time the new activity should begin at and the old one ends\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).")
|
||||
}
|
||||
|
||||
+23
-25
@@ -1,38 +1,36 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var switchCmd = &cobra.Command{
|
||||
Use: "switch",
|
||||
Short: "switch to another task",
|
||||
Long: "End running activity and track new activity, which can either be kept running until 'finish' is being called or parameterized to be a finished activity.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Use: "switch",
|
||||
Short: "switch to another task",
|
||||
Long: "End running activity and track new activity, which can either be kept running until 'finish' is being called or parameterized to be a finished activity.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
finish = switchString
|
||||
finishTask(FinishOnlyTime)
|
||||
|
||||
finish = switchString
|
||||
finishTask(FinishOnlyTime)
|
||||
|
||||
finish = ""
|
||||
begin = switchString
|
||||
trackTask()
|
||||
},
|
||||
finish = ""
|
||||
begin = switchString
|
||||
trackTask()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(switchCmd)
|
||||
rootCmd.AddCommand(switchCmd)
|
||||
|
||||
switchCmd.Flags().StringVarP(&switchString, "begin", "b", "", "Time the new activity should begin at and the old one ends\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).")
|
||||
switchCmd.Flags().StringVarP(&project, "project", "p", "", "Project to be assigned")
|
||||
switchCmd.Flags().StringVarP(&task, "task", "t", "", "Task to be assigned")
|
||||
switchCmd.Flags().StringVarP(¬es, "notes", "n", "", "Activity notes")
|
||||
switchCmd.Flags().StringVarP(&switchString, "begin", "b", "", "Time the new activity should begin at and the old one ends\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).")
|
||||
switchCmd.Flags().StringVarP(&project, "project", "p", "", "Project to be assigned")
|
||||
switchCmd.Flags().StringVarP(&task, "task", "t", "", "Task to be assigned")
|
||||
switchCmd.Flags().StringVarP(¬es, "notes", "n", "", "Activity notes")
|
||||
|
||||
flagName := "task"
|
||||
switchCmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
user := GetCurrentUser()
|
||||
entries, _ := database.ListEntries(user)
|
||||
_, tasks := listProjectsAndTasks(entries)
|
||||
return tasks, cobra.ShellCompDirectiveDefault
|
||||
})
|
||||
flagName := "task"
|
||||
switchCmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
user := GetCurrentUser()
|
||||
entries, _ := database.ListEntries(user)
|
||||
_, tasks := listProjectsAndTasks(entries)
|
||||
return tasks, cobra.ShellCompDirectiveDefault
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,272 +1,268 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Task struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
GitRepository string `json:"gitRepository,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
GitRepository string `json:"gitRepository,omitempty"`
|
||||
}
|
||||
|
||||
func listEntries() []Entry {
|
||||
user := GetCurrentUser()
|
||||
user := GetCurrentUser()
|
||||
|
||||
entries, err := database.ListEntries(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
entries, err := database.ListEntries(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
sinceTime, untilTime := ParseSinceUntil(since, until, listRange)
|
||||
sinceTime, untilTime := ParseSinceUntil(since, until, listRange)
|
||||
|
||||
var filteredEntries []Entry
|
||||
filteredEntries, err = GetFilteredEntries(entries, project, task, sinceTime, untilTime)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
var filteredEntries []Entry
|
||||
filteredEntries, err = GetFilteredEntries(entries, project, task, sinceTime, untilTime)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if listOnlyProjectsAndTasks || listOnlyTasks {
|
||||
printProjects(filteredEntries)
|
||||
return nil
|
||||
}
|
||||
return filteredEntries
|
||||
if listOnlyProjectsAndTasks || listOnlyTasks {
|
||||
printProjects(filteredEntries)
|
||||
return nil
|
||||
}
|
||||
return filteredEntries
|
||||
}
|
||||
|
||||
func printProjects(entries []Entry) {
|
||||
projectsAndTasks, _ := listProjectsAndTasks(entries)
|
||||
for project := range projectsAndTasks {
|
||||
if listOnlyProjectsAndTasks && !listOnlyTasks {
|
||||
fmt.Printf("%s %s\n", CharMore, project)
|
||||
}
|
||||
|
||||
projectsAndTasks, _ := listProjectsAndTasks(entries)
|
||||
for project := range projectsAndTasks {
|
||||
if listOnlyProjectsAndTasks && !listOnlyTasks {
|
||||
fmt.Printf("%s %s\n", CharMore, project)
|
||||
}
|
||||
for task := range projectsAndTasks[project] {
|
||||
if listOnlyProjectsAndTasks && !listOnlyTasks {
|
||||
fmt.Printf("%*s└── ", 1, " ")
|
||||
}
|
||||
|
||||
for task := range projectsAndTasks[project] {
|
||||
if listOnlyProjectsAndTasks && !listOnlyTasks {
|
||||
fmt.Printf("%*s└── ", 1, " ")
|
||||
}
|
||||
|
||||
if appendProjectIDToTask {
|
||||
fmt.Printf("%s [%s]\n", task, project)
|
||||
} else {
|
||||
fmt.Printf("%s\n", task)
|
||||
}
|
||||
}
|
||||
}
|
||||
if appendProjectIDToTask {
|
||||
fmt.Printf("%s [%s]\n", task, project)
|
||||
} else {
|
||||
fmt.Printf("%s\n", task)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listProjectsAndTasks(entries []Entry) (map[string]map[string]bool, []string) {
|
||||
var projectsAndTasks = make(map[string]map[string]bool)
|
||||
var allTasks []string
|
||||
projectsAndTasks := make(map[string]map[string]bool)
|
||||
var allTasks []string
|
||||
|
||||
for _, filteredEntry := range entries {
|
||||
taskMap, ok := projectsAndTasks[filteredEntry.Project]
|
||||
for _, filteredEntry := range entries {
|
||||
taskMap, ok := projectsAndTasks[filteredEntry.Project]
|
||||
|
||||
if !ok {
|
||||
taskMap = make(map[string]bool)
|
||||
projectsAndTasks[filteredEntry.Project] = taskMap
|
||||
}
|
||||
if !ok {
|
||||
taskMap = make(map[string]bool)
|
||||
projectsAndTasks[filteredEntry.Project] = taskMap
|
||||
}
|
||||
|
||||
taskMap[filteredEntry.Task] = true
|
||||
projectsAndTasks[filteredEntry.Project] = taskMap
|
||||
allTasks = append(allTasks, filteredEntry.Task)
|
||||
}
|
||||
taskMap[filteredEntry.Task] = true
|
||||
projectsAndTasks[filteredEntry.Project] = taskMap
|
||||
allTasks = append(allTasks, filteredEntry.Task)
|
||||
}
|
||||
|
||||
return projectsAndTasks, allTasks
|
||||
return projectsAndTasks, allTasks
|
||||
}
|
||||
|
||||
func trackTask() {
|
||||
user := GetCurrentUser()
|
||||
user := GetCurrentUser()
|
||||
|
||||
runningEntryId, err := database.GetRunningEntryId(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
runningEntryId, err := database.GetRunningEntryId(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if runningEntryId != "" {
|
||||
fmt.Printf("%s a task is already running\n", CharTrack)
|
||||
os.Exit(1)
|
||||
}
|
||||
if runningEntryId != "" {
|
||||
fmt.Printf("%s a task is already running\n", CharTrack)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if project == "" && viper.GetString("project.default") != "" {
|
||||
project = viper.GetString("project.default")
|
||||
}
|
||||
if project == "" && viper.GetString("project.default") != "" {
|
||||
project = viper.GetString("project.default")
|
||||
}
|
||||
|
||||
if project == "" && viper.GetBool("project.mandatory") {
|
||||
fmt.Println("project is mandatory but missing")
|
||||
os.Exit(1)
|
||||
}
|
||||
if project == "" && viper.GetBool("project.mandatory") {
|
||||
fmt.Println("project is mandatory but missing")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if task == "" && viper.GetBool("task.mandatory") {
|
||||
fmt.Println("task is mandatory but missing")
|
||||
os.Exit(1)
|
||||
}
|
||||
if task == "" && viper.GetBool("task.mandatory") {
|
||||
fmt.Println("task is mandatory but missing")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
newEntry, err := NewEntry("", begin, finish, project, task, user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
newEntry, err := NewEntry("", begin, finish, project, task, user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if notes != "" {
|
||||
newEntry.Notes = notes
|
||||
}
|
||||
if notes != "" {
|
||||
newEntry.Notes = notes
|
||||
}
|
||||
|
||||
isRunning := newEntry.Finish.IsZero()
|
||||
isRunning := newEntry.Finish.IsZero()
|
||||
|
||||
_, err = database.AddEntry(user, newEntry, isRunning)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
_, err = database.AddEntry(user, newEntry, isRunning)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Print(newEntry.GetOutputForTrack(isRunning, false))
|
||||
fmt.Print(newEntry.GetOutputForTrack(isRunning, false))
|
||||
}
|
||||
|
||||
func finishTask(mode int) {
|
||||
user := GetCurrentUser()
|
||||
|
||||
user := GetCurrentUser()
|
||||
runningEntryId, err := database.GetRunningEntryId(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
runningEntryId, err := database.GetRunningEntryId(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if runningEntryId == "" {
|
||||
fmt.Printf("%s not running\n", CharFinish)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if runningEntryId == "" {
|
||||
fmt.Printf("%s not running\n", CharFinish)
|
||||
os.Exit(1)
|
||||
}
|
||||
runningEntry, err := database.GetEntry(user, runningEntryId)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
runningEntry, err := database.GetEntry(user, runningEntryId)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
tmpEntry, err := NewEntry(runningEntry.ID, begin, finish, project, task, user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
tmpEntry, err := NewEntry(runningEntry.ID, begin, finish, project, task, user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if begin != "" {
|
||||
runningEntry.Begin = tmpEntry.Begin
|
||||
}
|
||||
|
||||
if begin != "" {
|
||||
runningEntry.Begin = tmpEntry.Begin
|
||||
}
|
||||
if finish != "" {
|
||||
runningEntry.Finish = tmpEntry.Finish
|
||||
} else {
|
||||
runningEntry.Finish = time.Now()
|
||||
}
|
||||
|
||||
if finish != "" {
|
||||
runningEntry.Finish = tmpEntry.Finish
|
||||
} else {
|
||||
runningEntry.Finish = time.Now()
|
||||
}
|
||||
if mode == FinishWithMetadata {
|
||||
finishTaskMetadata(user, &runningEntry, &tmpEntry)
|
||||
}
|
||||
|
||||
if mode == FinishWithMetadata {
|
||||
finishTaskMetadata(user, &runningEntry, &tmpEntry)
|
||||
}
|
||||
if !runningEntry.IsFinishedAfterBegan() {
|
||||
fmt.Printf("%s %+v\n", CharError, "beginning time of tracking cannot be after finish time")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !runningEntry.IsFinishedAfterBegan() {
|
||||
fmt.Printf("%s %+v\n", CharError, "beginning time of tracking cannot be after finish time")
|
||||
os.Exit(1)
|
||||
}
|
||||
_, err = database.FinishEntry(user, runningEntry)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, err = database.FinishEntry(user, runningEntry)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Print(runningEntry.GetOutputForFinish())
|
||||
fmt.Print(runningEntry.GetOutputForFinish())
|
||||
}
|
||||
|
||||
func finishTaskMetadata(user string, runningEntry *Entry, tmpEntry *Entry) {
|
||||
if project != "" {
|
||||
runningEntry.Project = tmpEntry.Project
|
||||
}
|
||||
|
||||
if project != "" {
|
||||
runningEntry.Project = tmpEntry.Project
|
||||
}
|
||||
if task != "" {
|
||||
runningEntry.Task = tmpEntry.Task
|
||||
}
|
||||
|
||||
if task != "" {
|
||||
runningEntry.Task = tmpEntry.Task
|
||||
}
|
||||
if notes != "" {
|
||||
runningEntry.Notes = fmt.Sprintf("%s\n%s", runningEntry.Notes, notes)
|
||||
}
|
||||
|
||||
if notes != "" {
|
||||
runningEntry.Notes = fmt.Sprintf("%s\n%s", runningEntry.Notes, notes)
|
||||
}
|
||||
|
||||
if runningEntry.Task != "" {
|
||||
task, err := database.GetTask(user, runningEntry.Task)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
taskGit(&task, runningEntry)
|
||||
}
|
||||
if runningEntry.Task != "" {
|
||||
task, err := database.GetTask(user, runningEntry.Task)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
taskGit(&task, runningEntry)
|
||||
}
|
||||
}
|
||||
|
||||
func taskGit(task *Task, runningEntry *Entry) {
|
||||
if task.GitRepository != "" && task.GitRepository != "-" {
|
||||
stdout, stderr, err := GetGitLog(task.GitRepository, runningEntry.Begin, runningEntry.Finish)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if task.GitRepository != "" && task.GitRepository != "-" {
|
||||
stdout, stderr, err := GetGitLog(task.GitRepository, runningEntry.Begin, runningEntry.Finish)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if stderr == "" {
|
||||
runningEntry.Notes = fmt.Sprintf("%s\n%s", runningEntry.Notes, stdout)
|
||||
} else {
|
||||
fmt.Printf("%s notes were not imported: %+v\n", CharError, stderr)
|
||||
}
|
||||
}
|
||||
if stderr == "" {
|
||||
runningEntry.Notes = fmt.Sprintf("%s\n%s", runningEntry.Notes, stdout)
|
||||
} else {
|
||||
fmt.Printf("%s notes were not imported: %+v\n", CharError, stderr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func resumeTask(index int) {
|
||||
user := GetCurrentUser()
|
||||
user := GetCurrentUser()
|
||||
|
||||
entries, err := database.ListEntries(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
lastEntry := entries[len(entries)-index]
|
||||
entries, err := database.ListEntries(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
lastEntry := entries[len(entries)-index]
|
||||
|
||||
runningEntryId, err := database.GetRunningEntryId(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
runningEntryId, err := database.GetRunningEntryId(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if runningEntryId != "" {
|
||||
fmt.Printf("%s a task is already running\n", CharTrack)
|
||||
os.Exit(1)
|
||||
}
|
||||
if runningEntryId != "" {
|
||||
fmt.Printf("%s a task is already running\n", CharTrack)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
project = lastEntry.Project
|
||||
task = lastEntry.Task
|
||||
project = lastEntry.Project
|
||||
task = lastEntry.Task
|
||||
|
||||
newEntry, err := NewEntry("", begin, finish, project, task, user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
newEntry, err := NewEntry("", begin, finish, project, task, user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if lastEntry.Notes != "" {
|
||||
newEntry.Notes = lastEntry.Notes
|
||||
}
|
||||
if lastEntry.Notes != "" {
|
||||
newEntry.Notes = lastEntry.Notes
|
||||
}
|
||||
|
||||
isRunning := newEntry.Finish.IsZero()
|
||||
isRunning := newEntry.Finish.IsZero()
|
||||
|
||||
_, err = database.AddEntry(user, newEntry, isRunning)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
_, err = database.AddEntry(user, newEntry, isRunning)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Print(newEntry.GetOutputForTrack(isRunning, false))
|
||||
fmt.Print(newEntry.GetOutputForTrack(isRunning, false))
|
||||
}
|
||||
|
||||
+31
-31
@@ -1,48 +1,48 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
// "time"
|
||||
"github.com/spf13/cobra"
|
||||
// "github.com/gookit/color"
|
||||
"fmt"
|
||||
"os"
|
||||
// "time"
|
||||
"github.com/spf13/cobra"
|
||||
// "github.com/gookit/color"
|
||||
)
|
||||
|
||||
var taskGitRepository string
|
||||
|
||||
var taskCmd = &cobra.Command{
|
||||
Use: "task ([flags]) [task]",
|
||||
Short: "Task settings",
|
||||
Long: "Configure task settings.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
user := GetCurrentUser()
|
||||
taskName := args[0]
|
||||
Use: "task ([flags]) [task]",
|
||||
Short: "Task settings",
|
||||
Long: "Configure task settings.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
user := GetCurrentUser()
|
||||
taskName := args[0]
|
||||
|
||||
task, err := database.GetTask(user, taskName)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
task, err := database.GetTask(user, taskName)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
task.Name = taskName
|
||||
task.Name = taskName
|
||||
|
||||
if taskGitRepository != "-" {
|
||||
task.GitRepository = taskGitRepository
|
||||
}
|
||||
if taskGitRepository != "-" {
|
||||
task.GitRepository = taskGitRepository
|
||||
}
|
||||
|
||||
err = database.UpdateTask(user, taskName, task)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = database.UpdateTask(user, taskName, task)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("%s task updated\n", CharInfo)
|
||||
return
|
||||
},
|
||||
fmt.Printf("%s task updated\n", CharInfo)
|
||||
return
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(taskCmd)
|
||||
taskCmd.Flags().StringVarP(&taskGitRepository, "git", "g", "-", "Set the task's Git repository to enable commit message importing into activity notes.\nSet to an empty string '' to remove a previously set repository and disable git log imports.")
|
||||
rootCmd.AddCommand(taskCmd)
|
||||
taskCmd.Flags().StringVarP(&taskGitRepository, "git", "g", "-", "Set the task's Git repository to enable commit message importing into activity notes.\nSet to an empty string '' to remove a previously set repository and disable git log imports.")
|
||||
}
|
||||
|
||||
+61
-60
@@ -1,81 +1,82 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var trackCmd = &cobra.Command{
|
||||
Use: "track",
|
||||
Short: "Tracking time",
|
||||
Long: "Track new activity, which can either be kept running until 'finish' is being called or parameterized to be a finished activity.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
user := GetCurrentUser()
|
||||
Use: "track",
|
||||
Short: "Tracking time",
|
||||
Long: "Track new activity, which can either be kept running until 'finish' is being called or parameterized to be a finished activity.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
user := GetCurrentUser()
|
||||
|
||||
runningEntryId, err := database.GetRunningEntryId(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
runningEntryId, err := database.GetRunningEntryId(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if runningEntryId != "" {
|
||||
fmt.Printf("%s a task is already running\n", CharTrack)
|
||||
os.Exit(1)
|
||||
}
|
||||
if runningEntryId != "" {
|
||||
fmt.Printf("%s a task is already running\n", CharTrack)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if project == "" && viper.GetString("project.default") != "" {
|
||||
project = viper.GetString("project.default")
|
||||
}
|
||||
if project == "" && viper.GetString("project.default") != "" {
|
||||
project = viper.GetString("project.default")
|
||||
}
|
||||
|
||||
if project == "" && viper.GetBool("project.mandatory") {
|
||||
fmt.Println("project is mandatory but missing")
|
||||
os.Exit(1)
|
||||
}
|
||||
if project == "" && viper.GetBool("project.mandatory") {
|
||||
fmt.Println("project is mandatory but missing")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if task == "" && viper.GetBool("task.mandatory") {
|
||||
fmt.Println("task is mandatory but missing")
|
||||
os.Exit(1)
|
||||
}
|
||||
if task == "" && viper.GetBool("task.mandatory") {
|
||||
fmt.Println("task is mandatory but missing")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
newEntry, err := NewEntry("", begin, finish, project, task, user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
newEntry, err := NewEntry("", begin, finish, project, task, user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if notes != "" {
|
||||
newEntry.Notes = notes
|
||||
}
|
||||
if notes != "" {
|
||||
newEntry.Notes = notes
|
||||
}
|
||||
|
||||
isRunning := newEntry.Finish.IsZero()
|
||||
isRunning := newEntry.Finish.IsZero()
|
||||
|
||||
_, err = database.AddEntry(user, newEntry, isRunning)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
_, err = database.AddEntry(user, newEntry, isRunning)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf(newEntry.GetOutputForTrack(isRunning, false))
|
||||
return
|
||||
},
|
||||
fmt.Printf(newEntry.GetOutputForTrack(isRunning, false))
|
||||
return
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(trackCmd)
|
||||
trackCmd.Flags().StringVarP(&begin, "begin", "b", "", "Time the activity should begin at\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).")
|
||||
trackCmd.Flags().StringVarP(&finish, "finish", "s", "", "Time the activity should finish at\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).\nMust be after --begin time.")
|
||||
trackCmd.Flags().StringVarP(&project, "project", "p", "", "Project to be assigned")
|
||||
trackCmd.Flags().StringVarP(&task, "task", "t", "", "Task to be assigned")
|
||||
trackCmd.Flags().StringVarP(¬es, "notes", "n", "", "Activity notes")
|
||||
trackCmd.Flags().BoolVarP(&force, "force", "f", false, "Force begin tracking of a new task \neven though another one is still running \n(ONLY IF YOU KNOW WHAT YOU'RE DOING!)")
|
||||
rootCmd.AddCommand(trackCmd)
|
||||
trackCmd.Flags().StringVarP(&begin, "begin", "b", "", "Time the activity should begin at\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).")
|
||||
trackCmd.Flags().StringVarP(&finish, "finish", "s", "", "Time the activity should finish at\n\nEither in the formats 16:00 / 4:00PM \nor relative to the current time, \ne.g. -0:15 (now minus 15 minutes), +1.50 (now plus 1:30h).\nMust be after --begin time.")
|
||||
trackCmd.Flags().StringVarP(&project, "project", "p", "", "Project to be assigned")
|
||||
trackCmd.Flags().StringVarP(&task, "task", "t", "", "Task to be assigned")
|
||||
trackCmd.Flags().StringVarP(¬es, "notes", "n", "", "Activity notes")
|
||||
trackCmd.Flags().BoolVarP(&force, "force", "f", false, "Force begin tracking of a new task \neven though another one is still running \n(ONLY IF YOU KNOW WHAT YOU'RE DOING!)")
|
||||
|
||||
flagName := "task"
|
||||
trackCmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
user := GetCurrentUser()
|
||||
entries, _ := database.ListEntries(user)
|
||||
_, tasks := listProjectsAndTasks(entries)
|
||||
return tasks, cobra.ShellCompDirectiveDefault
|
||||
})
|
||||
flagName := "task"
|
||||
trackCmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
user := GetCurrentUser()
|
||||
entries, _ := database.ListEntries(user)
|
||||
_, tasks := listProjectsAndTasks(entries)
|
||||
return tasks, cobra.ShellCompDirectiveDefault
|
||||
})
|
||||
}
|
||||
|
||||
+27
-26
@@ -1,40 +1,41 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var trackingCmd = &cobra.Command{
|
||||
Use: "tracking",
|
||||
Short: "Currently tracking activity",
|
||||
Long: "Show currently tracking activity.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
user := GetCurrentUser()
|
||||
Use: "tracking",
|
||||
Short: "Currently tracking activity",
|
||||
Long: "Show currently tracking activity.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
user := GetCurrentUser()
|
||||
|
||||
runningEntryId, err := database.GetRunningEntryId(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
runningEntryId, err := database.GetRunningEntryId(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if runningEntryId == "" {
|
||||
fmt.Printf("%s not running\n", CharFinish)
|
||||
os.Exit(1)
|
||||
}
|
||||
if runningEntryId == "" {
|
||||
fmt.Printf("%s not running\n", CharFinish)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
runningEntry, err := database.GetEntry(user, runningEntryId)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
runningEntry, err := database.GetEntry(user, runningEntryId)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf(runningEntry.GetOutputForTrack(true, true))
|
||||
return
|
||||
},
|
||||
fmt.Printf(runningEntry.GetOutputForTrack(true, true))
|
||||
return
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(trackingCmd)
|
||||
rootCmd.AddCommand(trackingCmd)
|
||||
}
|
||||
|
||||
@@ -1,117 +1,123 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"github.com/gookit/color"
|
||||
"github.com/shopspring/decimal"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
func GetOutputBoxForNumber(number int, clr (func(...interface {}) string) ) (string) {
|
||||
switch(number) {
|
||||
case 0: return clr(" ")
|
||||
case 1: return clr(" ▄")
|
||||
case 2: return clr("▄▄")
|
||||
case 3: return clr("▄█")
|
||||
case 4: return clr("██")
|
||||
}
|
||||
func GetOutputBoxForNumber(number int, clr func(...interface{}) string) string {
|
||||
switch number {
|
||||
case 0:
|
||||
return clr(" ")
|
||||
case 1:
|
||||
return clr(" ▄")
|
||||
case 2:
|
||||
return clr("▄▄")
|
||||
case 3:
|
||||
return clr("▄█")
|
||||
case 4:
|
||||
return clr("██")
|
||||
}
|
||||
|
||||
return clr(" ")
|
||||
return clr(" ")
|
||||
}
|
||||
|
||||
func GetOutputBarForHours(hours decimal.Decimal, stats []Statistic) ([]string) {
|
||||
var bar = []string{
|
||||
color.FgGray.Render("····"),
|
||||
color.FgGray.Render("····"),
|
||||
color.FgGray.Render("····"),
|
||||
color.FgGray.Render("····"),
|
||||
color.FgGray.Render("····"),
|
||||
color.FgGray.Render("····"),
|
||||
}
|
||||
func GetOutputBarForHours(hours decimal.Decimal, stats []Statistic) []string {
|
||||
bar := []string{
|
||||
color.FgGray.Render("····"),
|
||||
color.FgGray.Render("····"),
|
||||
color.FgGray.Render("····"),
|
||||
color.FgGray.Render("····"),
|
||||
color.FgGray.Render("····"),
|
||||
color.FgGray.Render("····"),
|
||||
}
|
||||
|
||||
hoursInt := int((hours.Round(0)).IntPart())
|
||||
rest := ((hours.Round(0)).Mod(decimal.NewFromInt(4))).Round(0)
|
||||
restInt := int(rest.IntPart())
|
||||
hoursInt := int((hours.Round(0)).IntPart())
|
||||
rest := ((hours.Round(0)).Mod(decimal.NewFromInt(4))).Round(0)
|
||||
restInt := int(rest.IntPart())
|
||||
|
||||
divisible := hoursInt - restInt
|
||||
fullparts := divisible / 4
|
||||
divisible := hoursInt - restInt
|
||||
fullparts := divisible / 4
|
||||
|
||||
colorsFull := make(map[int](func(...interface {}) string))
|
||||
colorsFullIdx := 0
|
||||
colorsFull := make(map[int](func(...interface{}) string))
|
||||
colorsFullIdx := 0
|
||||
|
||||
colorFraction := color.FgWhite.Render
|
||||
colorFractionPrevAmount := 0.0
|
||||
colorFraction := color.FgWhite.Render
|
||||
colorFractionPrevAmount := 0.0
|
||||
|
||||
for _, stat := range stats {
|
||||
statHoursInt, _ := stat.Hours.Float64()
|
||||
statRest := (stat.Hours.Round(0)).Mod(decimal.NewFromInt(4))
|
||||
statRestFloat, _ := statRest.Float64()
|
||||
for _, stat := range stats {
|
||||
statHoursInt, _ := stat.Hours.Float64()
|
||||
statRest := (stat.Hours.Round(0)).Mod(decimal.NewFromInt(4))
|
||||
statRestFloat, _ := statRest.Float64()
|
||||
|
||||
if statRestFloat > colorFractionPrevAmount {
|
||||
colorFractionPrevAmount = statRestFloat
|
||||
colorFraction = stat.Color
|
||||
}
|
||||
if statRestFloat > colorFractionPrevAmount {
|
||||
colorFractionPrevAmount = statRestFloat
|
||||
colorFraction = stat.Color
|
||||
}
|
||||
|
||||
fullColoredParts := int(math.Round(statHoursInt) / 4)
|
||||
fullColoredParts := int(math.Round(statHoursInt) / 4)
|
||||
|
||||
if fullColoredParts == 0 && statHoursInt > colorFractionPrevAmount {
|
||||
colorFractionPrevAmount = statHoursInt
|
||||
colorFraction = stat.Color
|
||||
}
|
||||
if fullColoredParts == 0 && statHoursInt > colorFractionPrevAmount {
|
||||
colorFractionPrevAmount = statHoursInt
|
||||
colorFraction = stat.Color
|
||||
}
|
||||
|
||||
for i := 0; i < fullColoredParts; i++ {
|
||||
colorsFull[colorsFullIdx] = stat.Color
|
||||
colorsFullIdx++
|
||||
}
|
||||
}
|
||||
for i := 0; i < fullColoredParts; i++ {
|
||||
colorsFull[colorsFullIdx] = stat.Color
|
||||
colorsFullIdx++
|
||||
}
|
||||
}
|
||||
|
||||
iColor := 0
|
||||
for i := (len(bar) - 1); i > (len(bar) - 1 - fullparts); i-- {
|
||||
if iColor < colorsFullIdx {
|
||||
bar[i] = " " + GetOutputBoxForNumber(4, colorsFull[iColor]) + " "
|
||||
iColor++
|
||||
} else {
|
||||
bar[i] = " " + GetOutputBoxForNumber(4, colorFraction) + " "
|
||||
}
|
||||
}
|
||||
iColor := 0
|
||||
for i := (len(bar) - 1); i > (len(bar) - 1 - fullparts); i-- {
|
||||
if iColor < colorsFullIdx {
|
||||
bar[i] = " " + GetOutputBoxForNumber(4, colorsFull[iColor]) + " "
|
||||
iColor++
|
||||
} else {
|
||||
bar[i] = " " + GetOutputBoxForNumber(4, colorFraction) + " "
|
||||
}
|
||||
}
|
||||
|
||||
if(restInt > 0) {
|
||||
bar[(len(bar) - 1 - fullparts)] = " " + GetOutputBoxForNumber(restInt, colorFraction) + " "
|
||||
}
|
||||
if restInt > 0 {
|
||||
bar[(len(bar) - 1 - fullparts)] = " " + GetOutputBoxForNumber(restInt, colorFraction) + " "
|
||||
}
|
||||
|
||||
return bar
|
||||
return bar
|
||||
}
|
||||
|
||||
func OutputAppendRight(leftStr string, rightStr string, pad int) (string) {
|
||||
var output string = ""
|
||||
var rpos int = 0
|
||||
func OutputAppendRight(leftStr string, rightStr string, pad int) string {
|
||||
var output string = ""
|
||||
var rpos int = 0
|
||||
|
||||
left := []rune(leftStr)
|
||||
leftLen := len(left)
|
||||
right := []rune(rightStr)
|
||||
rightLen := len(right)
|
||||
left := []rune(leftStr)
|
||||
leftLen := len(left)
|
||||
right := []rune(rightStr)
|
||||
rightLen := len(right)
|
||||
|
||||
for lpos := 0; lpos < leftLen; lpos++ {
|
||||
if left[lpos] == '\n' || lpos == (leftLen - 1) {
|
||||
output = fmt.Sprintf("%s%*s", output, pad, "")
|
||||
for rpos = rpos; rpos < rightLen; rpos++ {
|
||||
output = fmt.Sprintf("%s%c", output, right[rpos])
|
||||
if right[rpos] == '\n' {
|
||||
rpos++
|
||||
break
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
output = fmt.Sprintf("%s%c", output, left[lpos])
|
||||
}
|
||||
for lpos := 0; lpos < leftLen; lpos++ {
|
||||
if left[lpos] == '\n' || lpos == (leftLen-1) {
|
||||
output = fmt.Sprintf("%s%*s", output, pad, "")
|
||||
for rpos = rpos; rpos < rightLen; rpos++ {
|
||||
output = fmt.Sprintf("%s%c", output, right[rpos])
|
||||
if right[rpos] == '\n' {
|
||||
rpos++
|
||||
break
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
output = fmt.Sprintf("%s%c", output, left[lpos])
|
||||
}
|
||||
|
||||
return output
|
||||
return output
|
||||
}
|
||||
|
||||
func GetColorFnFromHex(colorHex string) (func(...interface {}) string) {
|
||||
if colorHex == "" {
|
||||
colorHex = "#dddddd"
|
||||
}
|
||||
return color.NewRGBStyle(color.HEX(colorHex)).Sprint
|
||||
func GetColorFnFromHex(colorHex string) func(...interface{}) string {
|
||||
if colorHex == "" {
|
||||
colorHex = "#dddddd"
|
||||
}
|
||||
return color.NewRGBStyle(color.HEX(colorHex)).Sprint
|
||||
}
|
||||
|
||||
@@ -1,88 +1,89 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
// "fmt"
|
||||
"os"
|
||||
"github.com/shopspring/decimal"
|
||||
"time"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
// "fmt"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type TymeEntry struct {
|
||||
Billing string `json:"billing"` // "UNBILLED",
|
||||
Category string `json:"category"` // "Client",
|
||||
Distance string `json:"distance"` // "0",
|
||||
Duration string `json:"duration"` // "15",
|
||||
Start string `json:"start"` // "2020-09-01T08:45:00+01:00",
|
||||
End string `json:"end"` // "2020-09-01T08:57:00+01:00",
|
||||
Note string `json:"note"` // "",
|
||||
Project string `json:"project"` // "Project",
|
||||
Quantity string `json:"quantity"` // "0",
|
||||
Rate string `json:"rate"` // "140",
|
||||
RoundingMethod string `json:"rounding_method"` // "NEAREST",
|
||||
RoundingMinutes int `json:"rounding_minutes"` // 15,
|
||||
Subtask string `json:"subtask"` // "",
|
||||
Sum string `json:"sum"` // "35",
|
||||
Task string `json:"task"` // "Development",
|
||||
Type string `json:"type"` // "timed",
|
||||
User string `json:"user"` // ""
|
||||
Billing string `json:"billing"` // "UNBILLED",
|
||||
Category string `json:"category"` // "Client",
|
||||
Distance string `json:"distance"` // "0",
|
||||
Duration string `json:"duration"` // "15",
|
||||
Start string `json:"start"` // "2020-09-01T08:45:00+01:00",
|
||||
End string `json:"end"` // "2020-09-01T08:57:00+01:00",
|
||||
Note string `json:"note"` // "",
|
||||
Project string `json:"project"` // "Project",
|
||||
Quantity string `json:"quantity"` // "0",
|
||||
Rate string `json:"rate"` // "140",
|
||||
RoundingMethod string `json:"rounding_method"` // "NEAREST",
|
||||
RoundingMinutes int `json:"rounding_minutes"` // 15,
|
||||
Subtask string `json:"subtask"` // "",
|
||||
Sum string `json:"sum"` // "35",
|
||||
Task string `json:"task"` // "Development",
|
||||
Type string `json:"type"` // "timed",
|
||||
User string `json:"user"` // ""
|
||||
}
|
||||
|
||||
type Tyme struct {
|
||||
Data []TymeEntry `json:"data"`
|
||||
Data []TymeEntry `json:"data"`
|
||||
}
|
||||
|
||||
func (tyme *Tyme) Load(filename string) error {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
decoder := json.NewDecoder(file)
|
||||
decoder := json.NewDecoder(file)
|
||||
|
||||
if err = decoder.Decode(&tyme); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = decoder.Decode(&tyme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tyme *Tyme) FromEntries(entries []Entry) error {
|
||||
for _, entry := range entries {
|
||||
duration := decimal.NewFromFloat(entry.Finish.Sub(entry.Begin).Minutes())
|
||||
for _, entry := range entries {
|
||||
duration := decimal.NewFromFloat(entry.Finish.Sub(entry.Begin).Minutes())
|
||||
|
||||
tymeEntry := TymeEntry{
|
||||
Billing: "UNBILLED",
|
||||
Category: "",
|
||||
Distance: "0",
|
||||
Duration: duration.StringFixed(0),
|
||||
Start: entry.Begin.Format(time.RFC3339),
|
||||
End: entry.Finish.Format(time.RFC3339),
|
||||
Note: entry.Notes,
|
||||
Project: entry.Project,
|
||||
Quantity: "0",
|
||||
Rate: "0",
|
||||
RoundingMethod: "NEAREST",
|
||||
RoundingMinutes: 15,
|
||||
Subtask: "",
|
||||
Sum: "0",
|
||||
Task: entry.Task,
|
||||
Type: "timed",
|
||||
User: "",
|
||||
}
|
||||
tymeEntry := TymeEntry{
|
||||
Billing: "UNBILLED",
|
||||
Category: "",
|
||||
Distance: "0",
|
||||
Duration: duration.StringFixed(0),
|
||||
Start: entry.Begin.Format(time.RFC3339),
|
||||
End: entry.Finish.Format(time.RFC3339),
|
||||
Note: entry.Notes,
|
||||
Project: entry.Project,
|
||||
Quantity: "0",
|
||||
Rate: "0",
|
||||
RoundingMethod: "NEAREST",
|
||||
RoundingMinutes: 15,
|
||||
Subtask: "",
|
||||
Sum: "0",
|
||||
Task: entry.Task,
|
||||
Type: "timed",
|
||||
User: "",
|
||||
}
|
||||
|
||||
tyme.Data = append(tyme.Data, tymeEntry)
|
||||
}
|
||||
tyme.Data = append(tyme.Data, tymeEntry)
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tyme *Tyme) Stringify() string {
|
||||
stringified, err := json.Marshal(tyme)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
stringified, err := json.Marshal(tyme)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return string(stringified)
|
||||
return string(stringified)
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
var fractional bool
|
||||
|
||||
func fmtDuration(dur time.Duration) (string) {
|
||||
return fmtHours(decimal.NewFromFloat(dur.Hours()))
|
||||
func fmtDuration(dur time.Duration) string {
|
||||
return fmtHours(decimal.NewFromFloat(dur.Hours()))
|
||||
}
|
||||
|
||||
func fmtHours(hours decimal.Decimal) (string) {
|
||||
if fractional {
|
||||
return hours.StringFixed(2)
|
||||
} else {
|
||||
return fmt.Sprintf(
|
||||
"%s:%02s",
|
||||
hours.Floor(), // hours
|
||||
hours.Sub(hours.Floor()).
|
||||
Mul(decimal.NewFromFloat(.6)).
|
||||
Mul(decimal.NewFromInt(100)).
|
||||
Floor())
|
||||
}
|
||||
func fmtHours(hours decimal.Decimal) string {
|
||||
if fractional {
|
||||
return hours.StringFixed(2)
|
||||
} else {
|
||||
return fmt.Sprintf(
|
||||
"%s:%02s",
|
||||
hours.Floor(), // hours
|
||||
hours.Sub(hours.Floor()).
|
||||
Mul(decimal.NewFromFloat(.6)).
|
||||
Mul(decimal.NewFromInt(100)).
|
||||
Floor())
|
||||
}
|
||||
}
|
||||
|
||||
+9
-9
@@ -1,22 +1,22 @@
|
||||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var VERSION string
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
}
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Display what Zeit it is",
|
||||
Long: `The version of Zeit.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("zeit", VERSION)
|
||||
},
|
||||
Use: "version",
|
||||
Short: "Display what Zeit it is",
|
||||
Long: `The version of Zeit.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("zeit", VERSION)
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user