From 09ad1901e93158ccbbfd64033849e1835fb3ecb8 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 12 Jun 2025 20:54:13 +0300 Subject: improve stats calculation to exclude pause periods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Modify newStats() and gatherPostedStats() to accept config parameter - Add calculatePausedDays() helper function for pause period overlap calculation - Update postsPerDay and totalPostsPerDay calculations to exclude paused days - Prevent artificially low throughput during pause periods - Add comprehensive unit tests for pause-aware stats functionality - Maintain backward compatibility when no pause is configured Fixes issue where Gos would post aggressively after vacation periods due to including pause days in throughput calculations. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- internal/schedule/stats.go | 71 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 5 deletions(-) (limited to 'internal/schedule/stats.go') diff --git a/internal/schedule/stats.go b/internal/schedule/stats.go index eba2eee..b0f35d1 100644 --- a/internal/schedule/stats.go +++ b/internal/schedule/stats.go @@ -7,6 +7,7 @@ import ( "time" "codeberg.org/snonux/gos/internal/colour" + "codeberg.org/snonux/gos/internal/config" "codeberg.org/snonux/gos/internal/entry" "codeberg.org/snonux/gos/internal/oi" "codeberg.org/snonux/gos/internal/platforms" @@ -31,10 +32,10 @@ type stats struct { pauseDays int } -func newStats(dir string, lookback time.Duration, target, pauseDays, maxQueuedDays int) (stats, error) { +func newStats(dir string, lookback time.Duration, target, pauseDays, maxQueuedDays int, cfg config.Config) (stats, error) { s := stats{postsPerDayTarget: float64(target) / 7, pauseDays: pauseDays} - if err := s.gatherPostedStats(dir, pastTime(lookback)); err != nil { + if err := s.gatherPostedStats(dir, pastTime(lookback), cfg); err != nil { return s, err } if err := s.gatherQueuedStats(dir); err != nil { @@ -71,7 +72,7 @@ func (s stats) targetHit() bool { return false } -func (s *stats) gatherPostedStats(dir string, lookbackTime time.Time) error { +func (s *stats) gatherPostedStats(dir string, lookbackTime time.Time, cfg config.Config) error { var ( now time.Time = timestamp.NowTime() newest time.Time = timestamp.OldestValidTime() @@ -114,12 +115,26 @@ func (s *stats) gatherPostedStats(dir string, lookbackTime time.Time) error { since := now.Sub(oldest) s.sinceDays = since.Abs().Hours() / 24.0 - s.postsPerDay = float64(s.posted) / float64(s.sinceDays) + + // Subtract paused days from the calculation period + pausedDays := calculatePausedDays(oldest, now, cfg) + activeDays := s.sinceDays - pausedDays + if activeDays > 0 { + s.postsPerDay = float64(s.posted) / activeDays + } else { + s.postsPerDay = 0 + } s.lastPostDaysAgo = now.Sub(newest).Hours() / 24.0 since = now.Sub(totalOldest) s.totalSinceDays = since.Abs().Hours() / 24.0 - s.totalPostsPerDay = float64(s.totalPosted) / float64(s.totalSinceDays) + totalPausedDays := calculatePausedDays(totalOldest, now, cfg) + totalActiveDays := s.totalSinceDays - totalPausedDays + if totalActiveDays > 0 { + s.totalPostsPerDay = float64(s.totalPosted) / totalActiveDays + } else { + s.totalPostsPerDay = 0 + } return nil } @@ -164,3 +179,49 @@ func (s stats) RenderTable(platform platforms.Platform) { func pastTime(duration time.Duration) time.Time { return timestamp.NowTime().Add(-duration) } + +// calculatePausedDays calculates the number of days that fall within pause periods +// between startTime and endTime +func calculatePausedDays(startTime, endTime time.Time, cfg config.Config) float64 { + if cfg.PauseStart == "" || cfg.PauseEnd == "" { + return 0 + } + + pauseStart, err := time.Parse("2006-01-02", cfg.PauseStart) + if err != nil { + return 0 // If parse fails, assume no pause + } + + pauseEnd, err := time.Parse("2006-01-02", cfg.PauseEnd) + if err != nil { + return 0 // If parse fails, assume no pause + } + + // Set to start and end of day + pauseStart = time.Date(pauseStart.Year(), pauseStart.Month(), pauseStart.Day(), 0, 0, 0, 0, startTime.Location()) + pauseEnd = time.Date(pauseEnd.Year(), pauseEnd.Month(), pauseEnd.Day(), 23, 59, 59, 999999999, endTime.Location()) + + // Find intersection of [startTime, endTime] with [pauseStart, pauseEnd] + intersectionStart := maxTime(startTime, pauseStart) + intersectionEnd := minTime(endTime, pauseEnd) + + if intersectionStart.Before(intersectionEnd) || intersectionStart.Equal(intersectionEnd) { + return intersectionEnd.Sub(intersectionStart).Hours() / 24.0 + } + + return 0 +} + +func maxTime(a, b time.Time) time.Time { + if a.After(b) { + return a + } + return b +} + +func minTime(a, b time.Time) time.Time { + if a.Before(b) { + return a + } + return b +} -- cgit v1.2.3