diff options
-rw-r--r-- | templates/report.t | 13 | ||||
-rw-r--r-- | zeitgeist.go | 160 |
2 files changed, 169 insertions, 4 deletions
diff --git a/templates/report.t b/templates/report.t index ce1e74c..d82b8bf 100644 --- a/templates/report.t +++ b/templates/report.t @@ -4,7 +4,16 @@ Start: {{.Start}} End: {{.End}} Beneficiary: {{.Beneficiary}} -{{ range $id, $wp := .Workpackages }} + +# Calendar + +{{ range $y, $c := .Calendars }} +{{ $c }} +{{ end }} + + + +{{ range $id, $wp := .Workpackages -}} ## Workpackage {{ $id }}: {{ $wp.Title }} {{ range $tid, $task := $wp.Tasks }} {{$task.ID}}: {{ $task.Title }} @@ -21,7 +30,7 @@ Beneficiary: {{.Beneficiary}} {{- $tl := .Timeline }}{{- $hd := .Holidays }} # Days -{{ range .Days }} +{{ range .Days 2024 03 }} {{if or .IsWeekend .IsHoliday }}# {{end}}{{.Date}}{{ if .IsHoliday}} ({{ index $hd .Date}}){{end -}} {{ with .Planned }} Planned: {{ .Total }}{{ end }}{{ with .Worked }} Worked: {{ .Total }}{{end}} {{- end }} diff --git a/zeitgeist.go b/zeitgeist.go index c161fa0..e20ad35 100644 --- a/zeitgeist.go +++ b/zeitgeist.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "bytes" "embed" "encoding/json" "flag" @@ -37,6 +38,7 @@ type Project struct { Planning string Workpackages map[string]Workpackage Holidays map[Date]string + Blocked Timeline Timeline Timeline Planned Timeline } @@ -123,6 +125,7 @@ func (u Amount) String() string { r := "" if int(m) > 0 { r = fmt.Sprintf("%dm", int(m)) + d -= DaysInMonth * m } if int(d) > 0 { r += fmt.Sprintf("%dd", int(d)) @@ -540,9 +543,58 @@ func (f *FullDate) Difference() Amount { return f.Planned.Total() - f.Worked.Total() } -func (p *Project) Days() []FullDate { +func (p *Project) Days(ymd ...int) []FullDate { dates := []FullDate{} - for d := time.Time(p.Start); d.Before(time.Time(p.End)); d = d.AddDate(0, 0, 1) { + start := time.Time(p.Start) + end := time.Time(p.End) + + if len(ymd) > 0 { + year := ymd[0] + month := -1 + day := -1 + + if len(ymd) > 1 { + month = ymd[1] + } + + if len(ymd) > 2 { + day = ymd[2] + } + + if month < 0 && day < 0 { + s := time.Date(year, time.January, 1, 0, 0, 0, 0, time.UTC) + e := s.AddDate(1, 0, -1) + if s.Before(start) { + slog.Error("year out of range", "year", year) + return nil + } + start = s + if e.Before(end) { + end = e + } + } else if day < 0 { + s := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC) + e := s.AddDate(0, 1, -1) + if s.Before(start) { + slog.Error("year and month out of range", "year", year, "month", month, "start", start, "s", s) + return nil + } + start = s + if e.Before(end) { + end = e + } + } else { + s := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC) + if s.Before(start) { + slog.Error("year, month, day out of range", "year", year, "month", month, "day", day) + return nil + } + start = s + end = s + } + } + + for d := start; !d.Equal(end); d = d.AddDate(0, 0, 1) { d := Date(d) dates = append(dates, @@ -556,3 +608,107 @@ func (p *Project) Days() []FullDate { } return dates } + +func (p *Project) monthCal(year, month int) []string { + /* + 2 9 16 23 30 + 3 10 17 24 31 + 4 11 18 25 + 5 12 19 26 + 6 13 20 27 + 7 14 21 28 + 1 8 15 22 29 + */ + + const cw = 4 + const wd = 6 * cw + const sp = " " + + lines := [7]string{} + m := time.Month(month) + date := time.Date(year, m, 1, 0, 0, 0, 0, time.UTC) + skip := 0 + for i := 0; i < 42; i++ { + d := int((date.Weekday() + 6) % 7) + if i < d { + lines[i] += sp + skip += 1 + continue + } + if date.Month() != m { + lines[d] += sp + } else { + l := 2 + if i-skip > 6 { + l = 3 + } + str := fmt.Sprintf("%[1]*d", l, i+1-skip) + suf := " " + if _, ok := p.Holidays[Date(date)]; ok { + str = fmt.Sprintf("%[1]*s", l, "☺") + } else if _, ok := p.Blocked[Date(date)]; ok { + suf = "¬" + } else if _, ok := p.Timeline[Date(date)]; ok { + suf = "•" + } else if _, ok := p.Planned[Date(date)]; ok { + suf = "¤" //"¶" + } + lines[d] += str + suf + } + date = date.AddDate(0, 0, 1) + } + + return lines[:] +} + +func (p *Project) MonthCalendar(year, month int) string { + lines := []string{"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"} + c := p.monthCal(year, month) + h := fmt.Sprintf(" %-[1]*s %4d\n", len(bytes.Runes([]byte(c[0])))-9, time.Month(month), year) + for i, pre := range lines { + lines[i] = pre + " " + c[i] + } + return h + strings.Join(lines, "\n") +} + +func (p *Project) QuaterYearCalendar(year, quarter int) string { + return "not implemented" +} + +func (p *Project) YearCalendar(year int) string { + t := [12][]string{} + for i := range 12 { + t[i] = p.monthCal(year, i+1) + } + wd := len(bytes.Runes([]byte(t[0][0]))) + lines := []string{} + pre := []string{"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"} + + for q := range 4 { + header := "" + for m := range 3 { + header += fmt.Sprintf(" %-[1]*s", wd-2, time.Month(q*3+m+1)) + } + lines = append(lines, header) + + for w, line := range pre { + for m := range 3 { + line += " " + t[q*3+m][w] + } + lines = append(lines, line) + } + lines = append(lines, "") + } + + s := fmt.Sprintf("%[1]*d\n", 3*wd/2+2, year) + + return s + strings.Join(lines, "\n") + "\n\n" +} + +func (p *Project) Calendars() map[int]string { + r := make(map[int]string) + for y := p.Start.Year(); y <= p.End.Year(); y++ { + r[y] = p.YearCalendar(y) + } + return r +} |