From f885a8ff9168f5e83a85ec9b1b02764e02c0f4e8 Mon Sep 17 00:00:00 2001 From: Taras Madan Date: Fri, 30 Aug 2024 10:28:50 +0200 Subject: dashboard/app: total coverage is about months or quarters Total coverage page defaults to quarters (?period=quarter). Monthly data is available with ?period=month. --- pkg/coveragedb/time_period.go | 52 ++++++++++++++++++++++++++++++++++---- pkg/coveragedb/time_period_test.go | 28 ++++++++++++++------ 2 files changed, 67 insertions(+), 13 deletions(-) (limited to 'pkg/coveragedb') diff --git a/pkg/coveragedb/time_period.go b/pkg/coveragedb/time_period.go index 463ee6c41..b3a2aad73 100644 --- a/pkg/coveragedb/time_period.go +++ b/pkg/coveragedb/time_period.go @@ -4,6 +4,8 @@ package coveragedb import ( + "errors" + "slices" "sort" "cloud.google.com/go/civil" @@ -14,19 +16,59 @@ type TimePeriod struct { Days int } +const ( + MonthPeriod = "month" + QuarterPeriod = "quarter" +) + +var errUnknownTimePeriodType = errors.New("unknown time period type") + +func MinMaxDays(periodType string) (int, int, error) { + switch periodType { + case MonthPeriod: + return 28, 31, nil + case QuarterPeriod: + return 31 + 28 + 31, 31 + 30 + 31, nil + default: + return 0, 0, errUnknownTimePeriodType + } +} + +func PeriodOps(periodType string) (periodOps, error) { + switch periodType { + case MonthPeriod: + return &MonthPeriodOps{}, nil + case QuarterPeriod: + return &QuarterPeriodOps{}, nil + default: + return nil, errUnknownTimePeriodType + } +} + type periodOps interface { - isValidPeriod(p TimePeriod) bool + IsValidPeriod(p TimePeriod) bool lastPeriodDate(d civil.Date) civil.Date pointedPeriodDays(d civil.Date) int } +func GenNPeriodEndDatesTill(n int, d civil.Date, po periodOps) []civil.Date { + var res []civil.Date + for i := 0; i < n; i++ { + d = po.lastPeriodDate(d) + res = append(res, d) + d = d.AddDays(-po.pointedPeriodDays(d)) + } + slices.Reverse(res) + return res +} + type DayPeriodOps struct{} func (dpo *DayPeriodOps) lastPeriodDate(d civil.Date) civil.Date { return d } -func (dpo *DayPeriodOps) isValidPeriod(p TimePeriod) bool { +func (dpo *DayPeriodOps) IsValidPeriod(p TimePeriod) bool { return p.Days == 1 } @@ -43,7 +85,7 @@ func (m *MonthPeriodOps) lastPeriodDate(d civil.Date) civil.Date { return d.AddDays(-1) } -func (m *MonthPeriodOps) isValidPeriod(p TimePeriod) bool { +func (m *MonthPeriodOps) IsValidPeriod(p TimePeriod) bool { lmd := m.lastPeriodDate(p.DateTo) return lmd == p.DateTo && p.Days == lmd.Day } @@ -54,7 +96,7 @@ func (m *MonthPeriodOps) pointedPeriodDays(d civil.Date) int { type QuarterPeriodOps struct{} -func (q *QuarterPeriodOps) isValidPeriod(p TimePeriod) bool { +func (q *QuarterPeriodOps) IsValidPeriod(p TimePeriod) bool { lmd := q.lastPeriodDate(p.DateTo) return lmd == p.DateTo && p.Days == q.pointedPeriodDays(lmd) } @@ -83,7 +125,7 @@ func PeriodsToMerge(srcDates, mergedPeriods []TimePeriod, srcRows, mergedRows [] periodRows[periodID] += srcRows[i] } for i, period := range mergedPeriods { - if !ops.isValidPeriod(period) { + if !ops.IsValidPeriod(period) { continue } mergerPeriodID := period.DateTo diff --git a/pkg/coveragedb/time_period_test.go b/pkg/coveragedb/time_period_test.go index 85fb4b407..86201e4db 100644 --- a/pkg/coveragedb/time_period_test.go +++ b/pkg/coveragedb/time_period_test.go @@ -19,10 +19,14 @@ func TestDayPeriodOps(t *testing.T) { assert.Equal(t, "2024-02-20", ops.lastPeriodDate(d).String()) - assert.True(t, ops.isValidPeriod(goodPeriod)) - assert.False(t, ops.isValidPeriod(badPeriod)) + assert.True(t, ops.IsValidPeriod(goodPeriod)) + assert.False(t, ops.IsValidPeriod(badPeriod)) assert.Equal(t, 1, ops.pointedPeriodDays(d)) + + assert.Equal(t, + []civil.Date{{Year: 2024, Month: time.February, Day: 19}, {Year: 2024, Month: time.February, Day: 20}}, + GenNPeriodEndDatesTill(2, d, ops)) } func TestMonthPeriodOps(t *testing.T) { @@ -37,11 +41,15 @@ func TestMonthPeriodOps(t *testing.T) { assert.Equal(t, "2024-02-29", ops.lastPeriodDate(midMonthDate).String()) - assert.True(t, ops.isValidPeriod(goodPeriod)) - assert.False(t, ops.isValidPeriod(badPeriod1)) - assert.False(t, ops.isValidPeriod(badPeriod2)) + assert.True(t, ops.IsValidPeriod(goodPeriod)) + assert.False(t, ops.IsValidPeriod(badPeriod1)) + assert.False(t, ops.IsValidPeriod(badPeriod2)) assert.Equal(t, 29, ops.pointedPeriodDays(midMonthDate)) + + assert.Equal(t, + []civil.Date{{Year: 2024, Month: time.January, Day: 31}, {Year: 2024, Month: time.February, Day: 29}}, + GenNPeriodEndDatesTill(2, goodPeriod.DateTo, ops)) } func TestQuarterPeriodOps(t *testing.T) { @@ -57,11 +65,15 @@ func TestQuarterPeriodOps(t *testing.T) { assert.Equal(t, "2024-03-31", ops.lastPeriodDate(midQuarterDate).String()) - assert.True(t, ops.isValidPeriod(goodPeriod)) - assert.False(t, ops.isValidPeriod(badPeriod1)) - assert.False(t, ops.isValidPeriod(badPeriod2)) + assert.True(t, ops.IsValidPeriod(goodPeriod)) + assert.False(t, ops.IsValidPeriod(badPeriod1)) + assert.False(t, ops.IsValidPeriod(badPeriod2)) assert.Equal(t, 31+29+31, ops.pointedPeriodDays(midQuarterDate)) + + assert.Equal(t, + []civil.Date{{Year: 2023, Month: time.December, Day: 31}, {Year: 2024, Month: time.March, Day: 31}}, + GenNPeriodEndDatesTill(2, goodPeriod.DateTo, ops)) } func TestPeriodsToMerge(t *testing.T) { -- cgit mrf-deployment