summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--templates/report.t12
-rw-r--r--templates/wp-total.t27
-rw-r--r--zeitgeist.go125
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
}