Finished first shot on stats implementation
This commit is contained in:
parent
0b4839278c
commit
85fc684aa9
5 changed files with 119 additions and 148 deletions
1
go.mod
1
go.mod
|
@ -6,6 +6,7 @@ require (
|
|||
github.com/cnf/structhash v0.0.0-20201013183111-a92e111048cd
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/gookit/color v1.3.1
|
||||
github.com/jinzhu/now v1.1.1
|
||||
github.com/shopspring/decimal v1.2.0
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/tidwall/buntdb v1.1.2
|
||||
|
|
2
go.sum
2
go.sum
|
@ -45,6 +45,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
|
|||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
|
|
158
z/calendar.go
158
z/calendar.go
|
@ -2,10 +2,10 @@ package z
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
"github.com/gookit/color"
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/jinzhu/now"
|
||||
"github.com/gookit/color"
|
||||
)
|
||||
|
||||
type Statistic struct {
|
||||
|
@ -14,93 +14,95 @@ type Statistic struct {
|
|||
Color (func(...interface {}) string)
|
||||
}
|
||||
|
||||
type WeekStatistics map[string][]Statistic
|
||||
|
||||
type Week struct {
|
||||
Statistics WeekStatistics
|
||||
}
|
||||
|
||||
type Month struct {
|
||||
Name string
|
||||
Weeks [5]Week
|
||||
}
|
||||
|
||||
type Calendar struct {
|
||||
|
||||
Months [12]Month
|
||||
}
|
||||
|
||||
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 NewCalendar(entries []Entry) (Calendar, error) {
|
||||
cal := Calendar{}
|
||||
|
||||
return clr(" ")
|
||||
}
|
||||
for _, entry := range entries {
|
||||
endOfBeginDay := now.With(entry.Begin).EndOfDay()
|
||||
sameDayHours := decimal.NewFromInt(0)
|
||||
nextDayHours := decimal.NewFromInt(0)
|
||||
|
||||
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("····"),
|
||||
}
|
||||
/*
|
||||
* Apparently the activity end is on a new day.
|
||||
* This means we have to split the activity across two days.
|
||||
*/
|
||||
if endOfBeginDay.Before(entry.Finish) == true {
|
||||
startOfFinishDay := now.With(entry.Finish).BeginningOfDay()
|
||||
|
||||
hoursInt := int((hours.Round(0)).IntPart())
|
||||
rest := ((hours.Round(0)).Mod(decimal.NewFromInt(4))).Round(0)
|
||||
restInt := int(rest.IntPart())
|
||||
sameDayDuration := endOfBeginDay.Sub(entry.Begin)
|
||||
sameDay := sameDayDuration.Hours()
|
||||
sameDayHours = decimal.NewFromFloat(sameDay)
|
||||
|
||||
divisible := hoursInt - restInt
|
||||
fullparts := divisible / 4
|
||||
nextDayDuration := entry.Finish.Sub(startOfFinishDay)
|
||||
nextDay := nextDayDuration.Hours()
|
||||
nextDayHours = decimal.NewFromFloat(nextDay)
|
||||
|
||||
colorsFull := make(map[int](func(...interface {}) string))
|
||||
colorsFullIdx := 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()
|
||||
|
||||
if statRestFloat > colorFractionPrevAmount {
|
||||
colorFractionPrevAmount = statRestFloat
|
||||
colorFraction = stat.Color
|
||||
}
|
||||
|
||||
fmt.Printf("%f\n", statHoursInt)
|
||||
fullColoredParts := int(math.Round(statHoursInt) / 4)
|
||||
|
||||
if fullColoredParts == 0 && statHoursInt > colorFractionPrevAmount {
|
||||
colorFractionPrevAmount = statHoursInt
|
||||
colorFraction = stat.Color
|
||||
}
|
||||
|
||||
fmt.Printf("Full parts: %d\n", fullColoredParts)
|
||||
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) + " "
|
||||
sameDayDuration := entry.Finish.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]
|
||||
|
||||
stat := Statistic{
|
||||
Hours: sameDayHours,
|
||||
Project: entry.Project,
|
||||
Color: color.FgCyan.Render,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if nextDayHours.GreaterThan(decimal.NewFromInt(0)) {
|
||||
month, weeknumber := GetISOWeekInMonth(entry.Finish)
|
||||
month0 := month - 1
|
||||
weeknumber0 := weeknumber - 1
|
||||
weekday := entry.Begin.Weekday()
|
||||
weekdayName := weekday.String()[:2]
|
||||
|
||||
stat := Statistic{
|
||||
Hours: nextDayHours,
|
||||
Project: entry.Project,
|
||||
Color: color.FgCyan.Render,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
if(restInt > 0) {
|
||||
bar[(len(bar) - 1 - fullparts)] = " " + GetOutputBoxForNumber(restInt, colorFraction) + " "
|
||||
return cal, nil
|
||||
}
|
||||
|
||||
return bar
|
||||
}
|
||||
|
||||
func (calendar *Calendar) GetCalendarWeek(timestamp time.Time) (int) {
|
||||
var _, cw = timestamp.ISOWeek()
|
||||
return cw
|
||||
}
|
||||
|
||||
func (calendar *Calendar) GetOutputForWeekCalendar(cw int, data map[string][]Statistic) (string) {
|
||||
func (calendar *Calendar) GetOutputForWeekCalendar(date time.Time, month int, week int) (string) {
|
||||
var output string = ""
|
||||
var bars [][]string
|
||||
var totalHours = decimal.NewFromInt(0)
|
||||
|
@ -109,16 +111,16 @@ func (calendar *Calendar) GetOutputForWeekCalendar(cw int, data map[string][]Sta
|
|||
for _, day := range days {
|
||||
var dayHours = decimal.NewFromInt(0)
|
||||
|
||||
for _, stat := range data[day] {
|
||||
for _, stat := range calendar.Months[month].Weeks[week].Statistics[day] {
|
||||
dayHours = dayHours.Add(stat.Hours)
|
||||
totalHours = totalHours.Add(stat.Hours)
|
||||
}
|
||||
|
||||
bar := GetOutputBarForHours(dayHours, data[day])
|
||||
bar := GetOutputBarForHours(dayHours, calendar.Months[month].Weeks[week].Statistics[day])
|
||||
bars = append(bars, bar)
|
||||
}
|
||||
|
||||
output = fmt.Sprintf("CW %02d %s H\n", cw, totalHours.StringFixed(2))
|
||||
output = fmt.Sprintf("CW %02d %s H\n", GetISOCalendarWeek(date), totalHours.StringFixed(2))
|
||||
for row := 0; row < len(bars[0]); row++ {
|
||||
output = fmt.Sprintf("%s%2d │", output, ((6 - row) * 4))
|
||||
for col := 0; col < len(bars); col++ {
|
||||
|
|
39
z/helpers.go
39
z/helpers.go
|
@ -1,11 +1,11 @@
|
|||
package z
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/user"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
"math"
|
||||
"errors"
|
||||
)
|
||||
|
||||
|
@ -53,6 +53,7 @@ func GetTimeFormat(timeStr string) (int) {
|
|||
return -1
|
||||
}
|
||||
|
||||
// TODO: Use https://golang.org/pkg/time/#ParseDuration
|
||||
func RelToTime(timeStr string, ftId int) (time.Time, error) {
|
||||
var re = regexp.MustCompile(TimeFormats()[ftId])
|
||||
gm := re.FindStringSubmatch(timeStr)
|
||||
|
@ -100,29 +101,19 @@ func ParseTime(timeStr string) (time.Time, error) {
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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])
|
||||
func GetISOCalendarWeek(date time.Time) (int) {
|
||||
var _, cw = date.ISOWeek()
|
||||
return cw
|
||||
}
|
||||
|
||||
return output
|
||||
func GetISOWeekInMonth(date time.Time) (month int, weeknumber int) {
|
||||
if date.IsZero() {
|
||||
return -1, -1
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
|
69
z/stats.go
69
z/stats.go
|
@ -3,9 +3,10 @@ package z
|
|||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"time"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/gookit/color"
|
||||
// "github.com/shopspring/decimal"
|
||||
// "github.com/gookit/color"
|
||||
)
|
||||
|
||||
var statsCmd = &cobra.Command{
|
||||
|
@ -13,55 +14,29 @@ var statsCmd = &cobra.Command{
|
|||
Short: "Display activity statistics",
|
||||
Long: "Display statistics on all tracked activities.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// user := GetCurrentUser()
|
||||
user := GetCurrentUser()
|
||||
|
||||
// entries, err := database.ListEntries(user)
|
||||
// if err != nil {
|
||||
// fmt.Printf("%s %+v\n", CharError, err)
|
||||
// os.Exit(1)
|
||||
// }
|
||||
|
||||
// for _, entry := range entries {
|
||||
// fmt.Printf("%s\n", entry.GetOutput())
|
||||
// }
|
||||
|
||||
var cal Calendar
|
||||
|
||||
var data = make(map[string][]Statistic)
|
||||
|
||||
data["Mo"] = []Statistic {
|
||||
Statistic{ Hours: decimal.NewFromFloat(12.0), Project: "zeit", Color: color.FgRed.Render },
|
||||
Statistic{ Hours: decimal.NewFromFloat(3.5), Project: "blog", Color: color.FgGreen.Render },
|
||||
}
|
||||
data["Tu"] = []Statistic {
|
||||
Statistic{ Hours: decimal.NewFromFloat(2.25), Project: "zeit", Color: color.FgRed.Render },
|
||||
Statistic{ Hours: decimal.NewFromFloat(4.0), Project: "blog", Color: color.FgGreen.Render },
|
||||
}
|
||||
data["We"] = []Statistic {
|
||||
Statistic{ Hours: decimal.NewFromFloat(10.0), Project: "zeit", Color: color.FgRed.Render },
|
||||
Statistic{ Hours: decimal.NewFromFloat(1.5), Project: "blog", Color: color.FgGreen.Render },
|
||||
}
|
||||
data["Th"] = []Statistic {
|
||||
Statistic{ Hours: decimal.NewFromFloat(4.0), Project: "zeit", Color: color.FgRed.Render },
|
||||
Statistic{ Hours: decimal.NewFromFloat(4.5), Project: "blog", Color: color.FgGreen.Render },
|
||||
}
|
||||
data["Fr"] = []Statistic {
|
||||
Statistic{ Hours: decimal.NewFromFloat(0.5), Project: "zeit", Color: color.FgRed.Render },
|
||||
Statistic{ Hours: decimal.NewFromFloat(3.5), Project: "blog", Color: color.FgGreen.Render },
|
||||
}
|
||||
data["Sa"] = []Statistic {
|
||||
Statistic{ Hours: decimal.NewFromFloat(1.0), Project: "zeit", Color: color.FgRed.Render },
|
||||
Statistic{ Hours: decimal.NewFromFloat(1.0), Project: "blog", Color: color.FgGreen.Render },
|
||||
}
|
||||
data["Su"] = []Statistic {
|
||||
Statistic{ Hours: decimal.NewFromFloat(10.0), Project: "zeit", Color: color.FgRed.Render },
|
||||
Statistic{ Hours: decimal.NewFromFloat(0.5), Project: "blog", Color: color.FgGreen.Render },
|
||||
entries, err := database.ListEntries(user)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %+v\n", CharError, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
out := cal.GetOutputForWeekCalendar(1, data)
|
||||
out2 := cal.GetOutputForWeekCalendar(2, data)
|
||||
cal, _ := NewCalendar(entries)
|
||||
|
||||
fmt.Printf("%s\n", OutputAppendRight(out, out2, 10))
|
||||
today := time.Now()
|
||||
month, weeknumber := GetISOWeekInMonth(today)
|
||||
month0 := month - 1
|
||||
weeknumber0 := weeknumber - 1
|
||||
thisWeek := cal.GetOutputForWeekCalendar(today, month0, weeknumber0)
|
||||
|
||||
oneWeekAgo := today.AddDate(0, 0, -7)
|
||||
month, weeknumber = GetISOWeekInMonth(oneWeekAgo)
|
||||
month0 = month - 1
|
||||
weeknumber0 = weeknumber - 1
|
||||
previousWeek := cal.GetOutputForWeekCalendar(oneWeekAgo, month0, weeknumber0)
|
||||
|
||||
fmt.Printf("%s\n", OutputAppendRight(thisWeek, previousWeek, 16))
|
||||
|
||||
return
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue