refactor: restore stored aggregates with incremental update on edit
- Restore TotalWorkSeconds, CurrentStreak, LongestStreak writes in computeAndAwardXP and updateStreak (incremental on clock out) - Add recalcDayWorkSeconds and recalcAggregates for event edit path (recompute day + total + streak from scratch when events change) - Call recalcAggregates after editEventTimeSet, deleteEventConfirm, addEventDo in calendar.go - Remove runtime computeStreaks and totalWorkSeconds (N+1 scan) from RPG display, burnout, and clockOut - Add SetDailyWorkSeconds, SumDailyWorkSeconds store methods - Clean up unused dead functions and dead code in rpg.go
This commit is contained in:
@@ -593,7 +593,8 @@ func (s *Store) UpdateRPGStats(st *RPGStats) error {
|
||||
|
||||
// -- Daily XP --
|
||||
|
||||
// UpsertDailyXP inserts or updates the daily XP record for a user/date.
|
||||
// UpsertDailyXP inserts or updates the daily XP and work seconds for a user/date.
|
||||
// xpEarned and workSeconds are accumulated on conflict.
|
||||
func (s *Store) UpsertDailyXP(userID int64, date string, xpEarned, workSeconds int64) error {
|
||||
_, err := s.db.Exec(`
|
||||
INSERT INTO daily_xp (user_id, date, xp_earned, work_seconds)
|
||||
@@ -605,6 +606,26 @@ func (s *Store) UpsertDailyXP(userID int64, date string, xpEarned, workSeconds i
|
||||
return err
|
||||
}
|
||||
|
||||
// SetDailyWorkSeconds overwrites the work_seconds for a specific day (used during recalc on edit).
|
||||
func (s *Store) SetDailyWorkSeconds(userID int64, date string, workSeconds int64) error {
|
||||
_, err := s.db.Exec(`
|
||||
INSERT INTO daily_xp (user_id, date, xp_earned, work_seconds)
|
||||
VALUES (?, ?, 0, ?)
|
||||
ON CONFLICT(user_id, date) DO UPDATE SET
|
||||
work_seconds = ?
|
||||
`, userID, date, workSeconds, workSeconds)
|
||||
return err
|
||||
}
|
||||
|
||||
// SumDailyWorkSeconds returns the sum of work_seconds across all days for the user.
|
||||
func (s *Store) SumDailyWorkSeconds(userID int64) (int64, error) {
|
||||
var total int64
|
||||
err := s.db.QueryRow(
|
||||
"SELECT COALESCE(SUM(work_seconds), 0) FROM daily_xp WHERE user_id=?", userID,
|
||||
).Scan(&total)
|
||||
return total, err
|
||||
}
|
||||
|
||||
// GetDailyXP returns the daily XP record for a user/date.
|
||||
func (s *Store) GetDailyXP(userID int64, date string) (*DailyXP, error) {
|
||||
row := s.db.QueryRow(`
|
||||
@@ -890,6 +911,31 @@ func (s *Store) GetUserDaysInRange(userID int64, startDate, endDate string) ([]D
|
||||
return days, rows.Err()
|
||||
}
|
||||
|
||||
// GetDatesWithEvents returns all distinct dates (from the days table) on which
|
||||
// the user has at least one event, ordered ascending.
|
||||
func (s *Store) GetDatesWithEvents(userID int64) ([]string, error) {
|
||||
rows, err := s.db.Query(`
|
||||
SELECT DISTINCT d.date
|
||||
FROM days d
|
||||
JOIN events e ON e.day_id = d.id
|
||||
WHERE d.user_id = ?
|
||||
ORDER BY d.date ASC
|
||||
`, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var dates []string
|
||||
for rows.Next() {
|
||||
var date string
|
||||
if err := rows.Scan(&date); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dates = append(dates, date)
|
||||
}
|
||||
return dates, rows.Err()
|
||||
}
|
||||
|
||||
// CreateEvent inserts a new event record (typically "in" or "out").
|
||||
func (s *Store) CreateEvent(userID int64, dayID int64, eventType string, workTypeID *int64, occurredAt int64, note string) error {
|
||||
_, err := s.db.Exec(
|
||||
|
||||
Reference in New Issue
Block a user