Move tgstore to its own package
This commit is contained in:
parent
5d9a08780f
commit
8207c59815
13 changed files with 113 additions and 81 deletions
|
@ -8,6 +8,7 @@ import (
|
||||||
"dynatron.me/x/stillbox/internal/trending"
|
"dynatron.me/x/stillbox/internal/trending"
|
||||||
"dynatron.me/x/stillbox/pkg/database"
|
"dynatron.me/x/stillbox/pkg/database"
|
||||||
"dynatron.me/x/stillbox/pkg/talkgroups"
|
"dynatron.me/x/stillbox/pkg/talkgroups"
|
||||||
|
"dynatron.me/x/stillbox/pkg/talkgroups/tgstore"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
@ -44,7 +45,8 @@ func (a *Alert) ToAddAlertParams() database.AddAlertParams {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make creates an alert for later rendering or storage.
|
// Make creates an alert for later rendering or storage.
|
||||||
func Make(ctx context.Context, store talkgroups.Store, score trending.Score[talkgroups.ID], origScore float64) (Alert, error) {
|
func Make(ctx context.Context, score trending.Score[talkgroups.ID], origScore float64) (Alert, error) {
|
||||||
|
store := tgstore.From(ctx)
|
||||||
d := Alert{
|
d := Alert{
|
||||||
Score: score,
|
Score: score,
|
||||||
Timestamp: time.Now(),
|
Timestamp: time.Now(),
|
||||||
|
|
|
@ -15,7 +15,8 @@ import (
|
||||||
"dynatron.me/x/stillbox/pkg/database"
|
"dynatron.me/x/stillbox/pkg/database"
|
||||||
"dynatron.me/x/stillbox/pkg/notify"
|
"dynatron.me/x/stillbox/pkg/notify"
|
||||||
"dynatron.me/x/stillbox/pkg/sinks"
|
"dynatron.me/x/stillbox/pkg/sinks"
|
||||||
talkgroups "dynatron.me/x/stillbox/pkg/talkgroups"
|
"dynatron.me/x/stillbox/pkg/talkgroups"
|
||||||
|
"dynatron.me/x/stillbox/pkg/talkgroups/tgstore"
|
||||||
|
|
||||||
"dynatron.me/x/stillbox/internal/timeseries"
|
"dynatron.me/x/stillbox/internal/timeseries"
|
||||||
"dynatron.me/x/stillbox/internal/trending"
|
"dynatron.me/x/stillbox/internal/trending"
|
||||||
|
@ -51,7 +52,7 @@ type alerter struct {
|
||||||
alertCache map[talkgroups.ID]alert.Alert
|
alertCache map[talkgroups.ID]alert.Alert
|
||||||
renotify time.Duration
|
renotify time.Duration
|
||||||
notifier notify.Notifier
|
notifier notify.Notifier
|
||||||
tgCache talkgroups.Store
|
tgCache tgstore.Store
|
||||||
}
|
}
|
||||||
|
|
||||||
type offsetClock time.Duration
|
type offsetClock time.Duration
|
||||||
|
@ -86,7 +87,7 @@ func WithNotifier(n notify.Notifier) AlertOption {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Alerter using the provided configuration.
|
// New creates a new Alerter using the provided configuration.
|
||||||
func New(cfg config.Alerting, tgCache talkgroups.Store, opts ...AlertOption) Alerter {
|
func New(cfg config.Alerting, tgCache tgstore.Store, opts ...AlertOption) Alerter {
|
||||||
if !cfg.Enable {
|
if !cfg.Enable {
|
||||||
return &noopAlerter{}
|
return &noopAlerter{}
|
||||||
}
|
}
|
||||||
|
@ -169,7 +170,7 @@ func (as *alerter) eval(ctx context.Context, now time.Time, testMode bool) ([]al
|
||||||
if s.Score > as.cfg.AlertThreshold || testMode {
|
if s.Score > as.cfg.AlertThreshold || testMode {
|
||||||
if old, inCache := as.alertCache[s.ID]; !inCache || now.Sub(old.Timestamp) > as.renotify {
|
if old, inCache := as.alertCache[s.ID]; !inCache || now.Sub(old.Timestamp) > as.renotify {
|
||||||
s.Score *= as.tgCache.Weight(ctx, s.ID, now)
|
s.Score *= as.tgCache.Weight(ctx, s.ID, now)
|
||||||
a, err := alert.Make(ctx, as.tgCache, s, origScore)
|
a, err := alert.Make(ctx, s, origScore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("makeAlert: %w", err)
|
return nil, fmt.Errorf("makeAlert: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -202,7 +203,7 @@ func (as *alerter) testNotifyHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
ridx := rand.Intn(len(as.scores))
|
ridx := rand.Intn(len(as.scores))
|
||||||
a, err := alert.Make(ctx, talkgroups.StoreFrom(ctx), as.scores[ridx], 1.0)
|
a, err := alert.Make(ctx, as.scores[ridx], 1.0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("test notify make alert fail")
|
log.Error().Err(err).Msg("test notify make alert fail")
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"dynatron.me/x/stillbox/internal/trending"
|
"dynatron.me/x/stillbox/internal/trending"
|
||||||
"dynatron.me/x/stillbox/pkg/config"
|
"dynatron.me/x/stillbox/pkg/config"
|
||||||
"dynatron.me/x/stillbox/pkg/talkgroups"
|
"dynatron.me/x/stillbox/pkg/talkgroups"
|
||||||
|
"dynatron.me/x/stillbox/pkg/talkgroups/tgstore"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
@ -59,7 +60,7 @@ func (s *Simulation) stepClock(t time.Time) {
|
||||||
// Simulate begins the simulation using the DB handle from ctx. It returns final scores.
|
// Simulate begins the simulation using the DB handle from ctx. It returns final scores.
|
||||||
func (s *Simulation) Simulate(ctx context.Context) (trending.Scores[talkgroups.ID], error) {
|
func (s *Simulation) Simulate(ctx context.Context) (trending.Scores[talkgroups.ID], error) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
tgc := talkgroups.NewCache()
|
tgc := tgstore.NewCache()
|
||||||
|
|
||||||
s.Enable = true
|
s.Enable = true
|
||||||
s.alerter = New(s.Alerting, tgc, WithClock(&s.clock)).(*alerter)
|
s.alerter = New(s.Alerting, tgc, WithClock(&s.clock)).(*alerter)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"dynatron.me/x/stillbox/pkg/calls"
|
"dynatron.me/x/stillbox/pkg/calls"
|
||||||
"dynatron.me/x/stillbox/pkg/pb"
|
"dynatron.me/x/stillbox/pkg/pb"
|
||||||
"dynatron.me/x/stillbox/pkg/talkgroups"
|
"dynatron.me/x/stillbox/pkg/talkgroups"
|
||||||
|
"dynatron.me/x/stillbox/pkg/talkgroups/tgstore"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
|
@ -59,9 +60,9 @@ func (c *client) SendError(cmd *pb.Command, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Talkgroup(ctx context.Context, tg *pb.Talkgroup) error {
|
func (c *client) Talkgroup(ctx context.Context, tg *pb.Talkgroup) error {
|
||||||
tgi, err := talkgroups.StoreFrom(ctx).TG(ctx, talkgroups.TG(tg.System, tg.Talkgroup))
|
tgi, err := tgstore.From(ctx).TG(ctx, talkgroups.TG(tg.System, tg.Talkgroup))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != talkgroups.ErrNotFound {
|
if err != tgstore.ErrNotFound {
|
||||||
log.Error().Err(err).Int32("sys", tg.System).Int32("tg", tg.Talkgroup).Msg("get talkgroup fail")
|
log.Error().Err(err).Int32("sys", tg.System).Int32("tg", tg.Talkgroup).Msg("get talkgroup fail")
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"dynatron.me/x/stillbox/pkg/talkgroups"
|
"dynatron.me/x/stillbox/pkg/talkgroups/tgstore"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
|
@ -87,8 +87,8 @@ func internalError(err error) render.Renderer {
|
||||||
type errResponder func(error) render.Renderer
|
type errResponder func(error) render.Renderer
|
||||||
|
|
||||||
var statusMapping = map[error]errResponder{
|
var statusMapping = map[error]errResponder{
|
||||||
talkgroups.ErrNoSuchSystem: errTextNotFound,
|
tgstore.ErrNoSuchSystem: errTextNotFound,
|
||||||
talkgroups.ErrNotFound: errTextNotFound,
|
tgstore.ErrNotFound: errTextNotFound,
|
||||||
pgx.ErrNoRows: recordNotFound,
|
pgx.ErrNoRows: recordNotFound,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"dynatron.me/x/stillbox/internal/forms"
|
"dynatron.me/x/stillbox/internal/forms"
|
||||||
"dynatron.me/x/stillbox/pkg/database"
|
"dynatron.me/x/stillbox/pkg/database"
|
||||||
"dynatron.me/x/stillbox/pkg/talkgroups"
|
"dynatron.me/x/stillbox/pkg/talkgroups"
|
||||||
|
"dynatron.me/x/stillbox/pkg/talkgroups/tgstore"
|
||||||
"dynatron.me/x/stillbox/pkg/talkgroups/importer"
|
"dynatron.me/x/stillbox/pkg/talkgroups/importer"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
@ -53,7 +54,7 @@ func (t tgParams) ToID() talkgroups.ID {
|
||||||
|
|
||||||
func (tga *talkgroupAPI) get(w http.ResponseWriter, r *http.Request) {
|
func (tga *talkgroupAPI) get(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
tgs := talkgroups.StoreFrom(ctx)
|
tgs := tgstore.From(ctx)
|
||||||
|
|
||||||
var p tgParams
|
var p tgParams
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ func (tga *talkgroupAPI) put(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
tgs := talkgroups.StoreFrom(ctx)
|
tgs := tgstore.From(ctx)
|
||||||
|
|
||||||
input := database.UpdateTalkgroupParams{}
|
input := database.UpdateTalkgroupParams{}
|
||||||
|
|
||||||
|
@ -137,12 +138,12 @@ func (tga *talkgroupAPI) putTalkgroups(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if id.System == nil { // don't think this would ever happen
|
if id.System == nil { // don't think this would ever happen
|
||||||
wErr(w, r, badRequest(talkgroups.ErrNoSuchSystem))
|
wErr(w, r, badRequest(tgstore.ErrNoSuchSystem))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
tgs := talkgroups.StoreFrom(ctx)
|
tgs := tgstore.From(ctx)
|
||||||
|
|
||||||
var input []database.UpsertTalkgroupParams
|
var input []database.UpsertTalkgroupParams
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"dynatron.me/x/stillbox/internal/version"
|
"dynatron.me/x/stillbox/internal/version"
|
||||||
"dynatron.me/x/stillbox/pkg/config"
|
"dynatron.me/x/stillbox/pkg/config"
|
||||||
"dynatron.me/x/stillbox/pkg/database"
|
"dynatron.me/x/stillbox/pkg/database"
|
||||||
"dynatron.me/x/stillbox/pkg/talkgroups"
|
"dynatron.me/x/stillbox/pkg/talkgroups/tgstore"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/v5/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
"github.com/go-chi/httprate"
|
"github.com/go-chi/httprate"
|
||||||
|
@ -28,7 +28,7 @@ func (s *Server) setupRoutes() {
|
||||||
|
|
||||||
r := s.r
|
r := s.r
|
||||||
r.Use(middleware.WithValue(database.DBCtxKey, s.db))
|
r.Use(middleware.WithValue(database.DBCtxKey, s.db))
|
||||||
r.Use(middleware.WithValue(talkgroups.StoreCtxKey, s.tgs))
|
r.Use(middleware.WithValue(tgstore.StoreCtxKey, s.tgs))
|
||||||
|
|
||||||
s.installPprof()
|
s.installPprof()
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,8 @@ import (
|
||||||
"dynatron.me/x/stillbox/pkg/rest"
|
"dynatron.me/x/stillbox/pkg/rest"
|
||||||
"dynatron.me/x/stillbox/pkg/sinks"
|
"dynatron.me/x/stillbox/pkg/sinks"
|
||||||
"dynatron.me/x/stillbox/pkg/sources"
|
"dynatron.me/x/stillbox/pkg/sources"
|
||||||
"dynatron.me/x/stillbox/pkg/talkgroups"
|
"dynatron.me/x/stillbox/pkg/talkgroups/tgstore"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/v5/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
"github.com/go-chi/cors"
|
"github.com/go-chi/cors"
|
||||||
|
@ -37,7 +38,7 @@ type Server struct {
|
||||||
alerter alerting.Alerter
|
alerter alerting.Alerter
|
||||||
notifier notify.Notifier
|
notifier notify.Notifier
|
||||||
hup chan os.Signal
|
hup chan os.Signal
|
||||||
tgs talkgroups.Store
|
tgs tgstore.Store
|
||||||
rest rest.API
|
rest rest.API
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ func New(ctx context.Context, cfg *config.Config) (*Server, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tgCache := talkgroups.NewCache()
|
tgCache := tgstore.NewCache()
|
||||||
api := rest.New()
|
api := rest.New()
|
||||||
|
|
||||||
srv := &Server{
|
srv := &Server{
|
||||||
|
@ -117,7 +118,7 @@ func (s *Server) Go(ctx context.Context) error {
|
||||||
s.installHupHandler()
|
s.installHupHandler()
|
||||||
|
|
||||||
ctx = database.CtxWithDB(ctx, s.db)
|
ctx = database.CtxWithDB(ctx, s.db)
|
||||||
ctx = talkgroups.CtxWithStore(ctx, s.tgs)
|
ctx = tgstore.CtxWithStore(ctx, s.tgs)
|
||||||
|
|
||||||
httpSrv := &http.Server{
|
httpSrv := &http.Server{
|
||||||
Addr: s.conf.Listen,
|
Addr: s.conf.Listen,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"dynatron.me/x/stillbox/internal/jsontypes"
|
"dynatron.me/x/stillbox/internal/jsontypes"
|
||||||
"dynatron.me/x/stillbox/pkg/database"
|
"dynatron.me/x/stillbox/pkg/database"
|
||||||
"dynatron.me/x/stillbox/pkg/talkgroups"
|
"dynatron.me/x/stillbox/pkg/talkgroups"
|
||||||
|
"dynatron.me/x/stillbox/pkg/talkgroups/tgstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ImportSource string
|
type ImportSource string
|
||||||
|
@ -66,9 +67,9 @@ var rrRE = regexp.MustCompile(`DEC\s+HEX\s+Mode\s+Alpha Tag\s+Description\s+Tag`
|
||||||
func (rr *radioReferenceImporter) importTalkgroups(ctx context.Context, sys int, r io.Reader) ([]talkgroups.Talkgroup, error) {
|
func (rr *radioReferenceImporter) importTalkgroups(ctx context.Context, sys int, r io.Reader) ([]talkgroups.Talkgroup, error) {
|
||||||
sc := bufio.NewScanner(r)
|
sc := bufio.NewScanner(r)
|
||||||
tgs := make([]talkgroups.Talkgroup, 0, 8)
|
tgs := make([]talkgroups.Talkgroup, 0, 8)
|
||||||
sysn, has := talkgroups.StoreFrom(ctx).SystemName(ctx, sys)
|
sysn, has := tgstore.From(ctx).SystemName(ctx, sys)
|
||||||
if !has {
|
if !has {
|
||||||
return nil, talkgroups.ErrNoSuchSystem
|
return nil, tgstore.ErrNoSuchSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
var groupName string
|
var groupName string
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package talkgroups
|
package tgstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -11,12 +11,13 @@ import (
|
||||||
"dynatron.me/x/stillbox/pkg/auth"
|
"dynatron.me/x/stillbox/pkg/auth"
|
||||||
"dynatron.me/x/stillbox/pkg/config"
|
"dynatron.me/x/stillbox/pkg/config"
|
||||||
"dynatron.me/x/stillbox/pkg/database"
|
"dynatron.me/x/stillbox/pkg/database"
|
||||||
|
tgsp "dynatron.me/x/stillbox/pkg/talkgroups"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tgMap map[ID]*Talkgroup
|
type tgMap map[tgsp.ID]*tgsp.Talkgroup
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrNotFound = errors.New("talkgroup not found")
|
ErrNotFound = errors.New("talkgroup not found")
|
||||||
|
@ -25,25 +26,25 @@ var (
|
||||||
|
|
||||||
type Store interface {
|
type Store interface {
|
||||||
// UpdateTG updates a talkgroup record.
|
// UpdateTG updates a talkgroup record.
|
||||||
UpdateTG(ctx context.Context, input database.UpdateTalkgroupParams) (*Talkgroup, error)
|
UpdateTG(ctx context.Context, input database.UpdateTalkgroupParams) (*tgsp.Talkgroup, error)
|
||||||
|
|
||||||
// UpsertTGs upserts a slice of talkgroups.
|
// UpsertTGs upserts a slice of talkgroups.
|
||||||
UpsertTGs(ctx context.Context, system int, input []database.UpsertTalkgroupParams) ([]*Talkgroup, error)
|
UpsertTGs(ctx context.Context, system int, input []database.UpsertTalkgroupParams) ([]*tgsp.Talkgroup, error)
|
||||||
|
|
||||||
// TG retrieves a Talkgroup from the Store.
|
// TG retrieves a Talkgroup from the Store.
|
||||||
TG(ctx context.Context, tg ID) (*Talkgroup, error)
|
TG(ctx context.Context, tg tgsp.ID) (*tgsp.Talkgroup, error)
|
||||||
|
|
||||||
// TGs retrieves many talkgroups from the Store.
|
// TGs retrieves many talkgroups from the Store.
|
||||||
TGs(ctx context.Context, tgs IDs) ([]*Talkgroup, error)
|
TGs(ctx context.Context, tgs tgsp.IDs) ([]*tgsp.Talkgroup, error)
|
||||||
|
|
||||||
// SystemTGs retrieves all Talkgroups associated with a System.
|
// SystemTGs retrieves all Talkgroups associated with a System.
|
||||||
SystemTGs(ctx context.Context, systemID int32) ([]*Talkgroup, error)
|
SystemTGs(ctx context.Context, systemID int32) ([]*tgsp.Talkgroup, error)
|
||||||
|
|
||||||
// SystemName retrieves a system name from the store. It returns the record and whether one was found.
|
// SystemName retrieves a system name from the store. It returns the record and whether one was found.
|
||||||
SystemName(ctx context.Context, id int) (string, bool)
|
SystemName(ctx context.Context, id int) (string, bool)
|
||||||
|
|
||||||
// Hint hints the Store that the provided talkgroups will be asked for.
|
// Hint hints the Store that the provided talkgroups will be asked for.
|
||||||
Hint(ctx context.Context, tgs []ID) error
|
Hint(ctx context.Context, tgs []tgsp.ID) error
|
||||||
|
|
||||||
// Load loads the provided talkgroup ID tuples into the Store.
|
// Load loads the provided talkgroup ID tuples into the Store.
|
||||||
Load(ctx context.Context, tgs database.TGTuples) error
|
Load(ctx context.Context, tgs database.TGTuples) error
|
||||||
|
@ -52,7 +53,7 @@ type Store interface {
|
||||||
Invalidate()
|
Invalidate()
|
||||||
|
|
||||||
// Weight returns the final weight of this talkgroup, including its static and rules-derived weight.
|
// Weight returns the final weight of this talkgroup, including its static and rules-derived weight.
|
||||||
Weight(ctx context.Context, id ID, t time.Time) float64
|
Weight(ctx context.Context, id tgsp.ID, t time.Time) float64
|
||||||
|
|
||||||
// Hupper
|
// Hupper
|
||||||
HUP(*config.Config)
|
HUP(*config.Config)
|
||||||
|
@ -66,7 +67,7 @@ func CtxWithStore(ctx context.Context, s Store) context.Context {
|
||||||
return context.WithValue(ctx, StoreCtxKey, s)
|
return context.WithValue(ctx, StoreCtxKey, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StoreFrom(ctx context.Context) Store {
|
func From(ctx context.Context) Store {
|
||||||
s, ok := ctx.Value(StoreCtxKey).(Store)
|
s, ok := ctx.Value(StoreCtxKey).(Store)
|
||||||
if !ok {
|
if !ok {
|
||||||
return NewCache()
|
return NewCache()
|
||||||
|
@ -102,7 +103,7 @@ func NewCache() Store {
|
||||||
return tgc
|
return tgc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *cache) Hint(ctx context.Context, tgs []ID) error {
|
func (t *cache) Hint(ctx context.Context, tgs []tgsp.ID) error {
|
||||||
t.RLock()
|
t.RLock()
|
||||||
var toLoad database.TGTuples
|
var toLoad database.TGTuples
|
||||||
if len(t.tgs) > len(tgs)/2 { // TODO: instrument this
|
if len(t.tgs) > len(tgs)/2 { // TODO: instrument this
|
||||||
|
@ -130,11 +131,11 @@ func (t *cache) Hint(ctx context.Context, tgs []ID) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *cache) add(rec *Talkgroup) {
|
func (t *cache) add(rec *tgsp.Talkgroup) {
|
||||||
t.Lock()
|
t.Lock()
|
||||||
defer t.Unlock()
|
defer t.Unlock()
|
||||||
|
|
||||||
tg := TG(rec.System.ID, rec.Talkgroup.TGID)
|
tg := tgsp.TG(rec.System.ID, rec.Talkgroup.TGID)
|
||||||
t.tgs[tg] = rec
|
t.tgs[tg] = rec
|
||||||
t.systems[int32(rec.System.ID)] = rec.System.Name
|
t.systems[int32(rec.System.ID)] = rec.System.Name
|
||||||
}
|
}
|
||||||
|
@ -147,15 +148,15 @@ type row interface {
|
||||||
GetLearned() bool
|
GetLearned() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func rowToTalkgroup[T row](r T) *Talkgroup {
|
func rowToTalkgroup[T row](r T) *tgsp.Talkgroup {
|
||||||
return &Talkgroup{
|
return &tgsp.Talkgroup{
|
||||||
Talkgroup: r.GetTalkgroup(),
|
Talkgroup: r.GetTalkgroup(),
|
||||||
System: r.GetSystem(),
|
System: r.GetSystem(),
|
||||||
Learned: r.GetLearned(),
|
Learned: r.GetLearned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addToRowList[T row](t *cache, r []*Talkgroup, tgRecords []T) []*Talkgroup {
|
func addToRowList[T row](t *cache, r []*tgsp.Talkgroup, tgRecords []T) []*tgsp.Talkgroup {
|
||||||
for _, rec := range tgRecords {
|
for _, rec := range tgRecords {
|
||||||
tg := rowToTalkgroup(rec)
|
tg := rowToTalkgroup(rec)
|
||||||
t.add(tg)
|
t.add(tg)
|
||||||
|
@ -166,11 +167,11 @@ func addToRowList[T row](t *cache, r []*Talkgroup, tgRecords []T) []*Talkgroup {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *cache) TGs(ctx context.Context, tgs IDs) ([]*Talkgroup, error) {
|
func (t *cache) TGs(ctx context.Context, tgs tgsp.IDs) ([]*tgsp.Talkgroup, error) {
|
||||||
r := make([]*Talkgroup, 0, len(tgs))
|
r := make([]*tgsp.Talkgroup, 0, len(tgs))
|
||||||
var err error
|
var err error
|
||||||
if tgs != nil {
|
if tgs != nil {
|
||||||
toGet := make(IDs, 0, len(tgs))
|
toGet := make(tgsp.IDs, 0, len(tgs))
|
||||||
t.RLock()
|
t.RLock()
|
||||||
for _, id := range tgs {
|
for _, id := range tgs {
|
||||||
rec, has := t.tgs[id]
|
rec, has := t.tgs[id]
|
||||||
|
@ -211,7 +212,7 @@ func (t *cache) Load(ctx context.Context, tgs database.TGTuples) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *cache) Weight(ctx context.Context, id ID, tm time.Time) float64 {
|
func (t *cache) Weight(ctx context.Context, id tgsp.ID, tm time.Time) float64 {
|
||||||
tg, err := t.TG(ctx, id)
|
tg, err := t.TG(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 1.0
|
return 1.0
|
||||||
|
@ -224,17 +225,17 @@ func (t *cache) Weight(ctx context.Context, id ID, tm time.Time) float64 {
|
||||||
return float64(m)
|
return float64(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *cache) SystemTGs(ctx context.Context, systemID int32) ([]*Talkgroup, error) {
|
func (t *cache) SystemTGs(ctx context.Context, systemID int32) ([]*tgsp.Talkgroup, error) {
|
||||||
recs, err := database.FromCtx(ctx).GetTalkgroupsWithLearnedBySystem(ctx, systemID)
|
recs, err := database.FromCtx(ctx).GetTalkgroupsWithLearnedBySystem(ctx, systemID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
r := make([]*Talkgroup, 0, len(recs))
|
r := make([]*tgsp.Talkgroup, 0, len(recs))
|
||||||
return addToRowList(t, r, recs), nil
|
return addToRowList(t, r, recs), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *cache) TG(ctx context.Context, tg ID) (*Talkgroup, error) {
|
func (t *cache) TG(ctx context.Context, tg tgsp.ID) (*tgsp.Talkgroup, error) {
|
||||||
t.RLock()
|
t.RLock()
|
||||||
rec, has := t.tgs[tg]
|
rec, has := t.tgs[tg]
|
||||||
t.RUnlock()
|
t.RUnlock()
|
||||||
|
@ -279,7 +280,7 @@ func (t *cache) SystemName(ctx context.Context, id int) (name string, has bool)
|
||||||
return n, has
|
return n, has
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *cache) UpdateTG(ctx context.Context, input database.UpdateTalkgroupParams) (*Talkgroup, error) {
|
func (t *cache) UpdateTG(ctx context.Context, input database.UpdateTalkgroupParams) (*tgsp.Talkgroup, error) {
|
||||||
sysName, has := t.SystemName(ctx, int(*input.SystemID))
|
sysName, has := t.SystemName(ctx, int(*input.SystemID))
|
||||||
if !has {
|
if !has {
|
||||||
return nil, ErrNoSuchSystem
|
return nil, ErrNoSuchSystem
|
||||||
|
@ -290,7 +291,7 @@ func (t *cache) UpdateTG(ctx context.Context, input database.UpdateTalkgroupPara
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
record := &Talkgroup{
|
record := &tgsp.Talkgroup{
|
||||||
Talkgroup: tg,
|
Talkgroup: tg,
|
||||||
System: database.System{ID: int(tg.SystemID), Name: sysName},
|
System: database.System{ID: int(tg.SystemID), Name: sysName},
|
||||||
}
|
}
|
||||||
|
@ -299,7 +300,7 @@ func (t *cache) UpdateTG(ctx context.Context, input database.UpdateTalkgroupPara
|
||||||
return record, nil
|
return record, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *cache) UpsertTGs(ctx context.Context, system int, input []database.UpsertTalkgroupParams) ([]*Talkgroup, error) {
|
func (t *cache) UpsertTGs(ctx context.Context, system int, input []database.UpsertTalkgroupParams) ([]*tgsp.Talkgroup, error) {
|
||||||
db := database.FromCtx(ctx)
|
db := database.FromCtx(ctx)
|
||||||
sysName, hasSys := t.SystemName(ctx, system)
|
sysName, hasSys := t.SystemName(ctx, system)
|
||||||
if !hasSys {
|
if !hasSys {
|
||||||
|
@ -310,7 +311,7 @@ func (t *cache) UpsertTGs(ctx context.Context, system int, input []database.Upse
|
||||||
Name: sysName,
|
Name: sysName,
|
||||||
}
|
}
|
||||||
|
|
||||||
tgs := make([]*Talkgroup, 0, len(input))
|
tgs := make([]*tgsp.Talkgroup, 0, len(input))
|
||||||
|
|
||||||
err := db.InTx(ctx, func(db database.Store) error {
|
err := db.InTx(ctx, func(db database.Store) error {
|
||||||
versionParams := make([]database.StoreTGVersionParams, 0, len(input))
|
versionParams := make([]database.StoreTGVersionParams, 0, len(input))
|
||||||
|
@ -341,7 +342,7 @@ func (t *cache) UpsertTGs(ctx context.Context, system int, input []database.Upse
|
||||||
TGID: r.TGID,
|
TGID: r.TGID,
|
||||||
Submitter: auth.UIDFrom(ctx),
|
Submitter: auth.UIDFrom(ctx),
|
||||||
})
|
})
|
||||||
tgs = append(tgs, &Talkgroup{
|
tgs = append(tgs, &tgsp.Talkgroup{
|
||||||
Talkgroup: r,
|
Talkgroup: r,
|
||||||
System: sys,
|
System: sys,
|
||||||
Learned: r.Learned,
|
Learned: r.Learned,
|
1
sql/postgres/migrations/002_learned.down.sql
Normal file
1
sql/postgres/migrations/002_learned.down.sql
Normal file
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE IF EXISTS talkgroup_versions;
|
49
sql/postgres/migrations/002_learned.up.sql
Normal file
49
sql/postgres/migrations/002_learned.up.sql
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS talkgroup_versions(
|
||||||
|
-- version metadata
|
||||||
|
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||||
|
time TIMESTAMPTZ NOT NULL,
|
||||||
|
created_by INTEGER REFERENCES users(id),
|
||||||
|
-- talkgroup snapshot
|
||||||
|
system_id INT4 REFERENCES systems(id),
|
||||||
|
tgid INT4,
|
||||||
|
name TEXT,
|
||||||
|
alpha_tag TEXT,
|
||||||
|
tg_group TEXT,
|
||||||
|
frequency INTEGER,
|
||||||
|
metadata JSONB,
|
||||||
|
tags TEXT[],
|
||||||
|
alert BOOLEAN,
|
||||||
|
alert_config JSONB,
|
||||||
|
weight REAL,
|
||||||
|
learned BOOLEAN
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Store current version
|
||||||
|
INSERT INTO talkgroup_versions(time, created_by,
|
||||||
|
system_id,
|
||||||
|
tgid,
|
||||||
|
name,
|
||||||
|
alpha_tag,
|
||||||
|
tg_group,
|
||||||
|
frequency,
|
||||||
|
metadata,
|
||||||
|
tags,
|
||||||
|
alert,
|
||||||
|
alert_config,
|
||||||
|
weight,
|
||||||
|
learned
|
||||||
|
) SELECT NOW(), @submitter,
|
||||||
|
tg.system_id,
|
||||||
|
tg.tgid,
|
||||||
|
tg.name,
|
||||||
|
tg.alpha_tag,
|
||||||
|
tg.tg_group,
|
||||||
|
tg.frequency,
|
||||||
|
tg.metadata,
|
||||||
|
tg.tags,
|
||||||
|
tg.alert,
|
||||||
|
tg.alert_config,
|
||||||
|
tg.weight,
|
||||||
|
tg.learned
|
||||||
|
FROM talkgroups;
|
||||||
|
|
|
@ -29,47 +29,20 @@ SELECT
|
||||||
sqlc.embed(tg), sqlc.embed(sys)
|
sqlc.embed(tg), sqlc.embed(sys)
|
||||||
FROM talkgroups tg
|
FROM talkgroups tg
|
||||||
JOIN systems sys ON tg.system_id = sys.id
|
JOIN systems sys ON tg.system_id = sys.id
|
||||||
WHERE (tg.system_id, tg.tgid) = (@system_id, @tgid) AND tg.learned IS NOT TRUE
|
WHERE (tg.system_id, tg.tgid) = (@system_id, @tgid);
|
||||||
UNION
|
|
||||||
SELECT
|
|
||||||
tgl.id, tgl.system_id::INT4, tgl.tgid::INT4, tgl.name,
|
|
||||||
tgl.alpha_tag, tgl.tg_group, NULL::INTEGER, NULL::JSONB,
|
|
||||||
CASE WHEN tgl.tg_group IS NULL THEN NULL ELSE ARRAY[tgl.tg_group] END,
|
|
||||||
NOT tgl.ignored, NULL::JSONB, 1.0, TRUE learned, sys.id, sys.name
|
|
||||||
FROM talkgroups_learned tgl
|
|
||||||
JOIN systems sys ON tgl.system_id = sys.id
|
|
||||||
WHERE tgl.system_id = @system_id AND tgl.tgid = @tgid AND ignored IS NOT TRUE;
|
|
||||||
|
|
||||||
-- name: GetTalkgroupsWithLearnedBySystem :many
|
-- name: GetTalkgroupsWithLearnedBySystem :many
|
||||||
SELECT
|
SELECT
|
||||||
sqlc.embed(tg), sqlc.embed(sys)
|
sqlc.embed(tg), sqlc.embed(sys)
|
||||||
FROM talkgroups tg
|
FROM talkgroups tg
|
||||||
JOIN systems sys ON tg.system_id = sys.id
|
JOIN systems sys ON tg.system_id = sys.id
|
||||||
WHERE tg.system_id = @system AND tg.learned IS NOT TRUE
|
WHERE tg.system_id = @system;
|
||||||
UNION
|
|
||||||
SELECT
|
|
||||||
tgl.id, tgl.system_id::INT4, tgl.tgid::INT4, tgl.name,
|
|
||||||
tgl.alpha_tag, tgl.tg_group, NULL::INTEGER, NULL::JSONB,
|
|
||||||
CASE WHEN tgl.tg_group IS NULL THEN NULL ELSE ARRAY[tgl.tg_group] END,
|
|
||||||
NOT tgl.ignored, NULL::JSONB, 1.0, TRUE learned, sys.id, sys.name
|
|
||||||
FROM talkgroups_learned tgl
|
|
||||||
JOIN systems sys ON tgl.system_id = sys.id
|
|
||||||
WHERE tgl.system_id = @system AND ignored IS NOT TRUE;
|
|
||||||
|
|
||||||
-- name: GetTalkgroupsWithLearned :many
|
-- name: GetTalkgroupsWithLearned :many
|
||||||
SELECT
|
SELECT
|
||||||
sqlc.embed(tg), sqlc.embed(sys)
|
sqlc.embed(tg), sqlc.embed(sys)
|
||||||
FROM talkgroups tg
|
FROM talkgroups tg
|
||||||
JOIN systems sys ON tg.system_id = sys.id
|
JOIN systems sys ON tg.system_id = sys.id
|
||||||
WHERE tg.learned IS NOT TRUE
|
|
||||||
UNION
|
|
||||||
SELECT
|
|
||||||
tgl.id, tgl.system_id::INT4, tgl.tgid::INT4, tgl.name,
|
|
||||||
tgl.alpha_tag, tgl.tg_group, NULL::INTEGER, NULL::JSONB,
|
|
||||||
CASE WHEN tgl.tg_group IS NULL THEN NULL ELSE ARRAY[tgl.tg_group] END,
|
|
||||||
NOT tgl.ignored, NULL::JSONB, 1.0, TRUE learned, sys.id, sys.name
|
|
||||||
FROM talkgroups_learned tgl
|
|
||||||
JOIN systems sys ON tgl.system_id = sys.id
|
|
||||||
WHERE ignored IS NOT TRUE;
|
WHERE ignored IS NOT TRUE;
|
||||||
|
|
||||||
-- name: GetSystemName :one
|
-- name: GetSystemName :one
|
||||||
|
|
Loading…
Reference in a new issue