Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .idea/dictionaries/jansorg.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions go-tom/dateTime/range.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ func (r DateRange) Intersection(start *time.Time, end *time.Time) time.Duration
return 0
}

func (r DateRange) Intersects(start *time.Time, end *time.Time) bool {
return r.Intersection(start, end) > 0
}

func (r DateRange) Years(loc *time.Location) []DateRange {
first := r.Start.In(loc).Year()
last := r.End.In(loc).Year()
Expand Down
31 changes: 31 additions & 0 deletions go-tom/model/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ type Frame struct {
Archived bool `json:"archived,omitempty"`
}

func (f *Frame) copy() *Frame {
return &Frame{
ID: f.ID,
ProjectId: f.ProjectId,
Start: f.Start,
End: f.End,
Updated: f.Updated,
Notes: f.Notes,
TagIDs: f.TagIDs,
Archived: f.Archived,
}
}

func (f *Frame) sortTagIDs() {
sort.Strings(f.TagIDs)
}
Expand Down Expand Up @@ -135,6 +148,24 @@ func (f *Frame) Intersection(activeEnd *time.Time, timeRange *dateTime.DateRange
return time.Duration(0)
}

// Contains returns if this frame's time range contains this ref
// an unstopped frame contains ref if the start time is equal to it
func (f *Frame) Contains(ref *time.Time) bool {
if ref == nil || ref.IsZero() {
return false
}

if f.Start != nil && f.End != nil {
debug := fmt.Sprintf("%s -> %s <- %s", f.Start.String(), ref.String(), f.End.String())
fmt.Println(debug)

return ref.Equal(*f.Start) ||
ref.Equal(*f.End) ||
ref.After(*f.Start) && ref.Before(*f.End)
}
return f.Start != nil && f.Start.Equal(*ref)
}

func (f *Frame) IsBefore(other *Frame) bool {
return f.Start != nil && other.Start != nil && f.Start.Before(*other.Start)
}
Expand Down
55 changes: 46 additions & 9 deletions go-tom/model/frame_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,24 +88,37 @@ func (f *FrameList) FilterByEndDate(maxEndDate time.Time, acceptUnstopped bool)
})
}

func (f *FrameList) FilterByDateRange(dateRange dateTime.DateRange, acceptUnstopped bool) {
func (f *FrameList) FilterByDateRange(dateRange dateTime.DateRange, acceptUnstopped bool, keepOverlapping bool) {
if keepOverlapping {
f.Filter(func(frame *Frame) bool {
return acceptUnstopped && frame.End == nil || dateRange.Intersects(frame.Start, frame.End)
})
return
}

f.FilterByStartDate(*dateRange.Start)
if dateRange.IsClosed() {
f.FilterByEndDate(*dateRange.End, acceptUnstopped)
}
}

func (f *FrameList) FilterByDate(start time.Time, end time.Time, acceptUnstopped bool) {
f.FilterByStartDate(start)
f.FilterByEndDate(end, acceptUnstopped)
func (f *FrameList) FilterByDate(start time.Time, end time.Time, acceptUnstopped bool, keepOverlapping bool) {
f.FilterByDatePtr(&start, &end, acceptUnstopped, keepOverlapping)
}

func (f *FrameList) FilterByDatePtr(start *time.Time, end *time.Time, includeActive bool) {
if start != nil && !start.IsZero() {
f.FilterByStartDate(*start)
func (f *FrameList) FilterByDatePtr(start *time.Time, end *time.Time, acceptUnstopped bool, keepOverlapping bool) {
if keepOverlapping {
f.Filter(func(frame *Frame) bool {
return acceptUnstopped && frame.End == nil ||
start != nil && frame.Contains(start) ||
end != nil && frame.Contains(end)
})
return
}
if end != nil && !end.IsZero() {
f.FilterByEndDate(*end, includeActive)

f.FilterByStartDate(*start)
if end != nil {
f.FilterByEndDate(*end, acceptUnstopped)
}
}

Expand All @@ -127,6 +140,30 @@ func (f *FrameList) ExcludeArchived() {
})
}

// CutEntriesTo cuts entries which span more than start->end
func (f *FrameList) CutEntriesTo(start *time.Time, end *time.Time) {
length := len(*f)
for i := 0; i < length; i++ {
frame := (*f)[i]

// must not modify the original frame, it may still be referenced by other lists
adaptStart := start != nil && frame.Start.Before(*start)
adaptEnd := end != nil && frame.End != nil && frame.End.After(*end)

if adaptStart || adaptEnd {
copied := frame.copy()
if adaptStart {
copied.Start = start
}
if adaptEnd {
copied.End = end
}
(*f)[i] = copied
}
}
f.Sort()
}

// Split splits all frames into one ore more parts
// The part a frame belongs to is coputed by the key function
// because the distribution of keys is not always in order a map has to be used here
Expand Down
30 changes: 30 additions & 0 deletions go-tom/model/frame_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package model

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestContains(t *testing.T) {
start := newDate(2019, time.January, 1, 10, 0)
end := newDate(2019, time.January, 1, 12, 0)
f := Frame{
Start: start,
End: end,
}

ref := start.Add(-10 * time.Minute)
assert.False(t, f.Contains(&ref))

assert.True(t, f.Contains(start))

ref = start.Add(10 * time.Minute)
assert.True(t, f.Contains(&ref))

assert.True(t, f.Contains(end))

ref = end.Add(10 * time.Minute)
assert.False(t, f.Contains(&ref))
}
29 changes: 15 additions & 14 deletions go-tom/report/bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,44 +313,45 @@ func (b *ResultBucket) SplitByDateRange(splitType SplitOperation) {
start := filterRange.Start
end := filterRange.End

var value dateTime.DateRange
var splitValue dateTime.DateRange
switch splitType {
case SplitByYear:
value = dateTime.NewYearRange(*start, b.ctx.Locale, start.Location())
splitValue = dateTime.NewYearRange(*start, b.ctx.Locale, start.Location())
case SplitByMonth:
value = dateTime.NewMonthRange(*start, b.ctx.Locale, start.Location())
splitValue = dateTime.NewMonthRange(*start, b.ctx.Locale, start.Location())
case SplitByWeek:
value = dateTime.NewWeekRange(*start, b.ctx.Locale, start.Location())
splitValue = dateTime.NewWeekRange(*start, b.ctx.Locale, start.Location())
case SplitByDay:
value = dateTime.NewDayRange(*start, b.ctx.Locale, start.Location())
splitValue = dateTime.NewDayRange(*start, b.ctx.Locale, start.Location())
}

for value.IsClosed() && value.Start.Before(*end) {
for splitValue.IsClosed() && splitValue.Start.Before(*end) {
matchingFrames := b.Frames.Copy()
matchingFrames.FilterByDateRange(value, false)
matchingFrames.FilterByDateRange(splitValue, false, true)
matchingFrames.CutEntriesTo(splitValue.Start, splitValue.End)

rangeCopy := value
rangeCopy := splitValue
if b.config.ShowEmpty || !matchingFrames.Empty() {
b.AddChild(&ResultBucket{
dateRange: value,
dateRange: splitValue,
Frames: matchingFrames,
Duration: dateTime.NewEmptyCopy(b.Duration),
SplitByType: splitType,
SplitBy: value,
SplitBy: splitValue,
DailyTracked: dateTime.NewTrackedDaily(&rangeCopy),
DailyUnTracked: dateTime.NewUntrackedDaily(&rangeCopy),
})
}

switch splitType {
case SplitByYear:
value = value.Shift(1, 0, 0)
splitValue = splitValue.Shift(1, 0, 0)
case SplitByMonth:
value = value.Shift(0, 1, 0)
splitValue = splitValue.Shift(0, 1, 0)
case SplitByWeek:
value = value.Shift(0, 0, 7)
splitValue = splitValue.Shift(0, 0, 7)
case SplitByDay:
value = value.Shift(0, 0, 1)
splitValue = splitValue.Shift(0, 0, 1)
}
}

Expand Down
14 changes: 10 additions & 4 deletions go-tom/report/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ func (b *BucketReport) Update() *ResultBucket {
b.source.ExcludeArchived()
}
if !b.config.DateFilterRange.Empty() {
b.source.FilterByDateRange(b.config.DateFilterRange, false)
b.source.FilterByDateRange(b.config.DateFilterRange, false, true)
b.source.CutEntriesTo(b.config.DateFilterRange.Start, b.config.DateFilterRange.End)
}

projectIDs := b.config.ProjectIDs
Expand Down Expand Up @@ -62,18 +63,23 @@ func (b *BucketReport) Update() *ResultBucket {
})
}

// setup the date filter range in the target timezone
config := b.config
var filterRange *dateTime.DateRange
if config.DateFilterRange.Empty() {
config.DateFilterRange = b.source.DateRange(b.ctx.Locale).In(config.Timezone)
} else if config.Timezone != nil {
config.DateFilterRange = config.DateFilterRange.In(config.Timezone)
} else {
if config.Timezone != nil {
config.DateFilterRange = config.DateFilterRange.In(config.Timezone)
}
filterRange = &config.DateFilterRange
}

b.result = &ResultBucket{
ctx: b.ctx,
config: config,
Frames: b.source,
Duration: dateTime.NewDurationSumAll(b.config.EntryRounding, nil, nil),
Duration: dateTime.NewDurationSumAll(b.config.EntryRounding, filterRange, nil),
DailyTracked: dateTime.NewTrackedDaily(nil),
DailyUnTracked: dateTime.NewUntrackedDaily(nil),
}
Expand Down
Loading