summaryrefslogtreecommitdiff
path: root/zeitgeist.go
diff options
context:
space:
mode:
Diffstat (limited to 'zeitgeist.go')
-rw-r--r--zeitgeist.go82
1 files changed, 69 insertions, 13 deletions
diff --git a/zeitgeist.go b/zeitgeist.go
index 66ef520..2e99d91 100644
--- a/zeitgeist.go
+++ b/zeitgeist.go
@@ -99,7 +99,7 @@ func (en Entries) FilterWPTask(wp, task string) Entries {
return r
}
-func (en Entries) Totals() Amount {
+func (en Entries) Totals(ymt ...int) Amount {
var a Amount
for _, e := range en {
a += e.Amount
@@ -107,6 +107,20 @@ func (en Entries) Totals() Amount {
return a
}
+func (t Timeline) FilterDates(rang ...Date) Entries {
+ r := Entries{}
+ for d, e := range t { // TODO: sorted!?
+ if len(rang) > 0 && d.Before(rang[0]) {
+ continue
+ }
+ if len(rang) > 1 && d.After(rang[1]) {
+ continue
+ }
+ r = append(r, e...)
+ }
+ return r
+}
+
func (t Timeline) FilterWPTask(wp, task string) Entries {
r := Entries{}
for _, e := range t {
@@ -124,8 +138,9 @@ func (t Timeline) FilterWP(name string) Entries {
}
type User struct {
- Name string
- Type string
+ Name string
+ Type string
+ Blocked map[Date]string
}
type Workpackage struct {
@@ -185,6 +200,10 @@ func (a Date) After(b Date) bool {
return (time.Time(a)).After(time.Time(b))
}
+func (a Date) Before(b Date) bool {
+ return (time.Time(a)).Before(time.Time(b))
+}
+
type Amount float64
const (
@@ -620,15 +639,22 @@ func (p *Project) readTimelogs(dir string) error {
var embedFS embed.FS
var once sync.Once
var templates *template.Template
+var funcs = template.FuncMap{
+ "ParseDate": func(in string) (Date, error) {
+ d, e := time.Parse(time.DateOnly, in)
+ return Date(d), e
+ },
+}
func (p *Project) printReport(name string) {
var e error
once.Do(func() {
+ templates = template.New("templates/").Funcs(funcs)
if *fl_templates != "" {
- templates, e = template.ParseGlob(*fl_templates + "/*.md")
+ templates, e = templates.ParseGlob(*fl_templates + "/*.md")
} else {
- templates, e = template.ParseFS(embedFS, "templates/*.md")
+ templates, e = templates.ParseFS(embedFS, "templates/*.md")
}
if e != nil {
log.Fatalf("Couldn't parse templates: %v\n", e)
@@ -668,6 +694,24 @@ func (p *Project) IsBlocked(d Date) bool {
return ok
}
+func (p *Project) IsBlockedForUser(d Date, u string) bool {
+ user, ok := p.Users[u]
+ if !ok {
+ return false
+ }
+ _, ok = user.Blocked[d]
+ return ok
+}
+
+func (p *Project) IsBlockedForAnyUser(d Date) bool {
+ for _, user := range p.Users {
+ if _, ok := user.Blocked[d]; ok {
+ return true
+ }
+ }
+ return false
+}
+
func (f *FullDate) Difference() Amount {
return f.Planned.Total() - f.Worked.Total()
}
@@ -916,7 +960,17 @@ func (t *Totals) Open() Amount {
return d
}
-func (p *Project) TotalWPTask(wp, task string) (to *Totals, e error) {
+func (t *Totals) Percent() float64 {
+ if t.Done < 1 && t.Planned < 1 {
+ return 0
+ }
+ if t.Planned < 1 {
+ t.Planned = 1
+ }
+ return float64(t.Done/t.Planned) * 100
+}
+
+func (p *Project) TotalWPTask(wp, task string, rang ...Date) (to *Totals, e error) {
w, ok := p.Workpackages[wp]
if !ok {
return nil, fmt.Errorf("no such workpackage handle: %q", wp)
@@ -927,13 +981,13 @@ func (p *Project) TotalWPTask(wp, task string) (to *Totals, e error) {
}
to = &Totals{
Budgeted: t.Budget,
- Planned: p.Planned.FilterWPTask(wp, task).Total(),
- Done: p.Timeline.FilterWPTask(wp, task).Total(),
+ Planned: p.Planned.FilterDates(rang...).FilterWPTask(wp, task).Total(),
+ Done: p.Timeline.FilterDates(rang...).FilterWPTask(wp, task).Total(),
}
return to, nil
}
-func (p *Project) TotalWP(name string) (tot *WPTotals, e error) {
+func (p *Project) TotalWP(name string, rang ...Date) (tot *WPTotals, e error) {
wp, ok := p.Workpackages[name]
if !ok {
return nil, fmt.Errorf("no such workpackage handle: %q", name)
@@ -941,8 +995,8 @@ func (p *Project) TotalWP(name string) (tot *WPTotals, e error) {
tot = &WPTotals{
Totals: Totals{
Budgeted: wp.TotalBudget(),
- Planned: p.Planned.FilterWP(name).Total(),
- Done: p.Timeline.FilterWP(name).Total(),
+ Planned: p.Planned.FilterDates(rang...).FilterWP(name).Total(),
+ Done: p.Timeline.FilterDates(rang...).FilterWP(name).Total(),
},
Tasks: make(map[string]*Totals),
}
@@ -961,12 +1015,12 @@ type AllTotals struct {
WP map[string]*WPTotals
}
-func (p *Project) Totals() (at *AllTotals) {
+func (p *Project) Totals(rang ...Date) (at *AllTotals) {
at = &AllTotals{
WP: make(map[string]*WPTotals),
}
for n := range p.Workpackages {
- t, _ := p.TotalWP(n)
+ t, _ := p.TotalWP(n, rang...)
at.WP[n] = t
at.Totals.Budgeted += t.Budgeted
at.Totals.Planned += t.Planned
@@ -1024,11 +1078,13 @@ func (p *Project) ParallelWPTotals(tmpl ...string) ([]string, error) {
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 := range slices.Sorted(maps.Keys(totals.WP)) {
wpt := totals.WP[name]