d52f60c54e
- 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>
62 lines
2.1 KiB
Go
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
|
|
}
|