mirror of
https://github.com/muety/wakapi.git
synced 2025-12-05 22:20:24 -08:00
fix: summary from date display (resolve #843)
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/becheran/wildmatch-go"
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
"github.com/duke-git/lancet/v2/datetime"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
"github.com/leandro-lugaresi/hub"
|
||||
@@ -62,7 +63,7 @@ func (srv *SummaryService) Aliased(from, to time.Time, user *models.User, f type
|
||||
cacheKey := srv.getHash(from.String(), to.String(), user.ID, filters.Hash(), strconv.Itoa(int(requestedTimeout)), "--aliased")
|
||||
if to.Truncate(time.Second).Equal(to) && from.Truncate(time.Second).Equal(from) {
|
||||
if cacheResult, ok := srv.cache.Get(cacheKey); ok && !skipCache {
|
||||
return cacheResult.(*models.Summary), nil
|
||||
return cacheResult.(*models.Summary).Sorted().InTZ(user.TZ()), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +118,7 @@ func (srv *SummaryService) Retrieve(from, to time.Time, user *models.User, filte
|
||||
// Get all already existing, pre-generated summaries that fall into the requested interval
|
||||
result, err := srv.repository.GetByUserWithin(user, from, to)
|
||||
if err == nil {
|
||||
summaries = result
|
||||
summaries = srv.fixZeroDuration(result)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
@@ -146,6 +147,9 @@ func (srv *SummaryService) Retrieve(from, to time.Time, user *models.User, filte
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// prevent 0001-01-01T00:00:00 caused by empty "pre" missing interval, see https://github.com/muety/wakapi/issues/843
|
||||
summary.FromTime = models.CustomTime(condition.Ternary(summary.FromTime.T().Before(from), from, summary.FromTime.T()))
|
||||
|
||||
if filters != nil && filters.CountDistinctTypes() == 1 && filters.SelectFilteredOnly {
|
||||
filter := filters.OneOrEmpty()
|
||||
summary.KeepOnly(map[uint8]bool{filter.Entity: true}).ApplyFilter(filter)
|
||||
@@ -470,6 +474,27 @@ func (srv *SummaryService) getMissingIntervals(from, to time.Time, summaries []*
|
||||
return intervals
|
||||
}
|
||||
|
||||
// Since summary timestamps are only second-level precision, we rarely observe examples where from- and to-time are allegedly equal.
|
||||
// We artificially modify those to give them a one second duration and potentially fix the subsequent summary as well to prevent overlaps.
|
||||
// Assumes summaries slice to be sorted by from time.
|
||||
func (s *SummaryService) fixZeroDuration(summaries []*models.Summary) []*models.Summary {
|
||||
for i, summary := range summaries {
|
||||
if summary.FromTime.T().Equal(summary.ToTime.T()) {
|
||||
summary.ToTime = models.CustomTime(summary.ToTime.T().Add(1 * time.Second))
|
||||
|
||||
if i < len(summaries)-1 {
|
||||
summaryNext := summaries[i+1]
|
||||
if summaryNext.FromTime.T().Before(summary.ToTime.T()) {
|
||||
// intentionally not trying to resolve larger overlaps that were there before (even though they shouldn't happen in theory)
|
||||
summaryNext.FromTime = models.CustomTime(summaryNext.FromTime.T().Add(1 * time.Second))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return summaries
|
||||
}
|
||||
|
||||
func (srv *SummaryService) getHash(args ...string) string {
|
||||
return strings.Join(args, "__")
|
||||
}
|
||||
|
||||
@@ -381,6 +381,118 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Retrieve_DuplicateSumma
|
||||
suite.DurationService.AssertNumberOfCalls(suite.T(), "Get", 2)
|
||||
}
|
||||
|
||||
func (suite *SummaryServiceTestSuite) TestSummaryService_Retrieve_ZeroLengthSummaries() {
|
||||
sut := NewSummaryService(suite.SummaryRepository, suite.HeartbeatService, suite.DurationService, suite.AliasService, suite.ProjectLabelService)
|
||||
|
||||
suite.ProjectLabelService.On("GetByUser", suite.TestUser.ID).Return([]*models.ProjectLabel{}, nil)
|
||||
|
||||
var (
|
||||
summaries []*models.Summary
|
||||
from time.Time
|
||||
to time.Time
|
||||
result *models.Summary
|
||||
err error
|
||||
)
|
||||
|
||||
from, to = suite.TestStartTime.Add(-12*time.Hour), suite.TestStartTime.Add(12*time.Hour)
|
||||
summaries = []*models.Summary{
|
||||
{
|
||||
ID: uint(rand.Uint32()),
|
||||
UserID: TestUserId,
|
||||
FromTime: models.CustomTime(from.Add(10 * time.Minute)),
|
||||
ToTime: models.CustomTime(from.Add(10 * time.Minute)),
|
||||
Projects: []*models.SummaryItem{},
|
||||
Languages: []*models.SummaryItem{},
|
||||
Editors: []*models.SummaryItem{},
|
||||
OperatingSystems: []*models.SummaryItem{},
|
||||
Machines: []*models.SummaryItem{},
|
||||
},
|
||||
}
|
||||
|
||||
suite.SummaryRepository.On("GetByUserWithin", suite.TestUser, from, to).Return(summaries, nil)
|
||||
suite.DurationService.On("Get", from, summaries[0].FromTime.T(), suite.TestUser, mock.Anything, mock.Anything, false).Return(models.Durations{}, nil)
|
||||
suite.DurationService.On("Get", summaries[0].ToTime.T().Add(1*time.Second), to, suite.TestUser, mock.Anything, mock.Anything, false).Return(models.Durations{}, nil)
|
||||
|
||||
result, err = sut.Retrieve(from, to, suite.TestUser, nil, nil)
|
||||
|
||||
assert.Nil(suite.T(), err)
|
||||
assert.NotNil(suite.T(), result)
|
||||
suite.DurationService.AssertNumberOfCalls(suite.T(), "Get", 2)
|
||||
}
|
||||
|
||||
func (suite *SummaryServiceTestSuite) TestSummaryService_Retrieve_DateRange() {
|
||||
sut := NewSummaryService(suite.SummaryRepository, suite.HeartbeatService, suite.DurationService, suite.AliasService, suite.ProjectLabelService)
|
||||
|
||||
suite.ProjectLabelService.On("GetByUser", suite.TestUser.ID).Return([]*models.ProjectLabel{}, nil)
|
||||
|
||||
var (
|
||||
summaries []*models.Summary
|
||||
from time.Time
|
||||
to time.Time
|
||||
result *models.Summary
|
||||
err error
|
||||
)
|
||||
|
||||
from, to = suite.TestStartTime.Add(-12*time.Hour), suite.TestStartTime.Add(12*time.Hour)
|
||||
summaries = []*models.Summary{
|
||||
{
|
||||
ID: uint(rand.Uint32()),
|
||||
UserID: TestUserId,
|
||||
FromTime: models.CustomTime(from.Add(10 * time.Minute)),
|
||||
ToTime: models.CustomTime(to.Add(-10 * time.Minute)),
|
||||
Projects: []*models.SummaryItem{
|
||||
{
|
||||
Type: models.SummaryProject,
|
||||
Key: TestProject1,
|
||||
Total: 45 * time.Minute / time.Second, // hack
|
||||
},
|
||||
},
|
||||
Languages: []*models.SummaryItem{},
|
||||
Editors: []*models.SummaryItem{},
|
||||
OperatingSystems: []*models.SummaryItem{},
|
||||
Machines: []*models.SummaryItem{},
|
||||
},
|
||||
}
|
||||
|
||||
suite.SummaryRepository.On("GetByUserWithin", suite.TestUser, from, to).Return(summaries, nil)
|
||||
suite.DurationService.On("Get", from, summaries[0].FromTime.T(), suite.TestUser, mock.Anything, mock.Anything, false).Return(models.Durations{}, nil)
|
||||
suite.DurationService.On("Get", summaries[0].ToTime.T(), to, suite.TestUser, mock.Anything, mock.Anything, false).Return(models.Durations{}, nil)
|
||||
|
||||
result, err = sut.Retrieve(from, to, suite.TestUser, nil, nil)
|
||||
|
||||
assert.Nil(suite.T(), err)
|
||||
assert.NotNil(suite.T(), result)
|
||||
assert.Equal(suite.T(), from, result.FromTime.T()) // requested from date
|
||||
assert.Equal(suite.T(), to, result.ToTime.T()) // requested to date
|
||||
suite.DurationService.AssertNumberOfCalls(suite.T(), "Get", 2)
|
||||
}
|
||||
|
||||
func (suite *SummaryServiceTestSuite) TestSummaryService_Retrieve_DateRange_NoData() {
|
||||
sut := NewSummaryService(suite.SummaryRepository, suite.HeartbeatService, suite.DurationService, suite.AliasService, suite.ProjectLabelService)
|
||||
|
||||
suite.ProjectLabelService.On("GetByUser", suite.TestUser.ID).Return([]*models.ProjectLabel{}, nil)
|
||||
|
||||
var (
|
||||
from time.Time
|
||||
to time.Time
|
||||
result *models.Summary
|
||||
err error
|
||||
)
|
||||
|
||||
from, to = suite.TestStartTime.Add(-12*time.Hour), suite.TestStartTime.Add(12*time.Hour)
|
||||
|
||||
suite.SummaryRepository.On("GetByUserWithin", suite.TestUser, from, to).Return([]*models.Summary{}, nil)
|
||||
suite.DurationService.On("Get", from, to, suite.TestUser, mock.Anything, mock.Anything, false).Return(models.Durations{}, nil)
|
||||
|
||||
result, err = sut.Retrieve(from, to, suite.TestUser, nil, nil)
|
||||
|
||||
assert.Nil(suite.T(), err)
|
||||
assert.NotNil(suite.T(), result)
|
||||
assert.Equal(suite.T(), from, result.FromTime.T())
|
||||
assert.Equal(suite.T(), to, result.ToTime.T())
|
||||
suite.DurationService.AssertNumberOfCalls(suite.T(), "Get", 1)
|
||||
}
|
||||
|
||||
func (suite *SummaryServiceTestSuite) TestSummaryService_Aliased() {
|
||||
sut := NewSummaryService(suite.SummaryRepository, suite.HeartbeatService, suite.DurationService, suite.AliasService, suite.ProjectLabelService)
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ tests {
|
||||
test("Correct time zone", function () {
|
||||
const targetDateTz = moment(`2021-05-28T00:00:00${bru.getCollectionVar('TZ_OFFSET')}`)
|
||||
expect(moment(res.body.from).isSame(targetDateTz)).to.eql(true)
|
||||
expect(moment(res.body.to).isSame(targetDateTz)).to.eql(true)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ tests {
|
||||
// when it was midnight in UTC+3, it was still 11 pm in Germany
|
||||
const targetDateTz = moment(`2021-05-28T00:00:00${bru.getCollectionVar('TZ_OFFSET')}`).add(-1, 'h')
|
||||
expect(moment(res.body.from).isSame(targetDateTz)).to.eql(true)
|
||||
expect(moment(res.body.to).isSame(targetDateTz)).to.eql(true)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user