zeit/z/calendar.go

170 lines
5.5 KiB
Go
Raw Normal View History

2020-10-16 15:36:44 +00:00
package z
import (
"fmt"
"time"
"github.com/shopspring/decimal"
"github.com/jinzhu/now"
"github.com/gookit/color"
2020-10-16 15:36:44 +00:00
)
2020-10-16 17:11:33 +00:00
type Statistic struct {
Hours decimal.Decimal
Project string
Color (func(...interface {}) string)
}
type WeekStatistics map[string][]Statistic
2020-10-16 15:36:44 +00:00
type Week struct {
Statistics WeekStatistics
2020-10-16 15:36:44 +00:00
}
type Month struct {
Name string
Weeks [5]Week
2020-10-16 15:36:44 +00:00
}
type Calendar struct {
Months [12]Month
Distribution map[string]Statistic
TotalHours decimal.Decimal
}
2020-10-16 15:36:44 +00:00
func NewCalendar(entries []Entry) (Calendar, error) {
cal := Calendar{}
2020-10-16 15:36:44 +00:00
cal.Distribution = make(map[string]Statistic)
for _, entry := range entries {
endOfBeginDay := now.With(entry.Begin).EndOfDay()
sameDayHours := decimal.NewFromInt(0)
nextDayHours := decimal.NewFromInt(0)
2020-10-16 15:36:44 +00:00
/*
* 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()
2020-10-16 17:11:33 +00:00
sameDayDuration := endOfBeginDay.Sub(entry.Begin)
sameDay := sameDayDuration.Hours()
sameDayHours = decimal.NewFromFloat(sameDay)
2020-10-16 17:11:33 +00:00
nextDayDuration := entry.Finish.Sub(startOfFinishDay)
nextDay := nextDayDuration.Hours()
nextDayHours = decimal.NewFromFloat(nextDay)
2020-10-16 17:11:33 +00:00
} else {
sameDayDuration := entry.Finish.Sub(entry.Begin)
sameDay := sameDayDuration.Hours()
sameDayHours = decimal.NewFromFloat(sameDay)
2020-10-16 17:11:33 +00:00
}
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]
2020-10-16 18:38:00 +00:00
stat := Statistic{
Hours: sameDayHours,
Project: entry.Project,
Color: color.FgCyan.Render,
}
2020-10-16 18:38:00 +00:00
if cal.Months[month0].Weeks[weeknumber0].Statistics == nil {
cal.Months[month0].Weeks[weeknumber0].Statistics = make(WeekStatistics)
}
2020-10-16 17:11:33 +00:00
cal.Months[month0].Weeks[weeknumber0].Statistics[weekdayName] = append(cal.Months[month0].Weeks[weeknumber0].Statistics[weekdayName], stat)
2020-10-16 17:11:33 +00:00
}
2020-10-16 15:36:44 +00:00
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]
2020-10-16 15:36:44 +00:00
stat := Statistic{
Hours: nextDayHours,
Project: entry.Project,
Color: color.FgCyan.Render, // TODO: Make configurable
}
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)
}
var dist = cal.Distribution[entry.Project]
dist.Project = entry.Project
dist.Hours = dist.Hours.Add(sameDayHours)
dist.Hours = dist.Hours.Add(nextDayHours)
dist.Color = color.FgCyan.Render // TODO: Make configurable
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)
}
2020-10-16 15:36:44 +00:00
return cal, nil
2020-10-16 15:36:44 +00:00
}
func (calendar *Calendar) GetOutputForWeekCalendar(date time.Time, month int, week int) (string) {
2020-10-16 15:36:44 +00:00
var output string = ""
var bars [][]string
var totalHours = decimal.NewFromInt(0)
var days = []string{"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}
for _, day := range days {
2020-10-16 17:11:33 +00:00
var dayHours = decimal.NewFromInt(0)
for _, stat := range calendar.Months[month].Weeks[week].Statistics[day] {
2020-10-16 17:11:33 +00:00
dayHours = dayHours.Add(stat.Hours)
totalHours = totalHours.Add(stat.Hours)
}
bar := GetOutputBarForHours(dayHours, calendar.Months[month].Weeks[week].Statistics[day])
2020-10-16 15:36:44 +00:00
bars = append(bars, bar)
}
output = fmt.Sprintf("CW %02d %s H\n", GetISOCalendarWeek(date), totalHours.StringFixed(2))
2020-10-16 15:36:44 +00:00
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)
}
2020-10-16 16:02:41 +00:00
output = fmt.Sprintf("%s └────────────────────────────\n %s %s %s %s %s %s %s\n",
2020-10-16 15:36:44 +00:00
output, days[0], days[1], days[2], days[3], days[4], days[5], days[6])
2020-10-16 16:02:41 +00:00
return output
2020-10-16 15:36:44 +00:00
}
func (calendar *Calendar) GetOutputForDistribution() (string) {
var output string = ""
output = fmt.Sprintf("DISTRIBUTION\n\n");
output = fmt.Sprintf("%s████████████████████████████████████████████████████████████████████████████████\n\n", output)
// fmt.Printf("%s\n", calendar.TotalHours.String())
for _, stat := range calendar.Distribution {
divided := stat.Hours.Div(calendar.TotalHours)
percentage := divided.Mul(decimal.NewFromInt(100))
hoursStr := stat.Hours.StringFixed(2)
percentageStr := percentage.StringFixed(2)
output = fmt.Sprintf("%s%s%*s H / %*s %%\n", output, stat.Project, (68 - len(stat.Project)), hoursStr, 5, percentageStr)
}
return output
}