Finished first shot on stats implementation

This commit is contained in:
マリウス 2020-10-16 21:41:19 +01:00
parent 0b4839278c
commit 85fc684aa9
No known key found for this signature in database
GPG key ID: C228EF0A530AF06F
5 changed files with 119 additions and 148 deletions

1
go.mod
View file

@ -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
View file

@ -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=

View file

@ -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 bar
return cal, nil
}
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++ {

View file

@ -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])
}
return output
func GetISOCalendarWeek(date time.Time) (int) {
var _, cw = date.ISOWeek()
return cw
}
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));
}

View file

@ -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
},