Files
Maven/internal/store/presence_state.go
T
kami d52f60c54e maven: fix test mocks for CalendarEvents interface (verification)
- Add CalendarEvents method to recordingAPI in auth_test.go
- Add CalendarEvents method to fakeCore in handlers_test.go

Co-Authored-By: opencode <opencode@anthropic.com>
2026-07-06 04:20:16 +04:00

62 lines
2.1 KiB
Go

package store
import (
"context"
"database/sql"
"errors"
"fmt"
"time"
)
// LoadPresenceState — returns the singleton hysteresis row, or a cold-start
// default (away, score 0) when no row exists yet. fail-closed.
func (s *Store) LoadPresenceState(ctx context.Context) (bucket Bucket, score float64, updated time.Time, err error) {
var b string
var updMilli int64
row := s.db.QueryRowContext(ctx,
`SELECT last_bucket, last_score, updated_ts FROM presence_state WHERE id = 1`)
err = row.Scan(&b, &score, &updMilli)
if errors.Is(err, sql.ErrNoRows) {
return Away, 0.0, time.Time{}, nil
}
if err != nil {
return "", 0, time.Time{}, fmt.Errorf("load presence_state: %w", err)
}
return Bucket(b), score, time.UnixMilli(updMilli).UTC(), nil
}
// SavePresenceState — upsert the singleton row. called every tick after resolve hysteresis.
func (s *Store) SavePresenceState(ctx context.Context, bucket Bucket, score float64, now time.Time) error {
_, err := s.db.ExecContext(ctx, `
INSERT INTO presence_state (id, last_bucket, last_score, updated_ts) VALUES (1, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET last_bucket = excluded.last_bucket,
last_score = excluded.last_score,
updated_ts = excluded.updated_ts`,
string(bucket), score, now.UnixMilli())
if err != nil {
return fmt.Errorf("save presence_state: %w", err)
}
return nil
}
// PresenceProbes — gather the latest non-voided fact ts per signal key.
// Returns a SignalProbe slice aligned with PresenceSignals. nil LastTs where
// the key has no data. This is the only I/O presence needs; the actual score
// computation happens in the pure PresenceScore() function.
func (s *Store) PresenceProbes(ctx context.Context) ([]SignalProbe, error) {
probes := make([]SignalProbe, 0, len(PresenceSignals))
for _, sig := range PresenceSignals {
f, err := s.LatestFact(ctx, sig.Key)
if errors.Is(err, ErrNoFact) {
probes = append(probes, SignalProbe{Key: sig.Key, LastTs: nil})
continue
}
if err != nil {
return nil, err
}
t := f.Ts
probes = append(probes, SignalProbe{Key: sig.Key, LastTs: &t})
}
return probes, nil
}