mirror of
https://github.com/muety/wakapi.git
synced 2023-08-10 21:12:56 +03:00
refactor: replace most custom date util functions by lancet ones
refactor: add precision mode to missing intervals function
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/datetime"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -13,7 +14,6 @@ import (
|
||||
"github.com/muety/wakapi/config"
|
||||
"github.com/muety/wakapi/models"
|
||||
wakatime "github.com/muety/wakapi/models/compat/wakatime/v1"
|
||||
"github.com/muety/wakapi/utils"
|
||||
"go.uber.org/atomic"
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
@@ -295,8 +295,8 @@ func mapHeartbeat(
|
||||
func generateDays(from, to time.Time) []time.Time {
|
||||
days := make([]time.Time, 0)
|
||||
|
||||
from = utils.StartOfDay(from)
|
||||
to = utils.StartOfDay(to.AddDate(0, 0, 1))
|
||||
from = datetime.BeginOfDay(from)
|
||||
to = datetime.BeginOfDay(to.AddDate(0, 0, 1))
|
||||
|
||||
for d := from; d.Before(to); d = d.AddDate(0, 0, 1) {
|
||||
days = append(days, d)
|
||||
|
@@ -3,6 +3,7 @@ package services
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/datetime"
|
||||
"github.com/emvi/logbuch"
|
||||
"github.com/leandro-lugaresi/hub"
|
||||
"github.com/muety/wakapi/config"
|
||||
@@ -113,7 +114,7 @@ func (srv *SummaryService) Retrieve(from, to time.Time, user *models.User, filte
|
||||
}
|
||||
|
||||
// Generate missing slots (especially before and after existing summaries) from durations (formerly raw heartbeats)
|
||||
missingIntervals := srv.getMissingIntervals(from, to, summaries)
|
||||
missingIntervals := srv.getMissingIntervals(from, to, summaries, false)
|
||||
for _, interval := range missingIntervals {
|
||||
if s, err := srv.Summarize(interval.Start, interval.End, user, filters); err == nil {
|
||||
summaries = append(summaries, s)
|
||||
@@ -368,7 +369,7 @@ func (srv *SummaryService) mergeSummaryItems(existing []*models.SummaryItem, new
|
||||
return itemList
|
||||
}
|
||||
|
||||
func (srv *SummaryService) getMissingIntervals(from, to time.Time, summaries []*models.Summary) []*models.Interval {
|
||||
func (srv *SummaryService) getMissingIntervals(from, to time.Time, summaries []*models.Summary, precise bool) []*models.Interval {
|
||||
if len(summaries) == 0 {
|
||||
return []*models.Interval{{from, to}}
|
||||
}
|
||||
@@ -377,37 +378,43 @@ func (srv *SummaryService) getMissingIntervals(from, to time.Time, summaries []*
|
||||
|
||||
// Pre
|
||||
if from.Before(summaries[0].FromTime.T()) {
|
||||
intervals = append(intervals, &models.Interval{from, summaries[0].FromTime.T()})
|
||||
intervals = append(intervals, &models.Interval{Start: from, End: summaries[0].FromTime.T()})
|
||||
}
|
||||
|
||||
// Between
|
||||
for i := 0; i < len(summaries)-1; i++ {
|
||||
t1, t2 := summaries[i].ToTime.T(), summaries[i+1].FromTime.T()
|
||||
if t1.Equal(t2) {
|
||||
if t1.Equal(t2) || t1.Equal(to) || t1.After(to) {
|
||||
continue
|
||||
}
|
||||
|
||||
td1 := t1
|
||||
td2 := t2
|
||||
|
||||
// round to end of day / start of day, assuming that summaries are always generated on a per-day basis
|
||||
// we assume that, if summary for any time range within a day is present, no further heartbeats exist on that day before 'from' and after 'to' time of that summary
|
||||
// this requires that a summary exists for every single day in a year and none is skipped, which shouldn't ever happen
|
||||
td1 := time.Date(t1.Year(), t1.Month(), t1.Day()+1, 0, 0, 0, 0, t1.Location())
|
||||
td2 := time.Date(t2.Year(), t2.Month(), t2.Day(), 0, 0, 0, 0, t2.Location())
|
||||
// non-precise mode is mainly for speed when fetching summaries over large intervals and trades speed for summary accuracy / comprehensiveness
|
||||
if !precise {
|
||||
td1 = datetime.BeginOfDay(t1).AddDate(0, 0, 1)
|
||||
td2 = datetime.BeginOfDay(t2)
|
||||
|
||||
// we always want to jump to beginning of next day
|
||||
// however, if left summary ends already at midnight, we would instead jump to beginning of second-next day -> go back again
|
||||
if td1.Sub(t1) == 24*time.Hour {
|
||||
td1 = td1.Add(-1 * time.Hour)
|
||||
// we always want to jump to beginning of next day
|
||||
// however, if left summary ends already at midnight, we would instead jump to beginning of second-next day -> go back again
|
||||
if td1.Sub(t1) == 24*time.Hour {
|
||||
td1 = td1.Add(-1 * time.Hour)
|
||||
}
|
||||
}
|
||||
|
||||
// one or more day missing in between?
|
||||
if td1.Before(td2) {
|
||||
intervals = append(intervals, &models.Interval{summaries[i].ToTime.T(), summaries[i+1].FromTime.T()})
|
||||
intervals = append(intervals, &models.Interval{Start: summaries[i].ToTime.T(), End: summaries[i+1].FromTime.T()})
|
||||
}
|
||||
}
|
||||
|
||||
// Post
|
||||
if to.After(summaries[len(summaries)-1].ToTime.T()) {
|
||||
intervals = append(intervals, &models.Interval{summaries[len(summaries)-1].ToTime.T(), to})
|
||||
intervals = append(intervals, &models.Interval{Start: summaries[len(summaries)-1].ToTime.T(), End: to})
|
||||
}
|
||||
|
||||
return intervals
|
||||
|
@@ -485,6 +485,45 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Filters() {
|
||||
assert.Contains(suite.T(), effectiveFilters.Label, TestProjectLabel3)
|
||||
}
|
||||
|
||||
func (suite *SummaryServiceTestSuite) TestSummaryService_getMissingIntervals() {
|
||||
sut := NewSummaryService(suite.SummaryRepository, suite.DurationService, suite.AliasService, suite.ProjectLabelService)
|
||||
|
||||
from1, _ := time.Parse(time.RFC822, "25 Mar 22 11:00 UTC")
|
||||
to1, _ := time.Parse(time.RFC822, "25 Mar 22 13:00 UTC")
|
||||
from2, _ := time.Parse(time.RFC822, "25 Mar 22 15:00 UTC")
|
||||
to2, _ := time.Parse(time.RFC822, "26 Mar 22 00:00 UTC")
|
||||
|
||||
summaries := []*models.Summary{
|
||||
{FromTime: models.CustomTime(from1), ToTime: models.CustomTime(to1)},
|
||||
{FromTime: models.CustomTime(from2), ToTime: models.CustomTime(to2)},
|
||||
}
|
||||
|
||||
r1 := sut.getMissingIntervals(from1, to1, summaries, true)
|
||||
assert.Empty(suite.T(), r1)
|
||||
|
||||
r2 := sut.getMissingIntervals(from1, from1, summaries, true)
|
||||
assert.Empty(suite.T(), r2)
|
||||
|
||||
// non-precise mode will not return intra-day intervals
|
||||
// we might want to change this ...
|
||||
r3 := sut.getMissingIntervals(from1, to2, summaries, false)
|
||||
assert.Len(suite.T(), r3, 0)
|
||||
|
||||
r4 := sut.getMissingIntervals(from1, to2, summaries, true)
|
||||
assert.Len(suite.T(), r4, 1)
|
||||
assert.Equal(suite.T(), to1, r4[0].Start)
|
||||
assert.Equal(suite.T(), from2, r4[0].End)
|
||||
|
||||
r5 := sut.getMissingIntervals(from1.Add(-time.Hour), to2.Add(time.Hour), summaries, true)
|
||||
assert.Len(suite.T(), r5, 3)
|
||||
assert.Equal(suite.T(), from1.Add(-time.Hour), r5[0].Start)
|
||||
assert.Equal(suite.T(), from1, r5[0].End)
|
||||
assert.Equal(suite.T(), to1, r5[1].Start)
|
||||
assert.Equal(suite.T(), from2, r5[1].End)
|
||||
assert.Equal(suite.T(), to2, r5[2].Start)
|
||||
assert.Equal(suite.T(), to2.Add(time.Hour), r5[2].End)
|
||||
}
|
||||
|
||||
func filterDurations(from, to time.Time, durations models.Durations) models.Durations {
|
||||
filtered := make([]*models.Duration, 0, len(durations))
|
||||
for _, d := range durations {
|
||||
|
@@ -2,6 +2,7 @@ package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/datetime"
|
||||
"github.com/emvi/logbuch"
|
||||
"github.com/leandro-lugaresi/hub"
|
||||
"github.com/muety/wakapi/config"
|
||||
@@ -100,9 +101,9 @@ func (srv *UserService) GetAllByReports(reportsEnabled bool) ([]*models.User, er
|
||||
}
|
||||
|
||||
func (srv *UserService) GetActive(exact bool) ([]*models.User, error) {
|
||||
minDate := time.Now().Add(-24 * time.Hour * time.Duration(srv.config.App.InactiveDays))
|
||||
minDate := time.Now().AddDate(0, 0, -1*srv.config.App.InactiveDays)
|
||||
if !exact {
|
||||
minDate = utils.FloorDateHour(minDate)
|
||||
minDate = datetime.BeginOfHour(minDate)
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("%s--active", minDate.String())
|
||||
|
Reference in New Issue
Block a user