diff options
author | Özgür Kesim <oec@kesim.org> | 2024-11-28 18:27:59 +0100 |
---|---|---|
committer | Özgür Kesim <oec@kesim.org> | 2024-11-28 18:27:59 +0100 |
commit | 255c624b733e44e39392dd565da55d6f2350076f (patch) | |
tree | 5226ea6a193404ddf94baec14f1bb503b9419abc | |
parent | 69f0a3e8ebe620e840b7b49f8103ca41319ac031 (diff) |
parallel rendering of WP Totals
-rw-r--r-- | templates/report.t | 12 | ||||
-rw-r--r-- | templates/wp-total.t | 27 | ||||
-rw-r--r-- | zeitgeist.go | 125 |
3 files changed, 128 insertions, 36 deletions
diff --git a/templates/report.t b/templates/report.t index 9a807e5..a9b6a59 100644 --- a/templates/report.t +++ b/templates/report.t @@ -4,16 +4,22 @@ Start: {{.Start}} End: {{.End}} Beneficiary: {{.Beneficiary}} {{- $p := . }} +{{- $totals := .Totals }} # Planing -{{range $wp, $t := .Totals }} + +{{ range $l := .ParallelWPTotals }} +{{. -}} +{{ end }} + +{{range $wp, $t := $totals.WP }} Total for {{ $wp }}: - Budgeted: {{ printf "%8s" $t.Budgeted.AsDay }} - Planned: {{ printf "%8s" $t.Planned.AsDay }}{{with $t.Unaccounted }} !Unaccounted: {{printf "%8s" .AsDay}}{{end}} - Actual: {{ printf "%8s" $t.Done.AsDay }}{{with $t.Open }} !TODO: {{printf "%8s" .AsDay }}{{end}} - Per Task: - {{- range $n, $t := (index $p.Workpackages $wp).Tasks }} - - {{ $t.ID }} ({{ $n }}):{{ $tt := $p.TotalWPTask $wp $n }} + {{- range $n, $t := $t.Tasks }} + - {{ $n }}:{{ $tt := $p.TotalWPTask $wp $n }} - Budgeted: {{ printf "%8s" $tt.Budgeted.AsDay }} - Planned: {{ printf "%8s" $tt.Planned.AsDay }}{{with $tt.Unaccounted }} !Unaccounted: {{printf "%8s" .AsDay}}{{end}} - Actual: {{ printf "%8s" $tt.Done.AsDay }}{{with $tt.Open }} !TODO: {{printf "%8s" .AsDay}}{{end}} diff --git a/templates/wp-total.t b/templates/wp-total.t index a5fd93a..90adf9b 100644 --- a/templates/wp-total.t +++ b/templates/wp-total.t @@ -1,12 +1,17 @@ -{{ $p := .Project -}} Total for {{ .WP }}: - - Budgeted: {{ printf "%8s" .Total.Budgeted.AsDay }} - - Planned: {{ printf "%8s" .Total.Planned.AsDay }}{{with .Total.Unaccounted }} !Unaccounted: {{printf "%8s" .AsDay}}{{end}} - - Actual: {{ printf "%8s" .Total.Actual.AsDay }} - - Per Task: -{{- range $n, $t := .Tasks }} - - {{ $t.ID }} ({{ $n }}):{{ $tt := $p.TotalWPTask .WP $t }} - - Budgeted: {{ printf "%8s" $tt.Budgeted.AsDay }} - - Planned: {{ printf "%8s" $tt.Planned.AsDay }}{{with $tt.Unaccounted }} !Unaccounted: {{printf "%8s" .AsDay}}{{end}} - - Actual: {{ printf "%8s" $tt.Actual.AsDay }} -{{ end }} +------------------ +Budgeted: {{ printf "%8s" .Budgeted.AsDay }} + Planned: {{ printf "%8s" .Planned.AsDay }}{{with .Unaccounted }} +! Open !: {{ printf "%8s" .AsDay}}{{end}} + Actual: {{ printf "%8s" .Done.AsDay }}{{with .Open }} +! TODO !: {{ printf "%8s" .AsDay }}{{end}} + +Per Task: +{{ range $n, $t := .Tasks }} +»{{ $n }}«: +Budgeted: {{ printf "%8s" $t.Budgeted.AsDay }} + Planned: {{ printf "%8s" $t.Planned.AsDay }}{{with $t.Unaccounted }} +! Open !: {{ printf "%8s" .AsDay}}{{end}} + Actual: {{ printf "%8s" $t.Done.AsDay }}{{with $t.Open }} +! TODO !: {{ printf "%8s" .AsDay}}{{end}} +{{ end }}
\ No newline at end of file diff --git a/zeitgeist.go b/zeitgeist.go index 643f0e2..9dc6e67 100644 --- a/zeitgeist.go +++ b/zeitgeist.go @@ -15,6 +15,7 @@ import ( "slices" "strconv" "strings" + "sync" "text/template" "time" ) @@ -593,25 +594,28 @@ func (p *Project) readTimelogs(dir string) error { //go:embed templates/*.t var embedFS embed.FS +var once sync.Once +var templates *template.Template func (p *Project) printReport(name string) { - var tmp *template.Template var e error - if *fl_templates != "" { - tmp, e = template.ParseGlob(*fl_templates + "/*.t") - } else { - tmp, e = template.ParseFS(embedFS, "templates/*.t") - } - if e != nil { - log.Fatalf("Couldn't parse templates: %v\n", e) - } + once.Do(func() { + if *fl_templates != "" { + templates, e = template.ParseGlob(*fl_templates + "/*.t") + } else { + templates, e = template.ParseFS(embedFS, "templates/*.t") + } + if e != nil { + log.Fatalf("Couldn't parse templates: %v\n", e) + } + }) - if nil == tmp.Lookup(name) { + if nil == templates.Lookup(name) { log.Fatalf("Couldn't find template %q\n", name) } - e = tmp.ExecuteTemplate(os.Stdout, name, p) + e = templates.ExecuteTemplate(os.Stdout, name, p) if e != nil { log.Fatalf("Couldn't execute template %q: %v\n", name, e) } @@ -815,7 +819,7 @@ type Totals struct { } type WPTotals struct { - *Totals + Totals Tasks map[string]*Totals } @@ -854,23 +858,100 @@ func (p *Project) TotalWPTask(wp, task string) (to *Totals, e error) { return to, nil } -func (p *Project) TotalWP(name string) (t *Totals, e error) { +func (p *Project) TotalWP(name string) (tot *WPTotals, e error) { wp, ok := p.Workpackages[name] if !ok { return nil, fmt.Errorf("no such workpackage handle: %q", name) } - t = &Totals{ - Budgeted: wp.TotalBudget(), - Planned: p.Planned.FilterWP(name).Total(), - Done: p.Timeline.FilterWP(name).Total(), + tot = &WPTotals{ + Totals: Totals{ + Budgeted: wp.TotalBudget(), + Planned: p.Planned.FilterWP(name).Total(), + Done: p.Timeline.FilterWP(name).Total(), + }, + Tasks: make(map[string]*Totals), + } + for tn, t := range wp.Tasks { + tot.Tasks[tn] = &Totals{ + Budgeted: t.Budget, + Planned: p.Planned.FilterWPTask(name, tn).Total(), + Done: p.Timeline.FilterWPTask(name, tn).Total(), + } } - return t, nil + return tot, nil } -func (p *Project) Totals() (t map[string]*Totals) { - t = make(map[string]*Totals) +type AllTotals struct { + Totals + WP map[string]*WPTotals +} + +func (p *Project) Totals() (at *AllTotals) { + at = &AllTotals{ + // TODO: fill in totals here, too + WP: make(map[string]*WPTotals), + } for n := range p.Workpackages { - t[n], _ = p.TotalWP(n) + at.WP[n], _ = p.TotalWP(n) + } + return at +} + +func renderParallel(content ...[]string) []string { + maxh := 0 + maxw := make([]int, len(content)) + + for i, c := range content { + h := len(c) + if maxh < h { + maxh = h + } + for _, l := range c { + w := len(l) + if maxw[i] < w { + maxw[i] = w + } + } + } + output := []string{} + + for r := range maxh { + parts := []string{} + for c := range len(content) { + var snip string + if r < len(content[c]) { + snip = content[c][r] + } + parts = append(parts, fmt.Sprintf("%-[1]*s", maxw[c], snip)) + } + output = append(output, strings.Join(parts, " | ")) + } + + return output +} + +func (p *Project) ParallelWPTotals(tmpl ...string) ([]string, error) { + tm := "wp-total.t" + if len(tmpl) > 0 { + tm = tmpl[0] + } + if nil == templates.Lookup(tm) { + return nil, fmt.Errorf("template %q not found", tm) + } + + totals := p.Totals() + content := [][]string{} + for name, wpt := range totals.WP { + buf := &bytes.Buffer{} + data := struct { + WP string + *WPTotals + }{name, wpt} + e := templates.ExecuteTemplate(buf, tm, data) + if e != nil { + return nil, fmt.Errorf("while rendering totals for %s with template %s: %w", name, tm, e) + } + content = append(content, strings.Split(buf.String(), "\n")) } - return t + return renderParallel(content...), nil } |